文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

31_网络编程-struct

2023-01-30 22:35

关注
一、struct
 
  1、简述
   我们可以借助一个模块,这个模块可以把要发送的数据长度转换成固定长度的字节。这样客户端每次接收消息之前只要先接受这个固定长度字节的内容看一看接下来要接收的信息大小,那么最终接受的数据只要达到这个值就停止,就能刚好不多不少的接收完整的数据了。
  该模块可以把一个类型,如数字,转成固定长度的bytes
1 >>> struct.pack('i',1111111111111)
2 struct.error: 'i' format requires -2147483648 <= number <= 2147483647 #这个是范围
 

 

 1 import json,struct
 2 #假设通过客户端上传1T:1073741824000的文件a.txt
 3 
 4 #为避免粘包,必须自定制报头
 5 header={'file_size':1073741824000,'file_name':'/a/b/c/d/e/a.txt','md5':'8f6fbf8347faa4924a76856701edb0f3'} #1T数据,文件路径和md5值
 6 
 7 #为了该报头能传送,需要序列化并且转为bytes
 8 head_bytes=bytes(json.dumps(header),encoding='utf-8') #序列化并转成bytes,用于传输
 9 
10 #为了让客户端知道报头的长度,用struck将报头长度这个数字转成固定长度:4个字节
11 head_len_bytes=struct.pack('i',len(head_bytes)) #这4个字节里只包含了一个数字,该数字是报头的长度
12 
13 #客户端开始发送
14 conn.send(head_len_bytes) #先发报头的长度,4个bytes
15 conn.send(head_bytes) #再发报头的字节格式
16 conn.sendall(文件内容) #然后发真实内容的字节格式
17 
18 #服务端开始接收
19 head_len_bytes=s.recv(4) #先收报头4个bytes,得到报头长度的字节格式
20 x=struct.unpack('i',head_len_bytes)[0] #提取报头的长度,解包出来是元组
21 
22 head_bytes=s.recv(x) #按照报头长度x,收取报头的bytes格式
23 header=json.loads(json.dumps(header)) #提取报头
24 
25 #最后根据报头的内容提取真实的数据,比如
26 real_data_len=s.recv(header['file_size'])
27 s.recv(real_data_len)
View Code

 

  2、struct解决黏包问题
  借助struct模块,我们知道长度数字可以被转换成一个标准大小的4字节数字。因此可以利用这个特点来预先发送数据长度。
 
发送时 接收时
先发送struct转换好的数据长度4字节 先接受4个字节使用struct转换成数字来获取要接收的数据长度
再发送数据 再按照长度接收数据
  
 
  服务端
 1 import socket
 2 import subprocess
 3 import struct
 4 
 5 server = socket.socket()
 6 ip_port = ('192.168.15.113',8001)
 7 server.bind(ip_port)
 8 server.listen()
 9 conn,addr = server.accept()
10 while 1:
11     #来自客户端的指令
12     print('等待接受信息。。。')
13     from_client_cmd = conn.recv(1024).decode('utf-8')
14     print(from_client_cmd)
15     #通过subprocess模块执行服务端的系统指令,并且拿到指令执行结果
16     sub_obj = subprocess.Popen(
17         from_client_cmd, #客户端的指令
18         shell=True,
19         stdout=subprocess.PIPE, #标准输出:正确指令的执行结果在这里
20         stderr=subprocess.PIPE, #标准错误输出:错误指令的执行结果在这里
21     )
22     #接受到的返回信息是bytes类型的,并且windows系统的默认编码为gbk
23     server_cmd_msg = sub_obj.stdout.read()
24     # server_cmd_err = sub_obj.stderr.read().decode('gbk')
25     #首先计算出你将要发送的数据的长度
26     cmd_msg_len = len(server_cmd_msg)
27     #先对数据长度进行打包,打包成4个字节的数据,目的是为了和你将要发送的数据拼在一起,就好我们自定制了一个消息头
28     msg_len_stru = struct.pack('i',cmd_msg_len)
29     conn.send(msg_len_stru) #首先发送打包成功后的那4个字节的数据
30     conn.sendall(server_cmd_msg) #循环send数据,直到数据全部发送成功
server

 

  客户端

 1 import socket
 2 import struct
 3 client = socket.socket()
 4 server_ip_port = ('192.168.15.113',8001)
 5 client.connect(server_ip_port)
 6 while 1:
 7     msg = input('请输入要执行的指令>>>')
 8     client.send(msg.encode('utf-8'))
 9     #先接收服务端要发送给我的信息的长度,前4个字节,固定的
