粘包现象(存在于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()

浙公网安备 33010602011771号