一、TCP协议、Socket编程流程TCP/IP协议及socket封装 二、Send 和 Recv的基本介绍2.1 Send函数int send( SOCKET s, const char FAR *buf, int len, int flags );
不论是客户还是服务器应用程序都用send函数来向TCP连接的另一端发送数据。客户程序一般用send函数向服务器发送请求,而服务器则通常用send函数来向客户程序发送应答。 参数说明: 第一个参数指定发送端套接字描述符;第二个参数指明一个存放应用程序要发送数据的缓冲区;第三个参数指明实际要发送的数据的字节数;第四个参数一般置0。12341234 这里只描述同步Socket的send函数的执行流程。当调用该函数时, 要注意send函数把buf中的数据成功copy到s的发送缓冲的剩余空间里后它就返回了,但是此时这些数据并不一定马上被传到连接的另一端。如果协议在后续的传送过程中出现网络错误的话,那么下一个Socket函数就会返回SOCKET_ERROR。(每一个除send外的Socket函数在执行的最开始总要先等待套接字的发送缓冲中的数据被协议传送完毕才能继续,如果在等待时出现网络错误,那么该Socket函数就返回 SOCKET_ERROR) 注意:在Unix系统下,如果send在等待协议传送数据时网络断开的话,调用send的进程会接收到一个SIGPIPE信号,进程对该信号的默认处理是进程终止。 通过测试发现,异步socket的send函数在网络刚刚断开时还能发送返回相应的字节数,同时使用select检测也是可写的,但是过几秒钟之后,再send就会出错了,返回-1。select也不能检测出可写了。 2.2 Recv函数int recv( SOCKET s, char FAR *buf, int len, int flags);
不论是客户还是服务器应用程序都用recv函数从TCP连接的另一端接收数据。 参数说明: 第一个参数指定接收端套接字描述符;第二个参数指明一个缓冲区,该缓冲区用来存放recv函数接收到的数据;第三个参数指明buf的长度;第四个参数一般置0。12341234 这里只描述同步Socket的recv函数的执行流程。当应用程序调用recv函数时,recv先等待s的发送缓冲中的数据被协议传送完毕, 特别提醒: 如果recv在copy时出错,那么它返回SOCKET_ERROR; 注意:在Unix系统下,如果recv函数在等待协议接收数据时网络断开了,那么调用recv的进程会接收到一个SIGPIPE信号,进程对该信号的默认处理是进程终止。 int send ( SOCKET s, const char FAR * buf, int len, int flags );
三、常见问题问题1:send函数每次最多可以发送多少数据?是int的最大值吗? 问题二:如果buffer中的数据过大,我也只需要调用一次send函数,而底层到底是一次传输成功还是陆续传输我不用管了吗? 问题三:阻塞和非阻塞的区别? 特别注意:You can use setsockopt to enlarge the buffer. 作为一个套接字,它拥有两个缓冲,接收数据缓冲和发送数据缓冲(此缓冲不同与你自己定义的缓冲),当有数据到达时,首先进入的 问题四:缓冲区怎么理解? recv函数也是一样的,它并不是直接从网络中获取数据,而是从输入缓冲区中读取数据。 输入输出缓冲区,系统会为每个socket都单独分配,并且是在socket创建的时候自动生成的。一般来说,默认的输入输出缓冲区大小为8K。套接字关闭的时候,输出缓冲区的数据不会丢失,会由协议发送到另一方;而输入缓冲区的数据则会丢失。 四、 对于数据接收不完整的情况,可以考虑从以下几个方面解决:1:利用SetSocketOpt()函数将接收方套接子接收缓冲设为足够大小; 具体操作:在send()的时候,返回的是实际发送出去的字节(同步)或发送到socket缓冲区的字节(异步);系统默认的状态发送和接收一次为8688字节(约为8.5K);在实际的过程中发送数据和接收数据量比较大,可以设置socket缓冲区,而避免了send(),recv()不断的循环收发: // 接收缓冲区int nRecvBuf=32*1024;//设置为32Ksetsockopt(s,SOL_SOCKET,SO_RCVBUF,(const char*)&nRecvBuf,sizeof(int));//发送缓冲区int nSendBuf=32*1024;//设置为32Ksetsockopt(s,SOL_SOCKET,SO_SNDBUF,(const char*)&nSendBuf,sizeof(int));123456123456 2.基于winsock API,比较实用,自己写的,简单又粗暴同时还有技巧~ bool SendAll(SOCKET &sock, char*buffer, int size){ while (size>0) { int SendSize= send(sock, buffer, size, 0); if(SOCKET_ERROR==SendSize) return false; size = size - SendSize;//用于循环发送且退出功能 buffer+=SendSize;//用于计算已发buffer的偏移量 } return true;}bool RecvAll(SOCKET &sock, char*buffer, int size){ while (size>0)//剩余部分大于0 { int RecvSize= recv(sock, buffer, size, 0); if(SOCKET_ERROR==RecvSize) return false; size = size - RecvSize; buffer+=RecvSize; } return true;}
3.设置为阻塞方式: int flags = fcntl(sock, F_GETFL, 0); fcntl(sock, F_SETFL, flags | O_NONBLOCK);1212 假设当前代码为服务器,并且已经执行过如下代码, 当sock为阻塞模式,调用accept会阻塞直到一个请求到来 当sock为非阻塞模式,accept会返回-1,errno设置为EAGAIN或者EWOULDBLOCK 3.在recv函数之前加sleep(0.01)函数,而不是recv之后,但是感觉这样没什么效果。 五、 参考资料,特此感谢,如有侵权,立删!1.关于setsockopt:https://blog.csdn.net/youxiazzz12/article/details/25634143 |
|