粘包现象(存在于tcp中)
多个包 多个命令的结果 粘到一起了 因为recv(1024)1024限制了导致的结果
所谓粘包问题主要还是因为接收方不知道消息之间的界限,不知道一次性提取多少字节的数据所造成的。
参考:http://www.cnblogs.com/linhaifeng/articles/6129246.html
粘包底层原理分析:
1、运行一个软件和硬盘、内存、cpu这三个硬件有关
2、启动程序:硬盘程序加载到内存启动一个软件就占一个内存空间
操作系统本身有一个内存空间
操作系统所占得到内存空间和软件的内存空间彼此互相隔离
注:只有TCP有粘包现象,UDP永远不会粘包。
3、send、recv的底层原理
应用程序通过send将自己数据发给操作系统,操作系统调用相应硬件中的数据及程序,通过网卡传送给操作系统(recv对应的操作系统),操作系统通过相应程序将数据传送给secv
1、send发到数据到服务端os的内存 # 慢
2、os的内存 copy 给程序 # 快
站在应用程序角度上:
send: 1.数据发给本地的os # 耗时短一些
recv:1.通知os收数据 2.os内存数据copy到应用程序内存中 # 耗时长一些
4、send recv 总结:
a、不管是recv还是send都不是直接接收对方的数据,而是操作自己的操作系统内存--->不是一个send对应一个recv # 一发可以对应多收 一收对应多发
b、recv:
wait data 耗时非常长
copy data
send:
copy data
c、数据量比较小 时间间隔比较短就合并成一个包,再发
使用了优化方法(Nagle算法)
5.recv 有关部门建议的不要超过 8192,再大反而会出现影响收发速度和不稳定的情况
解决粘包现象
用struct模块
import struct # res = struct.pack("i", 1230) # 第一个参数代表格式i(int) # print(res, type(res), len(res)) # # obj = struct.unpack("i", res) # print(obj[0]) # clienr.recv(4) res = struct.pack("l", 1200000000) # 第一个参数代表格式l(long) print(res, type(res), len(res))
解决粘包解释加报头
解决粘包(简单版)
服务端
import socket import subprocess import struct # 1、买手机 phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # phone.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) # (如果机器中存在,重新用端口)应对端口占用报错情况 # 2、绑定手机卡 phone.bind(("127.0.0.1", 9909)) # 127.0.0.1本地地址,端口范围0-65535:其中0-1024给操作系统使用 # 3、开机 phone.listen(5) # 5代表最大挂起连接数 # 4、等电话连接 print("starting...") while True: # 循环链接 conn, client = phone.accept() # conn套接字对象 # 5、收、发消息 while True: # 通讯循环 try: # a、接收命令 (命令:执行系统命令) cmd = conn.recv(8096) # 收1024个字节,接受数据的最大数。单位是bytes # if not data: break # 仅适用于Linux操作系统(客户端断开),win 用try...except # b、执行命令,拿到结果 obj = subprocess.Popen(cmd.decode("utf-8"), shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) stdout = obj.stdout.read() stderr = obj.stderr.read() # c、把命令的结果返回给客户端 # 第一步:制作固定长度的报头(import struct) total_size = len(stdout) + len(stderr) header = struct.pack("i", total_size) # 第二步:把报头(固定长度)发送给客户端 conn.send(header) # 第二步:再发送真实的数据 conn.send(stdout) conn.send(stderr) except ConnectionRefusedError: break # 6、挂电话 conn.close() # 7、关机 phone.close()
客户端
import socket import struct # 1、买手机 phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 2、打电话 phone.connect(("127.0.0.1", 9909)) # phone相当于服务端的conn # 3、发、收消息 while True: # a、发命令 cmd = input(">> ").strip() if not cmd: continue phone.send(cmd.encode("utf-8")) # b、拿命令结果并打印 # 第一步:先收报头 header = phone.recv(4) # 第二步:从报头中解析出对真实数据的描述信息(数据的长度) total_size = struct.unpack("i", header)[0] # 第三步:接受真实的数据 recv_size = 0 recv_data = b"" while recv_size < total_size: res = phone.recv(1024) recv_data += res recv_size += len(res) print(recv_data.decode("gbk")) # 系统发回的结果 # 4、关闭 phone.close()
解决粘包(终极版)
服务端
import socket import subprocess import struct import json # 1、买手机 phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # phone.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) # (如果机器中存在,重新用端口)应对端口占用报错情况 # 2、绑定手机卡 phone.bind(("127.0.0.1", 9909)) # 127.0.0.1本地地址,端口范围0-65535:其中0-1024给操作系统使用 # 3、开机 phone.listen(5) # 5代表最大挂起连接数 # 4、等电话连接 print("starting...") while True: # 循环链接 conn, client = phone.accept() # conn套接字对象 # 5、收、发消息 while True: # 通讯循环 try: # a、接收命令 (命令:执行系统命令) cmd = conn.recv(8096) # 收1024个字节,接受数据的最大数。单位是bytes # if not data: break # 仅适用于Linux操作系统(客户端断开),win 用try...except # b、执行命令,拿到结果 obj = subprocess.Popen(cmd.decode("utf-8"), shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) stdout = obj.stdout.read() stderr = obj.stderr.read() # c、把命令的结果返回给客户端 # 第一步:制作固定长度的报头(import struct) header_dic = { "filename": "a.text", "md5": "xxdxxx", "total_size": len(stdout) + len(stderr) } header_json = json.dumps(header_dic) header_bytes = header_json.encode("utf-8") # 第二步:先发送报头的长度 conn.send(struct.pack("i", len(header_bytes))) # 第三步:再发报头 conn.send(header_bytes) # 第四步:再发送真实的数据 conn.send(stdout) conn.send(stderr) except ConnectionRefusedError: break # 6、挂电话 conn.close() # 7、关机 phone.close()
客户端
import socket import struct import json # 1、买手机 phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 2、打电话 phone.connect(("127.0.0.1", 9909)) # phone相当于服务端的conn # 3、发、收消息 while True: # a、发命令 cmd = input(">> ").strip() if not cmd: continue phone.send(cmd.encode("utf-8")) # b、拿命令结果并打印 # 第一步:先收报头的长度 header = phone.recv(4) header_size = struct.unpack("i", header)[0] #第二步:再接收报头信息 header_bytes = phone.recv(header_size) # 第三步:从报头中解析出对真实数据的描述信息 header_json = header_bytes.decode("utf-8") header_dic = json.loads(header_json) print(header_dic) total_size = header_dic["total_size"] # 第四步:接受真实的数据 recv_size = 0 recv_data = b"" while recv_size < total_size: res = phone.recv(1024) recv_data += res recv_size += len(res) print(recv_data.decode("gbk")) # 系统发回的结果 # 4、关闭 phone.close()