Python全栈之路-Day37

1 粘包

1.1 粘包现象

server:


#!/usr/bin/env python
# __Author__: "wanyongzhen"
# Date: 2017/5/3

import socket
# 须知:只有TCP有粘包现象,UDP永远不会粘包
# 所谓粘包问题主要还是因为接收方不知道消息之间的界限,不知道一次性提取多少字节的数据所造成的。
# 此外,发送方引起的粘包是由TCP协议本身造成的,TCP为提高传输效率,发送方往往要收集到足够多的数据后才发送一个TCP段。
# 若连续几次需要send的数据都很少,通常TCP会根据优化算法把这些数据合成一个TCP段后一次发送出去,这样接收方就收到了粘包数据。
phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
phone.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) # 端口重用
phone.bind(('127.0.0.1',8080))
phone.listen(5)
conn,addr = phone.accept()

# 粘包现象1
# data1 = conn.recv(1024)
# data2 = conn.recv(1024)
# print(data1) # 收到粘包数据 b'helloworldSB'
# print(data2) # 收到空数据 b''

# 粘包现象2(基于客户端的:解决粘包现象1)
data1 = conn.recv(2)
data2 = conn.recv(1024)
print(data1) # 收到两个字节数据 b'he'
print(data2) # 收到粘包数据 b'lloworld' 没收到b'SB'


client:


#!/usr/bin/env python
# __Author__: "wanyongzhen"
# Date: 2017/5/3

import socket
phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)   # 买手机
phone.connect(('127.0.0.1',8080))   # 直接打电话,发起链接
# 以下两条消息被合并成一条数据发送到服务器端
phone.send('helloworld'.encode('utf-8'))

# 解决粘包现象1
# import time
# time.sleep(3) # 通过time.sleep()简单解决粘包问题
phone.send('SB'.encode('utf-8'))


1.2 解决粘包

server:


#!/usr/bin/env python
# __Author__: "wanyongzhen"
# Date: 2017/5/3

import socket  # 导入socket模块
import subprocess
# 类似生活中的打电话
phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM) # 买手机
phone.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) # 端口重用
phone.bind(('127.0.0.1',8080))   # 绑定手机卡 (绑定IP 端口)
phone.listen(5)                  # 开机  (TCP的半链接池或暂存池大小)

# while True:  # 链接循环
#     conn,addr = phone.accept()       # 等待电话链接
#     print('client conn is ',conn)    # 打印电话线路
#     print('client addr is ',addr)    # 打印客户端地址
#     while True:   # 通信循环
#         try:  # 应对Windows系统
#             cmd = conn.recv(1024)           # 收消息 (从缓存中收取消息)(每次收取的消息大小为1024字节)
#             if not cmd: break  # 应对Linux系统
#             res = subprocess.Popen(cmd.decode('utf-8'),
#                              shell=True,
#                              stderr=subprocess.PIPE,
#                              stdout=subprocess.PIPE)
#
#             conn.send(res.stderr.read())     # 回消息
#             conn.send(res.stdout.read())     # 回消息
#         except Exception:
#             break
#     conn.close()                     # 挂电话
# phone.close()                    # 关机

# # 自定义报头解决粘包
# import struct
# while True:  # 链接循环
#     conn,addr = phone.accept()       # 等待电话链接
#     print('client conn is ',conn)    # 打印电话线路
#     print('client addr is ',addr)    # 打印客户端地址
#     while True:   # 通信循环
#         try:  # 应对Windows系统
#             cmd = conn.recv(1024)           # 收消息 (从缓存中收取消息)(每次收取的消息大小为1024字节)
#             if not cmd: break  # 应对Linux系统
#             res = subprocess.Popen(cmd.decode('utf-8'),
#                              shell=True,
#                              stderr=subprocess.PIPE,
#                              stdout=subprocess.PIPE)
#             out_res = res.stdout.read()
#             err_res = res.stderr.read()
#             data_size = len(out_res) + len(err_res)
#             # 发送报头
#             conn.send(struct.pack('i',data_size))
#             # 发送数据
#             conn.send(out_res)     # 回消息
#             conn.send(err_res)     # 回消息
#         except Exception:
#             break
#     conn.close()                     # 挂电话
# phone.close()                    # 关机

