Python网络编程之黏包问题

时间:2022-12-15 17:05:03

 

二、解决黏包问题

2.1 解决黏包方法1

  • 计算消息实体的大小
  • 服务端接受两次,一次时消息大小,二次是消息实体,解决消息实体黏包
  • 客户端发送两次,一次是消息大小,一次是消息实体
  • 在两次收发之间加入一次多余通信,以防止消息大小和消息实体黏包

 

server端

import  socket

sk = socket.socket()

sk.bind(('127.0.0.1',9000))
sk.listen()

conn,addr = sk.accept()
print(conn,addr)
while True:
    cmd = input('请输入你的命令: ')
    if cmd == 'q':
        break

    conn.send(cmd.encode('gbk'))
    data_length = conn.recv(1024).decode('gbk')
    conn.send(b'ok')  # 接受到消息大小后马上send,这样可以隔开两次recv,并且第二次接受指定长度的消息
    data = conn.recv(int(data_length)).decode('gbk')
    print(data)

conn.close()
sk.close()

 

client

import  socket
import subprocess
sk = socket.socket()

sk.connect(('127.0.0.1',9000))

while True:
    cmd = sk.recv(1024).decode('gbk')
    ret = subprocess.Popen(cmd,shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
    std_out = ret.stdout.read()
    std_err = ret.stderr.read()

    data_length = str(len(std_out)+len(std_err)).encode('gbk')
    sk.send(data_length)
    sk.recv(1024)  # send消息大小后马上转入recv,隔开两次send。
    sk.send(std_out)
    sk.send(std_err)

sk.close()

 

2.2  借助于struct模块

 

server端

import socket
import struct

sk = socket.socket()
sk.bind(('127.0.0.1',8080))
sk.listen()


conn,addr = sk.accept()


while True:
    cmd = input('请输入命令: ')
    conn.send(cmd.encode('utf-8'))
    data_length_struct = conn.recv(4)
    data_length = struct.unpack('i',data_length_struct)[0]
    res = conn.recv(data_length).decode('gbk')
    print(res)

conn.close()
sk.close()

 

client端

import  socket
import subprocess
import  struct

sk = socket.socket()
sk.connect(('127.0.0.1',8080))

while True:
    cmd = sk.recv(1024).decode('utf-8')
    res = subprocess.Popen(cmd,shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
    stdout = res.stdout.read()
    std_err = res.stderr.read()
    data_length = len(stdout) + len(std_err)
    data_length_struct = struct.pack('i',data_length)
    sk.send(data_length_struct)
    sk.send(stdout)
    sk.send(std_err)

sk.close()

 

2.3 通过struct定制报头传输大文件

 

server端

 

import socket
import struct
import json

sk = socket.socket()
sk.bind(('127.0.0.1',8090))
sk.listen()
buffer = 2048

conn,addr = sk.accept()

# 先接受报头
# 根据报头接受消息实体

struct_length = conn.recv(4) # 接受struct长度的包
bytes_head_length = struct.unpack('i',struct_length)[0] # 计算报头的长度
bytes_head = conn.recv(bytes_head_length) # 接受报头
json_head = bytes_head.decode('utf-8') # 将报头转换为str
head = json.loads(json_head) # 将报头转换为字典
fileSize = head['fileSize']

with open(head['fileName'],'wb') as f:
    while fileSize:
        if fileSize >= buffer:
            content = conn.recv(buffer)
            f.write(content)
            fileSize -= buffer
        else:
            content = conn.recv(fileSize)
            f.write(content)
            break


conn.close()
sk.close()

 

client端

import  socket
import os
import json
import struct

sk = socket.socket()
sk.connect(('127.0.0.1',8090))
buffer = 2048
# 定制报头
# 先发送报头大小
# 发送报头
# 发送消息实体
head = {'fileSize':None,
        'fileName':r'a.tar.gz',
        'filePath':r'C:\Users\王诚\Desktop'}
file = os.path.join(head['filePath'],head['fileName'])
fileSize = os.path.getsize(file)
head['fileSize'] = fileSize
json_head = json.dumps(head) # 字典转换成了字符串
bytes_head = json_head.encode('utf-8') # 将字符串的head转换为bytes类型
bytes_head_length = len(bytes_head) #  计算bytes类型的head的大小

struct_length = struct.pack('i',bytes_head_length) # 通过struct转换为固定长度的bytes
sk.send(struct_length) # 先发报头的长度
sk.send(bytes_head) # 发报头
with open(file,'rb') as f:
    while fileSize:
        if fileSize >= buffer:
            content= f.read(buffer)
            sk.send(content)
            fileSize -= buffer
        else:
            content = f.read(fileSize)
            sk.send(content)
            break


sk.close()

 

三、检查客户端的合法性

使用hmac

server端

import  socket
import os
import hmac

secret_key = b'wangys'

sk = socket.socket()
sk.bind(('127.0.0.1',8080))
sk.listen()

def check_conn(conn):
    msg = os.urandom(32)
    conn.send(msg)
    h = hmac.new(secret_key,msg)
    server_msg = h.digest()
    client_msg = conn.recv(1024)
    res = hmac.compare_digest(server_msg,client_msg)
    return res

conn,addr = sk.accept()
res = check_conn(conn)
if res:
    print('合法的客户端')
    conn.close()
else:
    print('不合法的客户端')
    conn.close()

sk.close()

 

client端

 

import socket
import hmac
secret_key = b'wangys'

sk = socket.socket()
sk.connect(('127.0.0.1',8080))

msg = sk.recv(1024)
h = hmac.new(secret_key,msg)
client_msg = h.digest()
sk.send(client_msg)

sk.close()