python中socket接受数据的三种方法

原位置:http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/408859


Description:

An issue with socket.recv is how to know when you are done receiving data. A TCP stream guarantees the bytes will not arrive out of order or be sent more than once. But you do not know the size of the data that will be sent to you. 100 bytes could be sent as group of 10 bytes or maybe in one shot. Ultimately, this means you have to use a loop in some fashion until you know it is done.

The basic recv returns an empty string when the socket is disconnected.
From that you can build a simple loop that will work as long as the sender manages to disconnect the socket at the appropriate time. However, there could be situations where a local error will mask as a clean shutdown or maybe a close() is never called.

Three very basic methods are shown below that try to fix that problem. They use either a time-based, end marker, or size of payload method. Since you cannot be sure just what you are going to receive, you have to be careful that you get enough of a message to determine the size of payload or end marker.

I updated the recv_size method to allocate data in larger chunks if it gets a large stream of data, which can increase performance.

代码如下:

import socket,struct,sys,time
Port=2222
#assume a socket disconnect (data returned is empty string) means all data was #done being sent.
def recv_basic(the_socket):
total_data=[]
while True:
data = the_socket.recv(8192)
if not data: break
total_data.append(data)
return ''.join(total_data)

def recv_timeout(the_socket,timeout=2):
the_socket.setblocking(0)
total_data=[];data='';begin=time.time()
while 1:
#if you got some data, then break after wait sec
if total_data and time.time()-begin>timeout:
break
#if you got no data at all, wait a little longer
elif time.time()-begin>timeout*2:
break
try:
data=the_socket.recv(8192)
if data:
total_data.append(data)
begin=time.time()
else:
time.sleep(0.1)
except:
pass
return ''.join(total_data)

End='something useable as an end marker'
def recv_end(the_socket):
total_data=[];data=''
while True:
data=the_socket.recv(8192)
if End in data:
total_data.append(data[:data.find(End)])
break
total_data.append(data)
if len(total_data)>1:
#check if end_of_data was split
last_pair=total_data[-2]+total_data[-1]
if End in last_pair:
total_data[-2]=last_pair[:last_pair.find(End)]
total_data.pop()
break
return ''.join(total_data)

def recv_size(the_socket):
#data length is packed into 4 bytes
total_len=0;total_data=[];size=sys.maxint
size_data=sock_data='';recv_size=8192
while total_len<size:
sock_data=the_socket.recv(recv_size)
if not total_data:
if len(sock_data)>4:
size_data+=sock_data
size=struct.unpack('>i', size_data[:4])[0]
recv_size=size
if recv_size>524288:recv_size=524288
total_data.append(size_data[4:])
else:
size_data+=sock_data
else:
total_data.append(sock_data)
total_len=sum([len(i) for i in total_data ])
return ''.join(total_data)


##############
def start_server(recv_type=''):
sock=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
sock.bind(('',Port))
sock.listen(5)
print 'started on',Port
while True:
newsock,address=sock.accept()
print 'connected'
if recv_type=='size': result=recv_size(newsock)
elif recv_type=='end': result=recv_end(newsock)
elif recv_type=='timeout': result=recv_timeout(newsock)
else: result=newsock.recv(8192)
print 'got',result


if __name__=='__main__':
#start_server()
#start_server(recv_type='size')
#start_server(recv_type='timeout')
start_server(recv_type='end')

def send_size(data):
sock=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
sock.connect(('localhost',Port))
sock.sendall(struct.pack('>i', len(data))+data)
sock.close()

def send_end(data):
sock=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
sock.connect(('localhost',Port))
sock.sendall(data+End)
sock.close()

Discussion:

I employ a trivial server, to keep this as simple as possible. Just uncomment the type of receiving server you want to use to see the recv type chosen in action.

The recv_timeout function, which uses non-blocking sockets, will continue trying to get data as long as the client manages to even send a single byte. This is useful for moving data which you know very little about (like encrypted data), so cannot check for completion in a sane way.

The recv_end function tacks on an end marker understood by both the client and the server. One problem with this is that data cannot look like the marker.

The recv_size function looks for the size of the payload to be prepended to the data. I use a fixed length, 4 bytes. So then I know to always look for that. It is packed to allow a terse representation that can fit sys.maxint. This avoids the problem of data looking like the marker, however, even if it means a lot of data, you are limited w/the payload being the maximum number that can be packed. An interesting advantage of this method is that it can allocate data in larger chunks since it knows this size of the data ahead of time. For large streams of data, I saw it increase performace by 10 times.


To test this, in a another process, try using the functions that match with the server type.

send_size(data) #for recv_size
send_end(data) #for recv_end

sock.sendall(data) #for timeout or simple recv(8192)
#do not forget to close if you do a raw sendall
sock.close()


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
回答: 从你提供的引用内容来看,你的代码使用了Socket创建TCP服务端,并通过while循环一直接收小程序发送的数据。然而,你发现接收数据的速度比较慢。这个问题可能有多种原因导致。 首先,可能是因为你的代码没有设置合适的缓冲区大小。在你的服务端代码,你使用了一个缓冲区大小为BUF_SIZE的变量来接收数据。如果BUF_SIZE的值过小,可能会导致接收数据的速度变慢。你可以尝试增大缓冲区的大小,看看是否能提高接收数据的速度。 其次,可能是因为你的代码没有使用合适的数据接收方式。在你的服务端代码,你使用了一个嵌套的while循环来接收数据,这可能会导致接收数据的速度变慢。你可以尝试使用更高效的数据接收方式,比如使用recv()函数一次性接收所有数据,而不是逐个字节地接收数据。 另外,可能是因为你的网络环境不稳定,导致数据传输速度变慢。你可以尝试检查你的网络连接,确保网络连接稳定。 总结起来,要解决接收数据慢的问题,你可以尝试增大缓冲区的大小,使用更高效的数据接收方式,以及检查网络连接的稳定性。希望这些建议能帮助你解决问题。\[1\]\[2\]\[3\] #### 引用[.reference_title] - *1* [解决python程序Socket accept的阻塞问题](https://blog.csdn.net/m0_52239706/article/details/119139351)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insert_down28v1,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* *3* [Python实现socket通信样例,解决recv函数(客户端)收信(数据)不完整不全问题](https://blog.csdn.net/Gu_fCSDN/article/details/121277136)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insert_down28v1,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值