# 自定义json报头解决粘包
import struct
import json
while True:  # 链接循环
   conn,addr = phone.accept()       # 等待电话链接
   print('client conn is ',conn)    # 打印电话线路
   print('client addr is ',addr)    # 打印客户端地址
   while True:   # 通信循环
       try:  # 应对Windows系统
           cmd = conn.recv(1024)           # 收消息 (从缓存中收取消息)(每次收取的消息大小为1024字节)
           if not cmd: break  # 应对Linux系统
           res = subprocess.Popen(cmd.decode('utf-8'),
                            shell=True,
                            stderr=subprocess.PIPE,
                            stdout=subprocess.PIPE)
           out_res = res.stdout.read()
           err_res = res.stderr.read()
           data_size = len(out_res) + len(err_res)
           header_dict = {'length':data_size,'filename':None}
           header_json = json.dumps(header_dict)
           header_bytes = header_json.encode('utf-8')
           header_len = len(header_bytes)
           # 发送json header长度
           conn.send(struct.pack('i',header_len))
           # 发送json header
           conn.send(header_bytes)
           # 发送数据
           conn.send(out_res)     # 回消息
           conn.send(err_res)     # 回消息
       except Exception:
           break
   conn.close()                     # 挂电话
phone.close()                    # 关机


client:


#!/usr/bin/env python
# __Author__: "wanyongzhen"
# Date: 2017/5/3

import socket
phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)   # 买手机
phone.connect(('127.0.0.1',8080))   # 直接打电话,发起链接

# while True:   # 通信循环
#     cmd = input('>>: ').strip()         # 输入消息
#     if not cmd: continue
#     phone.send(cmd.encode('utf-8'))     # 发送消息(发送到缓存中,并由操作系统发送到服务端缓存中)
#     # 粘包问题:
#     # 当命令输出内容很大时(超过1024字节),客户端不能一次性接受完成
#     # 就会出现这种情况:在下次接受时会收到上次服务器端发送的命令结果
#     # 这就叫粘包
#     data = phone.recv(1024)             # 接收消息
#     print(data.decode('utf-8'))                # 打印消息
# phone.close()                       # 关机


# # 自定义报头解决粘包
# import struct
# while True:   # 通信循环
#     cmd = input('>>: ').strip()         # 输入消息
#     if not cmd: continue
#     phone.send(cmd.encode('utf-8'))     # 发送消息(发送到缓存中,并由操作系统发送到服务端缓存中)
#     # 接收报头
#     header = phone.recv(4)             # 接收消息
#     data_size = struct.unpack('i',header)[0]
#     # 接收数据
#     recv_size = 0
#     recv_data = b''
#     while recv_size < data_size:
#         data = phone.recv(1024)
#         recv_size += len(data)
#         recv_data += data
#     print(recv_data.decode('utf-8'))                # 打印消息
# phone.close()                       # 关机


# 自定义json报头解决粘包
import struct
import json
while True:   # 通信循环
    cmd = input('>>: ').strip()         # 输入消息
    if not cmd: continue
    phone.send(cmd.encode('utf-8'))     # 发送消息(发送到缓存中,并由操作系统发送到服务端缓存中)
    # 接收json header长度
    header_len_json = phone.recv(4)             # 接收消息
    json_len = struct.unpack('i',header_len_json)[0]
    # 接收json header数据
    header_json = phone.recv(json_len)
    header_json = header_json.decode('utf-8')
    header_dict = json.loads(header_json)
    data_size = header_dict['length']
    # 接收数据
    recv_size = 0
    recv_data = b''
    while recv_size < data_size:
        data = phone.recv(1024)
        recv_size += len(data)
        recv_data += data
    print(recv_data.decode('utf-8'))                # 打印消息
phone.close()                  # 关机


原文地址:https://www.cnblogs.com/wanyuetian/p/6821836.html