当前位置: 首页 > news >正文

网络通信与并发编程(三)粘包现象解决方案、socketserver实现并发

粘包现象解决方案、socketserver实现并发

文章目录

  • 粘包现象解决方案、socketserver实现并发
  • 一、粘包现象解决方案
    • 1.发送数据大小
    • 2.发送数据信息
  • 二、socketserver实现并发
    • 1.tcp版的socketserver并发
    • 2.udp版的socketserver并发

一、粘包现象解决方案

1.发送数据大小

有了上一篇文章的分析,我们知道tcp协议之所以会出现粘包现象,是因为无法得知每次传输的字节数。如果我们人为的给传输的数据添加一个报头用来表示接收内容的字节数就可以解决这个问题了。

#服务端
from socket import *
import subprocess
import structserver=socket(AF_INET,SOCK_STREAM)
server.bind(('127.0.0.1',8080))
server.listen(5)while True:conn,client_addr=server.accept()while True:try:cmd=conn.recv(1024)if len(cmd) == 0:breakobj=subprocess.Popen(cmd.decode('utf-8'),shell=True,stderr=subprocess.PIPE,stdout=subprocess.PIPE)stdout=obj.stdout.read()stderr=obj.stderr.read()#1、制作报头(固定长度)total_size=len(stdout) + len(stderr)#struct模块可以将一个数据类型转为固定长度的bytesheader=struct.pack('i',total_size)#2、发送固定长度的报头conn.send(header)#3、发送真实的数据conn.send(stdout)conn.send(stderr)except ConnectionResetError:breakconn.close()
#客户端
from socket import *
import structclient=socket(AF_INET,SOCK_STREAM)
client.connect(('127.0.0.1',8080))while True:cmd=input('>>: ').strip()if len(cmd) == 0:continueclient.send(cmd.encode('utf-8'))#1、接收固定长度的报头(bytes)header=client.recv(4)#从报头中解析接收内容的字节数total_size=struct.unpack('i',header)[0]#2、接收真实的数据#recv_size表示已接收字节数,total_size表示总的字节数recv_size=0res=b''while recv_size < total_size :data=client.recv(1024)res+=datarecv_size+=len(data)print(res.decode('gbk'))

接收结果:
映像名称 PID 会话名 会话# 内存使用
========================= ======== ================ =========== ============
System Idle Process 0 Services 0 8 K
System 4 Services 0 152 K
(接收内容过长,中间部分略去)
tasklist.exe 16076 Console 1 9,520 K
WmiPrvSE.exe 15416 Services 0 10,212 K

2.发送数据信息

在一般的文件传输中,我们除了可以看到文件的大小,还可以看见文件名称,创建日期当信息,为了将这些信息加入到报头中,我们可以将上面的代码进一步优化为如下形式:

#服务端
from socket import *
import subprocess
import struct
import json,timeserver=socket(AF_INET,SOCK_STREAM)
server.bind(('127.0.0.1',8080))
server.listen(5)while True:conn,client_addr=server.accept()while True:try:cmd=conn.recv(1024)if len(cmd) == 0:break# 运行系统命令obj=subprocess.Popen(cmd.decode('utf-8'),shell=True,stderr=subprocess.PIPE,stdout=subprocess.PIPE)stdout=obj.stdout.read()stderr=obj.stderr.read()#将报头信息制作成字典header_dic={'filename':'tasklist','total_size':len(stdout) + len(stderr),'create_time':time.strftime('%Y-%m-%d %H')}#通过json格式将报头字典转为bytesheader_json=json.dumps(header_dic)header_bytes=header_json.encode('utf-8')#由于报头字典大小超出struct的限制,所以此处先发送4个字节标识报头字典的大小#1、发送报头字典大小conn.send(struct.pack('i',len(header_bytes)))#2、发送报头字典conn.send(header_bytes)#3、发送真实的数据conn.send(stdout)conn.send(stderr)except ConnectionResetError:breakconn.close()
#客户端
from socket import *
import struct
import jsonclient=socket(AF_INET,SOCK_STREAM)
client.connect(('127.0.0.1',8080))while True:cmd=input('>>: ').strip()if len(cmd) == 0:continueclient.send(cmd.encode('utf-8'))#1、收4个字节,这4个字节表示报头长度header_len=struct.unpack('i',client.recv(4))[0]#2、再接收报头header_bytes=client.recv(header_len)#通过json解析出报头字典header_json=header_bytes.decode('utf-8')header_dic=json.loads(header_json)print(header_dic)total_size=header_dic['total_size']#3、接收真实的数据recv_size=0res=b''while recv_size < total_size :data=client.recv(1024)res+=datarecv_size+=len(data)print(res.decode('gbk'))

