php socket 粘包,Python 学习笔记 - socket(粘包及其处理方式)

本文介绍了Python中使用socket编程时遇到的粘包问题,以及如何通过发送数据长度作为前缀来解决该问题。示例代码展示了客户端和服务器端如何通过预先发送数据长度来协调接收,确保正确传输大块数据。通过这种方式,可以避免由于固定大小的接收缓冲区导致的数据混合。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前面学习了基本的最原始的单线程的socket的原理,下面学习一个新的知识点-粘包。由于我们接受的命令是recv(1024),那么如果当另一端发送的数据大于1024个字节的时候,他就会出现粘包的问题。每次只能发送1024个字节,如果我们直接放在一个循环里面不断发送,不断接受,那么当数据发完以后,他就会卡住在那里,因为我们知道在正常连接状态里,socket的accept和recv都是会进入阻塞的状态(换句话说,没有客户连接或者客户发空包,那么就会卡住!)。如何处理这个问题呢?一个思路是发送之前,先打个招呼,告诉对方自己要发送的字节长度,这样对方可以根据长度判断什么时候终止接受。

下面看一个模拟SSH操作的实例,客户端发送命令,服务器端执行之后返回结果给客户端

Server.py#!/usr/bin/env python

# -*- coding:utf-8 -*-

import socket

import subprocess #导入执行命令模块

ip_port=('127.0.0.1',9999) #定义元祖

#买手机

s=socket.socket()  #绑定协议,生成套接字

s.bind(ip_port)    #绑定ip+协议+端口:用来唯一标识一个进程,ip_port必须是元组格式

s.listen(5)        #定义最大可以挂起胡链接数

while True:  #用来重复接收新的链接

conn,addr=s.accept()   #接收客户端胡链接请求,返回conn(相当于一个特定胡链接),addr是客户端ip+port

#收消息

while True: #用来基于一个链接重复收发消息

try: #捕捉客户端异常关闭(ctrl+c)

recv_data=conn.recv(1024) #收消息,阻塞

if len(recv_data) == 0:break #客户端如果退出,服务端将收到空消息,退出

#发消息

p=subprocess.Popen(str(recv_data,encoding='utf8'),shell=True,stdout=subprocess.PIPE) #执行系统命令,windows平

# 台命令的标准输出是gbk编码,需要转换

res=p.stdout.read()   #获取标准输出

print(res,type(res))

if len(res) == 0:   #执行错误命令,标准输出为空,

send_data='cmd err'

else:

send_data=str(res,encoding='gbk')  #命令执行ok,字节gbk---->str---->字节utf-8

send_data=bytes(send_data,encoding='utf8')

#解决粘包问题

ready_tag='Ready|%s' %len(send_data)

conn.send(bytes(ready_tag,encoding='utf8')) #发送数据长度

feedback=conn.recv(1024)  #接收确认信息

feedback=str(feedback,encoding='utf8')

if feedback.startswith('Start'):

conn.send(send_data)  #发送命令的执行结果

except Exception:

break

conn.close()

client.py#!/usr/bin/env python

# -*- coding:utf-8 -*-

import socket

ip_port=('127.0.0.1',9999)

s=socket.socket()

s.connect(ip_port)  #链接服务端,如果服务已经存在一个好的连接,那么挂起

while True:        #基于connect建立的连接来循环发送消息

send_data=input(">>: ").strip()

if send_data == 'exit':break

if len(send_data) == 0:continue

s.send(bytes(send_data,encoding='utf8'))

#解决粘包问题

ready_tag=s.recv(1024) #收取带数据长度的字节:Ready|9998

ready_tag=str(ready_tag,encoding='utf8')

if ready_tag.startswith('Ready'):#Ready|9998

msg_size=int(ready_tag.split('|')[-1])  #获取待接收数据长度

start_tag='Start'

s.send(bytes(start_tag,encoding='utf8')) #发送确认信息

#基于已经收到的待接收数据长度,循环接收数据

recv_size=0

recv_msg=b''

while recv_size 

recv_data=s.recv(1024)

recv_msg+=recv_data

recv_size+=len(recv_data)

print('MSG SIZE %s RECE SIZE %s' %(msg_size,recv_size))

print(str(recv_msg,encoding='utf8'))

s.close()

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值