10     from_server_msglen = client.recv(4)
11     unpack_len_msg = struct.unpack('i',from_server_msglen)[0]
12     #接收数据长度统计,和服务端发给我的数据长度作比较,来确定跳出循环的条件
13     recv_msg_len = 0
14     #统计拼接接收到的数据,注意:这个不是统计长度
15     all_msg = b''
16     while recv_msg_len < unpack_len_msg:
17         every_recv_data = client.recv(1024)
18         #将每次接收的数据进行拼接和统计
19         all_msg += every_recv_data
20         #对每次接收到的数据的长度进行累加
21         recv_msg_len += len(every_recv_data)
22 
23     print(all_msg.decode('gbk'))
client

 

  复杂的服务端(自定义报头)

 1 import socket,struct,json
 2 import subprocess
 3 phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
 4 phone.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
 5 
 6 phone.bind(('127.0.0.1',8080))
 7 phone.listen(5)
 8 
 9 while True:
10     conn,addr=phone.accept()
11     while True:
12         cmd=conn.recv(1024)
13         if not cmd:break
14         print('cmd: %s' %cmd)
15 
16         res=subprocess.Popen(cmd.decode('utf-8'),
17                              shell=True,
18                              stdout=subprocess.PIPE,
19                              stderr=subprocess.PIPE)
20         err=res.stderr.read()
21         print(err)
22         if err:
23             back_msg=err
24         else:
25             back_msg=res.stdout.read()
26 
27         headers={'data_size':len(back_msg)}
28         head_json=json.dumps(headers)
29         head_json_bytes=bytes(head_json,encoding='utf-8')
30 
31         conn.send(struct.pack('i',len(head_json_bytes))) #先发报头的长度
32         conn.send(head_json_bytes) #再发报头
33         conn.sendall(back_msg) #在发真实的内容
34 
35     conn.close()
View Code
 
 1 from socket import *
 2 import struct,json
 3 
 4 ip_port=('127.0.0.1',8080)
 5 client=socket(AF_INET,SOCK_STREAM)
 6 client.connect(ip_port)
 7 
 8 while True:
 9     cmd=input('>>: ')
10     if not cmd:continue
11     client.send(bytes(cmd,encoding='utf-8'))
12 
13     head=client.recv(4)
14     head_json_len=struct.unpack('i',head)[0]
15     head_json=json.loads(client.recv(head_json_len).decode('utf-8'))
16     data_len=head_json['data_size']
17 
18     recv_size=0
19     recv_data=b''
20     while recv_size < data_len:
21         recv_data+=client.recv(1024)
22         recv_size+=len(recv_data)
23 
24     #print(recv_data.decode('utf-8'))
25     print(recv_data.decode('gbk')) #windows默认gbk编码
26 
27 tcp_client.py
View Code

 

  head = {'file name': 'test', 'filesize': 8192, 'filetype': 'txt', 'filepath': r'\user\bin'}
  报头长度       ——>         先接收4字节
  send(head) 报头   ——>        根据这4个字节获取报头
  send(file)    报文  ——>     从报头中获取filesize,然后根据filesize接收文件
  整个流程的大致解释:
  我们可以把报头做成字典,字典里包含将要发送的真实数据的描述信息(大小啊之类的),然后json序列化,然后用struck将序列化后的数据长度打包成4个字节。
  我们在网络上传输的所有数据 都叫做数据包,数据包里的所有数据都叫做报文,报文里面不止有你的数据,还有ip地址、mac地址、端口号等等,其实所有的报文都有报头,这个报头是协议规定的,看一下
 
  发送时:
  先发报头长度
  再编码报头内容然后发送
  最后发真实内容
 
  接收时:
  先手报头长度,用struct取出来
  根据取出的长度收取报头内容,然后解码,反序列化
  从反序列化的结果中取出待取数据的描述信息,然后去取真实的数据内容
阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