盘点python socket 中recv函数的坑

1. 首先来看一下recv函数的各个参数

函数原型:int recv( SOCKET s, char *buf, int  len, int flags)

功能:不论是客户还是服务器应用程序都用recv函数从TCP连接的另一端接收数据。

参数一:指定接收端套接字描述符;

参数二:指明一个缓冲区,该缓冲区用来存放recv函数接收到的数据;

参数三:指明buf的长度;

参数四 :一般置为0。
 

同步Socket的recv函数的执行流程:当应用程序调用recv函数时,recv先等待s的发送缓冲中的数据被协议传送完毕,

如果协议在传送s的发送缓冲中的数据时出现网络错误,那么recv函数返回SOCKET_ERROR;

如果s的发送缓冲中没有数据或者数据被协议成功发送完毕后,recv先检查套接字s的接收缓冲区,如果s接收缓冲区中没有数据或者协议正在接收数据,那么recv就一直等待,直到协议把数据接收完毕;

当协议把数据接收完毕,recv函数就把s的接收缓冲中的数据copy到buf中(注意协议接收到的数据可能大于buf的长度,所以在这种情况下要调用几次recv函数才能把s的接收缓冲中的数据copy完。recv函数仅仅是copy数据,真正的接收数据是协议来完成的),recv函数返回其实际copy的字节数;

如果recv在copy时出错,那么它返回SOCKET_ERROR;如果recv函数在等待协议接收数据时网络中断了,那么它返回0。
 

2.“坑”在哪里,在返回值上,在阻塞与非阻塞上

要知道,recv函数是阻塞的,也就是会一直等待服务端发送来的数据包。如果没有数据包到来,就一直会等待。

这就直接导致了我用python写服务端的时候出现的一个解决了很久的错误,我的代码很简单,就是python服务端循环调用recv函数接收从客户端发来的一个文件,如下:

    data = connection.recv(1024)
    total_data += data
    num = len(data)
    # 我一开始以为如果没有数据了,读出来的data长度为0,len(data)==0,从而导致卡在while循环中
    while len(data) > 0:
        data = connection.recv(1024)
        num += len(data)
        total_data += data

而实际上只有当recv函数在等待协议接收数据时网络中断了,它才返回0,这就导致我在服务端一直没有反应,等待了很久之后,我将客户端关闭之后,出现了结果,其实就是这时候 len(data)=0 了。

 

3. 解决方法有哪些呢?

后来搜集资料才发现recv本身是一个阻塞的,所以可以通过以下两种方法,将recv设置为非阻塞:

1. socket.setblocking(0)

2. 使用 socket.MSG_DONTWAIT

 

注意将套接字设置为非阻塞时,可能会报一个错:

WinError 10035 无法立即完成一个非阻止性套接字操作

这个错误就是,recv不阻塞了。在 send 数据出去后,服务端还来没来得急返回数据,客户端已经跑到了recv这里,而又不阻塞,所以抛出了一个异常。

要解决这个问题,你需要在每个 recv 函数前加一句 time.sleep(2),

 

其实还可以通过设置 socket.settimeout(5)  即超时时间来改变recv的阻塞状态,间接地将阻塞状态变为非阻塞状态。当然,你得确保你的超时时间足够使recv函数接收完所有的数据才行。

我就是通过设置超时时间解决了这个问题,你如果也有同样的问题的话,你也可以试试。

 

最后遇到问题,一定要去多看看别人是怎么解决的,多和有经验的人交流交流,不然时间就在纠结苦恼中溜走了。当然,自己首先要进行独立思考,并将问题一步步放小,找到真正的问题症结所在,再去下手。最后如果问题得到解决了,记得分享一下以供更多人参考哦~

 

 

 

你可能感兴趣的:(python)