zoukankan      html  css  js  c++  java
  • Python进阶-网络编程之TCP粘包

    一、什么是TCP粘包

      C/S架构下,接收方不知道每个消息的发送间隙、也不知道每次应该提取多少个字节的数据,与此同时,TCP是面向连接的,面向流的,收发两端都要有,因此发送端为了将多个发往接收端的数据包更高效的发给对方,使用了优化方法(Nagle算法),将多次间隔较小且数据量小的数据,合并成一个发送给接收端。此时接收端无法分辨出来,必须提供合理的拆包机制,即面向流的通信是无消息保护边界的。
      除此之外,因为TCP是基于流的,所以收发的消息不能为空,需要发送、接收端添加空消息处理机制,防止程序卡住。

    二、处理思路

       粘包现象主要是因为发送端没有确切的发送间隔以及发送数据包的大小,而接收端更不知道发送算发送了多少个数据包以及大小,只能来多少接收多少。
       解决方法:
        自定义报头,发送端发送数据之前,先将自定义的报头(数据包大小等信息)发送给接收端,接收端明确每个数据包的大小进行依次接收。

    三、实现方式

    方法一:

     1 # 客户端
     2 import socket
     3 import struct
     4 
     5 IP = '127.0.0.1'
     6 PORT = 8080
     7 bufsize = 1024
     8 
     9 client_socket = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    10 client_socket.connect((IP,PORT))
    11 
    12 while True:
    13     cmd = input('cmd>>>').strip()
    14     if len(cmd) == 0:continue
    15     elif cmd == 'q':break
    16 
    17     client_socket.send(cmd.encode('utf-8'))
    18 
    19     # 1.接收固定报头
    20     header = client_socket.recv(4)
    21 
    22     # 2.解析报头
    23     total_size = struct.unpack('i',header)[0]
    24     print(total_size)
    25 
    26     # 3.根据包头接收真实数据
    27     recv_size = 0
    28     # 保存接收的数据(接收到的是byte类型)
    29     res_data = b''
    30     while recv_size < total_size:
    31         recv_data = client_socket.recv(1024)
    32         res_data += recv_data
    33         recv_size += len(recv_data)
    34 
    35     print(recv_data.decode('gbk'))
    36 
    37 client_socket.close()
    38 
    39 # 服务端
    40 import socket
    41 import subprocess
    42 import struct
    43 
    44 IP = '127.0.0.1'
    45 PORT = 8080
    46 bufsize = 1024
    47 
    48 tcp_socket = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    49 tcp_socket.bind((IP,PORT),)
    50 tcp_socket.listen(5)
    51 
    52 while True:
    53     conn,addr = tcp_socket.accept()
    54     print('客户端:',addr)
    55 
    56     while True:
    57         try:
    58             cmd = conn.recv(bufsize)
    59             res = subprocess.Popen(cmd.decode('utf-8'),shell=True
    60                                    ,stdin=subprocess.PIPE
    61                                    ,stdout=subprocess.PIPE
    62                                    ,stderr=subprocess.PIPE)
    63             stderr = res.stderr.read()
    64             stdout = res.stdout.read()
    65 
    66 
    67             # 1.制作固定长度的报头
    68             total_size = len(stdout) + len(stderr)
    69             header = struct.pack('i',total_size)
    70 
    71             # 2.发送报头
    72             conn.send(header)
    73 
    74             # 3.发送真实数据
    75             conn.send(stderr)
    76             conn.send(stdout)
    77         except ConnectionResetError:
    78             break
    79     conn.close()
    80 tcp_socket.close()
    解决粘包问题

    方法二:

     1 # 客户端
     2 import socket
     3 import struct
     4 import json
     5 
     6 IP = '127.0.0.1'
     7 PORT = 8080
     8 bufsize = 1024
     9 
    10 client_socket = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    11 client_socket.connect((IP,PORT))
    12 
    13 while True:
    14     cmd = input('cmd>>>').strip()
    15     if len(cmd) == 0:continue
    16     elif cmd == 'q':break
    17 
    18     client_socket.send(cmd.encode('utf-8'))
    19 
    20     # 1.接收包头长度
    21     header_size = struct.unpack('i',client_socket.recv(4))[0]
    22 
    23     # 2.接收报头
    24     header_bytes = client_socket.recv(header_size)
    25 
    26     # 3.解析报头
    27     header_json = header_bytes.decode('utf-8')
    28     header_dic = json.loads(header_json)
    29     # print(header_dic)
    30 
    31     total_size = header_dic['total_size']
    32 
    33     # 3.根据包头接收真实数据
    34     recv_size = 0
    35     # 保存接收的数据(接收到的是byte类型)
    36     res_data = b''
    37     while recv_size < total_size:
    38         recv_data = client_socket.recv(1024)
    39         res_data += recv_data
    40         recv_size += len(recv_data)
    41 
    42     print(recv_data.decode('gbk'))
    43 
    44 client_socket.close()
    45 
    46 
    47 
    48 # 服务端
    49 import socket
    50 import subprocess
    51 import struct
    52 import json
    53 
    54 IP = '127.0.0.1'
    55 PORT = 8080
    56 bufsize = 1024
    57 
    58 tcp_socket = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    59 tcp_socket.bind((IP,PORT),)
    60 tcp_socket.listen(5)
    61 
    62 while True:
    63     conn,addr = tcp_socket.accept()
    64     print('客户端:',addr)
    65 
    66     while True:
    67         try:
    68             cmd = conn.recv(bufsize)
    69             res = subprocess.Popen(cmd.decode('utf-8'),shell=True
    70                                    ,stdin=subprocess.PIPE
    71                                    ,stdout=subprocess.PIPE
    72                                    ,stderr=subprocess.PIPE)
    73             stderr = res.stderr.read()
    74             stdout = res.stdout.read()
    75 
    76             # 1.制作固定长度的报头
    77             header_dic = {
    78                 'total_size':len(stdout) + len(stderr),
    79                 'md5':'123sssss222',
    80                 'filename':'120.txt'}
    81 
    82             header_json = json.dumps(header_dic)
    83             header_bytes = header_json.encode('utf-8')
    84 
    85             # 2.发送报头的长度
    86             total_size = len(header_bytes)
    87             conn.send(struct.pack('i',total_size))
    88 
    89             # 发送报头
    90             conn.send(header_bytes)
    91 
    92             # 3.发送真实数据
    93             conn.send(stderr)
    94             conn.send(stdout)
    95         except ConnectionResetError:
    96             break
    97     conn.close()
    98 tcp_socket.close()
    解决粘包问题
  • 相关阅读:
    java之day4补充
    java之day4
    JAVA之day3对象
    JAVA之DAY3
    JAVA之DAY2
    element-ui表格添加复选框及根据列表中的数据判断是否可选
    h5手机端上传多张图片(界面上的展示图片,删除图片)
    模态框-开启关闭事件
    Vue-粒子特效(vue-particles)
    网页常用代码片段-sessionStorage存储JSON
  • 原文地址:https://www.cnblogs.com/mdzzbojo/p/8982746.html
Copyright © 2011-2022 走看看