可以看到结果中接收到了报头信息:
{‘filename’: ‘tasklist’, ‘total_size’: 17942, ‘create_time’: ‘2024-10-18 18’}
映像名称 PID 会话名 会话# 内存使用
========================= ======== ================ =========== ============
System Idle Process 0 Services 0 8 K
System 4 Services 0 152 K
(接收内容过长,中间部分略去)
tasklist.exe 16076 Console 1 9,520 K
WmiPrvSE.exe 15416 Services 0 10,212 K

二、socketserver实现并发

1.tcp版的socketserver并发

#客户端
import socket
#表示当前开启的客户端序号
#并发通信时,可以开启斗个客户端与服务端通信
num=33
phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
phone.connect(('127.0.0.1',8080)) while True:msg = 'client%s'%numif len(msg) == 0:continuephone.send(msg.encode('utf-8'))data=phone.recv(1024)print(data)phone.close()
#服务端
import socketserver
#MyHandler为自定义的通信函数
class MyHandler(socketserver.BaseRequestHandler):#MyHandler继承了abc类,必须复写handle函数def handle(self):#通信循环while True:try:#self.request中存放这通信连接#self.client_address中存放着客户端的ip和端口号#self.server中存放着套接字对象data=self.request.recv(1024)if len(data) == 0:breakself.request.send(data.upper())except ConnectionResetError:breakif __name__ == '__main__':#创建socketserver对象s=socketserver.ThreadingTCPServer(('127.0.0.1',8080),MyHandler,bind_and_activate=True)#serve_forever函数在有客户端发送请求时创建一个线程#这个线程会根据建立的通信链接与客户端通信(创建MyHandler对象并调用handle方法)s.serve_forever()

2.udp版的socketserver并发

#客户端
import socket
num=33
client=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
while True:msg='client%s'%numclient.sendto(msg.encode('utf-8'),('127.0.0.1',8080))data,server_addr=client.recvfrom(1024)print(data)client.close()
#服务端
import socketserver
class MyHandler(socketserver.BaseRequestHandler):def handle(self):# 通信循环#self.request[1]存放着套接字对象#self.client_address存放着客户端的信息#self.request[0]存放着客户端发送的信息data = self.request[0]self.request[1].sendto(data.upper(), self.client_address)if __name__ == '__main__':s = socketserver.ThreadingUDPServer(('127.0.0.1', 8080), MyHandler)#当客户端发送信息以后,serve_forever函数会创建一个线程与客户端进行通信s.serve_forever()
http://www.lryc.cn/news/462936.html

相关文章:

  • 使用Uniapp开发微信小程序实现一个自定义的首页顶部轮播图效果?
  • 软硬连接及动静态库
  • vue3.0 + vite:中使用 sass
  • 搭建`mongodb`副本集-开启权限认证 mongo:7.0.5
  • 智能工厂的软件设计 由“原力“篇引发的思考: 回顾、展望和本位 之2 修订稿之2
  • 2025选题推荐|基于SpringBoot的幼儿园智能管理与监控系统的设计与实现
  • mqtt客户端订阅一直重复连接?
  • SegFormer: 一个基于Transformer的高效视觉图像分割算法
  • SoC芯片中Clock Gen和Reset Gen的时钟树综合
  • 学习资料:电子标签拣货技术
  • Git 提交规范参考
  • 【前端】Matter:物体的高级控制
  • ASP.NET Core 路由规则 MapControllerRoute、MapDefaultControllerRoute、MapController
  • linux命令之less用法
  • 试用cursor的简单的记录
  • 下载Edge/Chrome浏览器主题的背景图片
  • Python解力扣算法题4(基础)
  • 鸿蒙NEXT开发-网络管理(基于最新api12稳定版)
  • 【LeetCode每日一题】——1413.逐步求和得到正数的最小值
  • 音频分割:长语音音频 分割为 短语音音频 - python 实现
  • 【Echarts动态排序图,series使用背景色更新动画,背景底色不同步跟随柱子动画】大家有没有解决方案
  • FLINK SQL 元数据持久化扩展
  • MySQL【知识改变命运】04
  • AD9680(adc直采芯片)使用说明
  • 无人机之位置信息计算篇
  • 安卓cpu知识
  • 图书管理新纪元:Spring Boot进销存系统
  • Application protocol
  • 金融信用评分卡建模项目:AI辅助
  • java对接GPT 快速入门