一、粘包问题
1、问题一:无法确认对方发送过来数据的大小,对数据接收有影响
server.py文件内容:
<span>"""</span><span> 先启动套接字服务端 注意: 客户端一次发送,服务端先一次接收,再发送 </span><span>"""</span> <span>import</span><span> <a href="https://www.gaodaima.com/tag/socket" title="查看更多关于socket的文章" target="_blank">socket</a> </span><span>import</span><span> subprocess server </span>=<span> socket.socket() server.bind((</span><span>"</span><span>127.0.0.1</span><span>"</span>, 9527<span>)) server.listen(</span>5<span>) </span><span>while</span><span> True: conn, addr </span>=<span> server.accept() </span><span>print</span><span>(addr) </span><span>while</span><span> True: </span><span>try</span><span>: </span><span>#</span><span> 从内存中获取数据</span> data = conn.recv(1024<span>) </span><span>if</span> len(data) ==<span> 0: </span><span>continue</span><span> data </span>= data.decode(<span>"</span><span>utf-8</span><span>"</span><span>) </span><span>if</span> data == <span>"</span><span>q</span><span>"</span><span>: </span><span>break</span> <span>#</span><span> 调用subprocess,对终端进行操作,并获取操作后正确或错误的结果</span> <span>#</span><span> 接收转码后的字符串</span> obj = subprocess.Popen(data, shell=True, stdout=subprocess.PIPE, stderr=<span>subprocess.PIPE) </span><span>#</span><span> 将结果交给result变量</span> result = obj.stdout.read() +<span> obj.stderr.read() </span><span>print</span>(result.decode(<span>"</span><span>gbk</span><span>"</span><span>)) </span><span>#</span><span> 将结果返回给客户端</span> <span> conn.send(result) </span><span>except</span><span> Exception as e: </span><span>print</span><span>(e) </span><span>break</span><span> conn.close()</span>
www#gaodaima.com来源gao@!dai!ma.com搞$$代^@码网搞代码
client.py文件内容:
<span>"""</span><span> 启动服务端后再启动客户端 </span><span>"""</span> <span>import</span><span> socket client </span>=<span> socket.socket() client.connect((</span><span>"</span><span>127.0.0.1</span><span>"</span>, 9527<span>)) </span><span>while</span><span> True: cmd </span>= input(<span>"</span><span>请输入服务端命令:</span><span>"</span><span>) client.send(cmd.encode(</span><span>"</span><span>utf-8</span><span>"</span><span>)) data </span>= client.recv(1024<span>) <span># 这里只接受到1024字节数据,可以调整,但是对内存有影响 </span></span><span>if</span> len(data) ==<span> 0: </span><span>continue</span> <span>if</span> data == <span>"</span><span>q</span><span>"</span><span>: </span><span>break</span> <span>print</span>(data.decode(<span>"</span><span>gbk</span><span>"</span>))
server.py执行结果:
client.py执行结果:
2、问题二:在发送数据间隔短并且数据量小的情况下,会将所有数据一次性传入
server.py文件内容:
<span>"""</span><span> 先启动套接字服务端 注意: 客户端一次发送,服务端先一次接收,再发送 </span><span>"""</span> <span>import</span><span> socket server </span>=<span> socket.socket() server.bind((</span><span>"</span><span>127.0.0.1</span><span>"</span>, 9527<span>)) server.listen(</span>5<span>) conn, addr </span>=<span> server.accept() data </span>= conn.recv(1024<span>) </span><span>print</span>(data)
client.py文件内容:
<span>"""</span><span> 启动服务端后再启动客户端 </span><span>"""</span> <span>import</span><span> socket client </span>=<span> socket.socket() client.connect((</span><span>"</span><span>127.0.0.1</span><span>"</span>, 9527<span>)) <br><span>"""</span><br><span>发送三次请求,理论上结果应该是</span><br><span>b"hello"</span><br><span>b"hello"</span><br><span>b"hello"</span><br><span>"""</span> client.send(b</span><span>"</span><span>hello</span><span>"</span><span>) client.send(b</span><span>"</span><span>hello</span><span>"</span><span>) client.send(b</span><span>"</span><span>hello</span><span>"</span>)
server.py执行结果:
3、解决粘包问题(struct模块)
1、struct模块是什么
struct模块是一个python内置模块,他可以将固定长度的数据,打包成固定格式的长度
” i “模式:可以将数据打包成 4 个bytes
2、struct模块的作用
可以将真实数据,做成一个固定长度的报头,客户端发送给服务端,服务端可以接受报头,然后对报头进行解包,获取真实数据的长度,进行接收即可
3、struct模块的使用
<span>import</span><span> struct data </span>= b<span>"</span><span>11111111111111</span><span>"</span> <span>#</span><span> 打包制作报头</span> header = struct.pack(<span>"</span><span>i</span><span>"</span><span>, len(data)) </span><span>print</span><span>(header) </span><span>#</span><span> 解包获取真实数据长度 ---> 得到一个元组,元组中第一个值是真实数据的长度</span> res = struct.unpack(<span>"</span><span>i</span><span>"</span><span>, header)[0] </span><span>print</span>(res)
执行结果:
b<span>"</span><span>x0ex00x00x00</span><span>"</span> 14
4、粘包问题解决方法
只要确认对方数据的大小(长度),根据大小进行接收即可解决
- 无论哪一端先发送数据(例 客户端先行发送数据)
- 客户端
- 先制作报头并发送 struct.pack()
- 发送真实数据
- 服务端:
- 接收报头,并解包获取真实数据长度 struct.unpack()
- 根据真实数据长度 接收真实数据
server.py文件内容:
<span>import</span><span> socket </span><span>import</span><span> subprocess </span><span>import</span><span> struct server </span>=<span> socket.socket() server.bind((</span><span>"</span><span>127.0.0.1</span><span>"</span>, 8001<span>)) server.listen(</span>5<span>) </span><span>while</span><span> True: conn, addr </span>=<span> server.accept() </span><span>#</span><span> print(addr)</span> <span>while</span><span> True: </span><span>try</span><span>: </span><span>#</span><span> 获取客户端传过来的报头</span> client_headers = conn.recv(4<span>) </span><span>#</span><span> 解包获取真实数据长度</span> data_len = struct.unpack(<span>"</span><span>i</span><span>"</span><span>, client_headers)[0] </span><span>#</span><span> 准备接收真实数据</span> data =<span> conn.recv(data_len) </span><span>if</span> len(data) ==<span> 0: </span><span>continue</span><span> data </span>= data.decode(<span>"</span><span>utf-8</span><span>"</span><span>) </span><span>if</span> data == <span>"</span><span>q</span><span>"</span><span>: </span><span>break</span> <span>#</span><span> 调用subprocess,对终端进行操作,并获取操作后正确或错误的结果</span> <span>#</span><span> 接收转码后的字符串</span> obj = subprocess.Popen(data, shell=True, stdout=subprocess.PIPE, stderr=<span>subprocess.PIPE) </span><span>#</span><span> 将结果交给result变量</span> result = obj.stdout.read() +<span> obj.stderr.read() </span><span>#</span><span> 服务端做一个报头发送给客户端</span> server_headers = struct.pack(<span>"</span><span>i</span><span>"</span><span>, len(result)) </span><span>#</span><span> 传送报头数据</span> <span> conn.send(server_headers) </span><span>#</span><span> 待客户端确认长度后,发送真实数据返回给客户端</span> <span> conn.send(result) </span><span>except</span><span> Exception as e: </span><span>print</span><span>(e) </span><span>break</span><span> conn.close()</span>
client.py文件内容:
<span>import</span><span> socket </span><span>import</span><span> struct client </span>=<span> socket.socket() client.connect((</span><span>"</span><span>127.0.0.1</span><span>"</span>, 8001<span>)) </span><span>while</span><span> True: cmd </span>= input(<span>"</span><span>请输入服务端命令:</span><span>"</span><span>) cmd_bytes </span>= cmd.encode(<span>"</span><span>utf-8</span><span>"</span><span>) </span><span>#</span><span> 做一个报头</span> client_headers = struct.pack(<span>"</span><span>i</span><span>"</span><span>, len(cmd_bytes)) </span><span>#</span><span> 想服务端传送报头</span> <span> client.send(client_headers) </span><span>#</span><span> 待服务端确认数据长度后,发送真实数据</span> <span> client.send(cmd_bytes) </span><span>#</span><span> 获取服务端的传过来的报头</span> server_headers = client.recv(4<span>) </span><span>#</span><span> 解包获取数据的真实长度</span> data_len = struct.unpack(<span>"</span><span>i</span><span>"</span><span>, server_headers)[0] </span><span>#</span><span> 准备接受真实数据</span> data =<span> client.recv(data_len) </span><span>if</span> len(data) ==<span> 0: </span><span>continue</span> <span>if</span> data == <span>"</span><span>q</span><span>"</span><span>: </span><span>break</span> <span>print</span>(data.decode(<span>"</span><span>gbk</span><span>"</span>))
可自行测试。