网络编程

来源:互联网 发布:淘宝女装货源 编辑:程序博客网 时间:2024/05/17 07:48
在一次服务中提供数据或服务的称为服务器,获取数据的称为客户端。

在网络基础中讲到有许多协议,那这里就面临协议的选择,TCP还是UDP?

先来看看TCP和UDP的区别:
TCP:
面向连接的(服务器和客户端之间维护一条专有线路) 
可靠的(数据无差错、不丢失、不重复、并且按序到达) 
流式服务
UDP:
无连接(每次都要探寻线路) 
不可靠的
数据报服务

线路连接上以后,未来时间段不间断发送数据,选择TCP。但是维护连接线路要付出代价,根据数据是否重要来选择可靠还是不可靠,有些数据不一定要求可靠。

两种协议自然就有两种编程方式:
TCP编程流程:
  • 服务器端sever:
  1. 创建socket
  2. bind(将IP地址和端口号命名,一般我们访问网站都不是直接输IP地址的吧)    
  3. listen(监听连接,创建监听队列)
  4. accept(从listen拿到已经完成连接的)
  5. recv/send(收发数据)
  6. close(关闭连接,所有完成后再关闭一次socket)
  • 客户端client:
  1. 创建socket ()/* bind 一般不要*/
  2. 发起连接connect
  3. recv/send(收发数据)
  4. close

具体函数如下:
1、创建socket
int socket(int domain, int type, int protocol); //domain    协议簇 IPv4(AF_INET)/ IPv6(AF_INET6); //type      选择TCP(SOCK_STREAM)还是UDP(SOCK_DGRAM); //protocol  TCP下具体的哪个协议,现在已经不用,给0; //成功返回一个socket文件描述符,失败返回-1;

2、命名socket
int bind(int sockfd,const struct sockaddr*addr,int addrlen);//sockfd    socket函数返回的文件描述符;//addr      指定IP地址和端口号//addrlen   该socket地址的长度
//成功返回0,失败返回-1并设置erron。常见erron是EACCESS(被绑定的地址是受保护的地址,即小于1024,仅超级用户能够访问)
EADDRINUSE(被绑定的地址正在使用中)
struct sockaddr_in{      sa_family_t sin_family;   /*地址族:AF_INET*/      u_int16_t sin_port;       /*端口号(网络字节序:大端模式)*/      struct in_addr sin_addr; /*IPv4地址结构体*/};struct sin_addr{       u_int32_t s_addr;        /*IPv4地址,用网络字节序表示*/};
所有专用socket地址类型的变量在实际使用时都需要强制转换成通用的sockaddr地址类型,因为所有socket编程接口使用的地址参数类型都是sockaddr。当然在这里也要弄清楚主机字节序和网络字节序。

通常都是用点分十进制字符串表示IPv4地址,但是编程中需要转换成整数(二进制)使用。系统提供了3个函数用于,点分十进制字符串表示的IPv4地址和用网络字节序整数表示的IPv4地址之间的转换:

#include<arpa/inet.h>int_addr_t  inet_addr(const char*str);
//将点分十进制字符串表示的IPv4地址转换成用网络字节序整数表示的IPv4地址,失败时返回INADDR_NONE,方便
int inet_aton(const char*cp,struct in_addr *inp);
//将点分十进制字符串表示的IPv4地址转换成用网络字节序整数表示的IPv4地址,将转化结果储存于参数inp指向的地址结构中,成功返回1,失败返回0.相比于inet_addr更安全
char *inet_ntoa(struct in_addr in);
//用网络字节序整数表示的IPv4地址转换成点分十进制字符串表示的IPv4地址,该函数不可重入
3、监听socket
int listen(int sockfd, int backlog);//创建一个监听队列,有连接放到队列中,backlog表示队列大小,但是底层有默认值,给的小了就用默认值
//成功返回0,失败返回-1

4、接收连接
int accept(int sockfd,struct sockaddr*addr,int len);//sockfd     执行过listen监听的socket//addr        记录客户端的IP地址和端口号//len         长度//成功返回获取到的和客户端连接的文件描述符,此后由该返回值和客户端通信,失败返回-1

5、发起连接
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);//addr 要连接的服务器的IP地址和端口号
//成功返回0,失败返回-1
6、关闭连接
int close(int fd);//fd    待关闭的socket

7、收发数据
#include<sys/typs.h>#include<sys/socket.h>ssize_t  recv(int sockfd, void*buf, size_t len, int flags);//接受(读取)sockfd上的数据,放到buf缓冲区,大小为len,flags暂且设为0;//成功返回实际读取到的数据长度,返回0说明对方已关闭连接,出错返回-1;ssize_t  send(int sockfd, const void*buf, size_t len, int flags);//buf缓冲区的数据发送(写入)sockfd,大小为len,flags暂且设为0;//成功返回实际读写入数据长度,出错返回-1;
既然要完成通信,那么至少有一个服务器端一个客户端,代码如下:
//服务器端ser.c void main(){      int sockfd = socket(AF_INET,SOCK_STREAM,0);      assert(sockfd != -1);        struct sockaddr_in ser,cli;      ser.sin_family = AF_INET;      ser.sin_port = htons(6000);      ser.sin_addr.s_addr = inet_addr("127.0.0.1");        int res = bind(sockfd,(struct sockaddr*)&ser,sizeof(ser));      assert(res != -1);        listen(sockfd,5);        int len = sizeof(cli);      int c = accept(sockfd,(struct sockaddr*)&cli,&len);      assert(c != -1);        char buff[128] = {0};      recv(c,buff,127,0);      printf("recv::%s\n",buff);      send(c,"I Know",strlen("I Know"),0);        close(c);        close(sockfd);   }

//客户端cli.cvoid main() {     int sockfd = socket(AF_INET,SOCK_STREAN,0);      assert(sockfd != -1);         struct sockaddr_in ser,cli;      ser.sin_family = AF_INET;     ser.sin_port = htons(6000);      ser.sin_addr.s_addr = inet_addr("127.0.0.1");            int res = connect(sockfd,(struct sockaddr*)&ser,sizeof(ser));      assert(res != -1);         send(sockfd,"Hello World",strlen("Hello World"),0);      char buff[128] = {0};      recv(sockfd,buff,127,0);        printf("recv::%s\n",buff);        close(sockfd);  }

但是在实际应用中,服务器发送一次并没有结束,客户端可以一直申请,因此服务器端要循环收发数据。
 void main() {     int sockfd = socket(AF_INET,SOCK_STREAM,0);     assert(sockfd != -1);      struct sockaddr_in ser,cli;     ser.sin_family = AF_INET;     ser.sin_port = htons(6000);     ser.sin_addr.s_addr = inet_addr("127.0.0.1");        int res = bind(sockfd,(struct sockaddr*)&ser,sizeof(ser));      assert(res != -1);        listen(sockfd,5);          while(1)      {          int len = sizeof(cli);          int c = accept(sockfd,(struct sockaddr*)&cli,&len);          assert(c != -1);            char buff[128] = {0};          recv(c,buff,127,0);          printf("recv::%s\n",buff);          send(c,"I Know",strlen("I Know"),0);            close(c);      }        close(sockfd);    }