python网络编程-socket“粘包”(小数据发送问题)
一:什么是粘包
“粘包”, 即服务器端你调用时send 2次,但你send调用时,数据其实并没有立刻被发送给客户端,而是放到了系统的socket发送缓冲区里,等缓冲区满了、或者数据等待超时了,数据才会被send到客户端,这样就把好几次的小数据拼成一个大数据,统一发送到客户端了,这么做的目地是为了提高io利用效率,一次性发送总比连发好几次效率高嘛。 但也带来一个问题,就是“粘包”,即2次或多次的数据粘在了一起统一发送了。这里必须要想办法把粘包分开, 因为不分开,你就没办法取出来服务器端返回的命令执行结果的大小。
1)time.sleep(0.5),经多次测试,让服务器程序sleep 至少0.5就会造成缓冲区超时。
2)不用sleep,服务器端每发送一个数据给客户端,就立刻等待客户端进行回应,即调用 conn.recv(1024), 由于recv在接收不到数据时是阻塞的,这样就会造成,服务器端接收不到客户端的响应,就不会执行后面的conn.sendall(命令结果)的指令,收到客户端响应后,再发送命令结果时,缓冲区就已经被清空了,因为上一次的数据已经被强制发到客户端了。
#_*_coding:utf-8_*_ import socket import os,subprocess server = socket.socket() #获得socket实例 server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) server.bind(("localhost",9999)) #绑定ip port server.listen() #开始监听 while True: #第一层loop print("等待客户端的连接...") conn,addr = server.accept() #接受并建立与客户端的连接,程序在此处开始阻塞,只到有客户端连接进来... print("新连接:",addr ) while True: data = conn.recv(1024) if not data: print("客户端断开了...") break #这里断开就会再次回到第一次外层的loop print("收到命令:",data) #res = os.popen(data.decode()).read() #py3 里socket发送的只有bytes,os.popen又只能接受str,所以要decode一下 res = subprocess.Popen(data,shell=True,stdout=subprocess.PIPE).stdout.read() #跟上面那条命令的效果是一样的 if len(res) == 0: res = "cmd exec success,has not output!".encode("utf-8") conn.send(str(len(res)).encode("utf-8")) #发送数据之前,先告诉客户端要发多少数据给它 print("等待客户ack应答...") client_final_ack = conn.recv(1024) #等待客户端响应 print("客户应答:",client_final_ack.decode()) print(type(res)) conn.sendall(res) #发送端也有最大数据量限制,所以这里用sendall,相当于重复循环调用conn.send,直至数据发送完毕 server.close() 接收大数据 server端
#_*_coding:utf-8_*_ import socket import sys client = socket.socket() client.connect(("localhost",9999)) while True: msg = input(">>:").strip() if len(msg) == 0:continue client.send( msg.encode("utf-8") ) res_return_size = client.recv(1024) #接收这条命令执行结果的大小 print("getting cmd result , ", res_return_size) total_rece_size = int(res_return_size) print("total size:",res_return_size) client.send("准备好接收了,发吧loser".encode("utf-8")) received_size = 0 #已接收到的数据 cmd_res = b'' f = open("test_copy.html","wb")#把接收到的结果存下来,一会看看收到的数据 对不对 while received_size != total_rece_size: #代表还没收完 data = client.recv(1024) received_size += len(data) #为什么不是直接1024,还判断len干嘛,注意,实际收到的data有可能比1024少 cmd_res += data else: print("数据收完了",received_size) #print(cmd_res.decode()) f.write(cmd_res) #把接收到的结果存下来,一会看看收到的数据 对不对 #print(data.decode()) #命令执行结果 client.close() 接收大数据客户端