当前位置: 首页 > Linux

《Linux网络开发必学教程》13_数据发送和接收的扩展用法(上)

时间:2023-04-06 21:56:20 Linux

write()和send()都可以发送和接收数据,有什么区别?send可以使用标志来指定可选信息,其中0表示默认发送行为。当flags为0时,会等待发送缓冲区中的数据清空后再将数据放入发送缓冲区,然后返回写入。可选信息不能指定,不会阻塞read()和recv()都可以接收数据,有什么区别?recv可以使用flags来指定optional信息,其中0表示默认的接收行为recvflags为0时,会等待接收缓冲区有数据后再发送数据从接收缓冲区中取出并返回read不能指定optional信息,并且不会阻塞数据发送和接收选项,intflags);flags-指定发送和接收数据时的可选信息,其中0为默认发送和接收行为flags选项信息(部分)optional含义sendrecvMSG_OOB用于传输带外数据(OutOfBandData),即:紧急数据(先发送)√√MSG_PEEK验证接收缓冲区中是否有数据(有什么数据)√MSG_DONTROUTE数据发送过程不经过路由表,在本地局域网√MSG_DONTWAIT非阻塞模式√√MSG_WAITALL在发送或接收数据时立即返回。在接收到所有请求的数据之前,不要提前返回√MSG_MORE有更多的数据要发送,表示内核正在等待数据√...注:不同的操作系统可能对上述选项的支持不同。在实际工程开发中,需要提前考察目标系统支持的选项。MSG_OOB(out-of-banddata,emergencydata)原生定义了通过不同于普通数据的通道独立传输的带外数据的优先级。高于普通数据(传输优先级,对端接收优先级)TCP中的带外数据由于原生设计的限制,TCP无法提供真正的带外数据。在TCP中,只能使用传输协议报文头中的标记,传输的是紧急数据,长度只有1字节TCP带外数据实现原理URG指针指向下一位紧急讯息即URG指针指向的位置的第一个字节存放的是紧急消息。接收端先接收到紧急数据并存储在专门的缓冲区中,然后再接收普通数据。紧急数据:0x03普通数据:0x01、0x02TCPout-of-band数据处理策略由于TCP设计为流式数据,不可能实现真正的带外数据。标记为紧急的数据可以提前接收并进入一个专门的缓冲区(只有一个字节)。每个TCP包最多有一个紧急数据专用缓冲区只存放最新的紧急数据(如果没有及时收到会丢失)。通过以下方式发送和接收数据时会发生什么情况?紧急数据,正常模式接收:发送紧急数据时阻塞普通数据recv,紧急模式接收:正常,接收紧急数据编程实验:发送和接收TCP紧急数据client.c#include#include#include#include#include#include#includeintmain(){int袜子={0};结构sockaddr_in地址={0};intlen=0;char*test="Delpin-Tang";sock=socket(PF_INET,SOCK_STREAM,0);if(sock==-1){printf("套接字错误\n");返回-1;}addr.sin_family=AF_INET;地址.sin_addr.s_addr=inet_addr("127.0.0.1");地址.sin_port=htons(8888);如果(connect(sock,(structsockaddr*)&addr,sizeof(addr))==-1){printf("连接错误\n");返回-1;}printf("连接成功\n");len=发送(袜子,测试,strlen(测试),MSG_OOB);获取字符();关闭(袜子);返回0;}server.c#include#include#include#include#include#include#includeintmain(){intserver=0;结构sockaddr_insaddr={0};int客户=0;结构sockaddr_incaddr={0};socklen_t大小=0;int长度=0;字符buf[32]={0};整数r=0;服务器=套接字(PF_INET,SOCK_STREAM,0);if(server==-1){printf("服务器套接字错误\n");返回-1;}saddr.sin_family=AF_INET;saddr.sin_addr.s_addr=htonl(INADDR_ANY);saddr.sin_port=htons(8888);if(bind(server,(structsockaddr*)&saddr,sizeof(saddr))==-1){printf("服务器绑定错误\n");return-1;}if(listen(server,1)==-1){printf("服务器监听错误\n");return-1;}printf("服务器启动成功\n");while(1){asize=sizeof(caddr);client=accept(server,(structsockaddr*)&caddr,&asize);if(client==-1){printf("clientaccepterror");return-1;}printf("client:%d\n",client);do{r=recv(client,buf,sizeof(buf),MSG_OOB);if(r>0){buf[r]=0;printf("OOB:%s\n",buf);}r=recv(client,buf,sizeof(buf),0);if(r>0){buf[r]=0;printf("正常:%s\n",buf);}}while(r>0);close(client);}close(server);return0;}输出:serverstartsuccessclient:4NORMAL:Delpin-Tan//注意先输出普通数据(因为flags为MSG_OOB时不会阻塞,为0时会阻塞,直到接收到数据)OOB:g//注意,只输出最后一个字符!!小问题:在实际开发中,如何高效接收TCP紧急数据?使用select接收紧急数据。当socket上收到普通数据和紧急数据时,select会立即返回普通数据:socket处于数据可读状态(普通数据可以读取)紧急数据:socket处理异常状态(紧急数据可以读取)紧急数据接收示例num=select(max+1,&temp,0,&except,&timeout);if(num>0){for(i=1;i<=max;++i){if(FD_ISSET(i,&except)){if(i!=server){charbuf[32]={0};intr=recv(i,buf,sizeof(buf),MSG_OOB);如果(r>0){buf[r]=0;printf("OOB:%s\n",buf);}}}if(FD_ISSET(I,&temp)){//...}}}编程实验:使用select接收紧急数据client.c#include#include#include#include#include#include#includeintmain(){int袜子={0};结构sockaddr_in地址={0};int长度=0;char*test="Delpin-Tang";sock=socket(PF_INET,SOCK_STREAM,0);if(sock==-1){printf("套接字错误\n");返回-1;}addr.sin_family=AF_INET;地址.sin_addr.s_addr=inet_addr("127.0.0.1");地址.sin_port=htons(8888);if(connect(sock,(structsockaddr*)&addr,sizeof(addr))==-1){printf("连接错误\n");返回-1;}printf("连接成功\n");len=发送(袜子,测试,strlen(测试),MSG_OOB);获取字符();关闭(袜子);返回0;}select-server.c#include#include#include#include#include#include#include#includeintserver_handler(intserver){structsockaddr_inaddr={0};socklen_t作为大小=大小(地址);returnaccept(server,(structsockaddr*)&addr,&asize);}intclient_handler(intclient){charbuf[32]={0};intret=recv(客户端,buf,sizeof(buf)-1,0);如果(ret>0){buf[ret]=0;printf("接收:%s\n",buf);}returnret;}intclint_except_handler(intclient){charbuf[2]={0};intr=recv(client,buf,sizeof(buf),MSG_OOB);如果(r>0){buf[r]=0;printf("OOB:%s\n",buf);}returnr;}intmain(){intserver=0;结构sockaddr_insaddr={0};整数最大值=0;整数=0;fd_set读取={0};fd_settemps={0};fd_setexcept={0};结构timeval超时={0};服务器=套接字(PF_INET,SOCK_STREAM,0);if(server==-1){printf("服务器套接字错误\n");返回-1;}saddr.sin_family=AF_INET;saddr.sin_addr.s_addr=htonl(INADDR_ANY);saddr.sin_port=htons(8888);if(bind(server,(structsockaddr*)&saddr,sizeof(saddr))==-1){printf("服务器绑定错误\n");返回-1;}if(listen(server,1)==-1){printf("serverlisteberror\n");返回-1;}printf("服务器启动成功\n");FD_ZERO(&读取);FD_SET(服务器,读取);最大值=服务器;while(1){temps=读取;除了=读;timeout.tv_sec=0;timeout.tv_usec=10000;num=select(max+1,&temps,0,&except,&timeout);如果(num>0){inti=0;for(i=0;i<=max;++i){if(FD_ISSET(i,&except)){if(i!=server){clint_except_handler(i);}}}if(FD_ISSET(i,&temps)){if(i==server){intclient=server_handler(s永远);if(client>-1){FD_SET(client,&reads);最大值=(客户端>最大值)?客户:最大;printf("接受客户端:%d\n",客户端);}}else{intr=client_handler(i);如果(r==-1){FD_CLR(i,&reads);关闭(我);}}}}intclient=server_handler(server);}}}output:serverstartsuccessacceptclient:4OOB:gRecv:Delpin-Tan总结read()/write()可以用来收发普通数据(无扩展功能)send()/recv()可以扩展更多功能通过option信息TCP紧急数据可以识别256个紧急事件(Abnormalevents)紧急数据可以通过select及时处理,普通数据可以区分