最近使用python socket写了一个程序,遇到了一些问题。总结心得,避免后来人踩坑。

1、send和sendall的区别
发送方使用sendall,不要使用send。sendall在返回的时候,会确保数据已传给tcpip;send并不一定会把数据全部发完,而是发一次,就返回发送的数据长度。在一般的程序设计中,我们更多场景是使用sendall,使用send就需要自己处理未发送的数据。

2、recv是拷贝数据,不是接收数据,每次拷贝多少?
recv并不是取完对方发送的数据,而是取一次。取多少字节,取决于recv的参数buffsize recv(SOCK_BUFF_SIZE)。如果对方发送了超过buffsize的数据,recv需要多次调用,用户自己组装数据,自己写算法确保数据完整性。即使对方发送的数据没有超过buffsize,也有情况需要多次调用recv,因为发送的数据可能超过了当时的tcpip的承载MTU最大传输量。一般recv都是阻塞型的(可以设置)。

3、soceket ‘utf-8’ codec can’t decode 报错
sendall传送的数据类型是字节型,不是字符串,发送前需要encode(例如’utf-8‘),recv后需要decode(‘utf-8’)。有时候会发现encode没有报错,decode报了错误“‘utf-8’ codec can’t decode byte”。有些人修改decode参数 decode(‘utf-8’, ‘ignore’),忽略错误;在有些对数据完整性不敏感的场景下,是可以这么做的。为什么会出现encode没问题,decode有问题?参见下文。

4、recv会丢数据?不会
sendall时,将字符串打印出来;recv后,将字符串打印出来。当recv的buffsize设置成不同的值,会有不同的情况。情况一,接收字符串和发送字符串一致;情况二,不一致。为什么会出现这样呢,soceket发送接收字符串不一致?难道recv会丢数据?
这个和问题3有关。这里说的不一致时字符串不一致,不是字节。
不同的buffsize导致recv每次接收的数据长度不一样,这些数据是字节型的,截断位置不同,就导致不一定能decode成字符,所以出现recv后字符串与发送方不一致;也就是为什么decode有时就报错了。那要怎么处理呢?
先把字节型数据收集齐,再进行decode使用。我做的是,在发送开始标记本次字节长度,接收方recv多次后组装,然后再decode成字符串就没啥问题了。

5、标记了字节长度,接收方接收到了指定的字节长度,仍然出现了错误
问题出现在多线程,发送方是多线程,A线程向send buf里put的过程中,B也put了一个数据,接收方就把Bput的数据当成了A的数据,造成了错误。通过给put加锁,保证了一次数据发送的连续性。