解决socket粘包的两种low版模式 os.popen()和struct模块

无注释版

os.popen()模式

server端

import socket
import os

phone = socket.socket()
phone.bind(("localhost",8088))
phone.listen()

while 1:
    conn,addr = phone.accept()
    while 1:
        try:
            from_client_data = conn.recv(1024)
            if from_client_data.upper() == b'Q':
                print("客户端退出")
                break
            print(from_client_data.decode('utf-8'))
            cmd_res = os.popen(from_client_data.decode("utf-8")).read()
            if len(cmd_res) == 0:
                cmd_res = "cmd hs no output..."
            print(len(cmd_res.encode('gbk')))
            conn.send(str(len(cmd_res.encode('gbk'))).encode('gbk'))
            client.ack = conn.recv(1024)
            conn.send(cmd_res.encode('gbk'))
        except ConnectionResetError:
            break
    conn.close()
phone.close()

client端

import socket
phone = socket.socket()
phone.connect(('localhost',8088))
while 1:
    client_data = input(">>>")
    if not client_data:
        print("发送的内容不能为空")
        continue
    phone.send(client_data.encode("utf-8"))
    if client_data.upper() == "Q":
        quit()
    else:
        server_data = phone.recv(1024)
        phone.send("准备好接收了".encode('utf-8'))
        total_size = int(server_data.decode('gbk'))
        total_data = b''
        while 1:
            if len(total_data) < total_size:
                total_data += phone.recv(1024)
                print(len(total_data))
            else:
                break
        print(total_data.decode('gbk'))
phone.close()

struct模块解决粘包

server端

import socket
import subprocess
import struct

phone = socket.socket()
phone.bind(("localhost",8088))
phone.listen(5)

while 1:
    conn,addr = phone.accept()
    print(addr)
    while 1:
        try:
            from_client_data = conn.recv(1024)
            if from_client_data.upper() == b'Q':
                print("客户端退出")
                break
            print(from_client_data.decode('utf-8'))
            obj = subprocess.Popen(from_client_data.decode('utf-8'),
                                   shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE,)
            ret = obj.stdout.read()+obj.stderr.read()
            total_size = len(ret)
            header = struct.pack('i',total_size)
            print(total_size)
            conn.send(header)
            conn.send(ret)
        except ConnectionResetError:
            break
    conn.close()
phone.close()

client端

import socket
import struct
phone = socket.socket()
phone.connect(('localhost',8088))
while 1:
    client_data = input(">>>")
    if not client_data:
        print("发送的内容不能为空")
        continue
    phone.send(client_data.encode("utf-8"))
    if client_data.upper() == "Q":
        quit()
    else:
        server_data = phone.recv(4)
        total_size = struct.unpack('i',server_data)[0]
        total_data = b''
        while 1:
            if len(total_data) < total_size:
                total_data += phone.recv(1024)
                print(len(total_data))
            else:
                break
        print(total_data.decode('gbk'))
        # print(int(server_data.decode('gbk')))
phone.close()

有注释版

os.popen()模式

server端

import socket
import os

phone = socket.socket() 

# 实例化一个socket对象

phone.bind(("localhost",8088)) 

# 绑定地址(host,port)到套接字,在AF_INET下,以元组(host,port)的形式表示地址

phone.listen(5) 

#  	开始TCP监听。backlog指定在拒绝连接之前,操作系统可以挂起的最大连接数量。该值至少为1,大部分应用程序设为5就可以了。

while 1: # 开启循环,多个用户同时连接(当然不是并发,排队连接)
    conn,addr = phone.accept()
    
    # 被动接受TCP客户端连接,(阻塞式)等待连接的到来,会有两个值,conn我也不明白是什么值,addr是客户端连接的地址
    
    while 1: # 客户端与服务端多次对话,客户端多次执行循环
        try: # 异常处理,客户端直接退出ConnectionResetError错误
            from_client_data = conn.recv(1024) # 接收客户端数据
            if from_client_data.upper() == b'Q': 
                # 判断客户端输入是否是q或Q,如果是q,直接退出当前连接
                print("客户端退出")
                break
            print(from_client_data.decode('utf-8')) # 输出客户端转码后的数据
            cmd_res = os.popen(from_client_data.decode("utf-8")).read() # 执行os命令,通过popen执行cmd命令
            if len(cmd_res) == 0: # 如果没有该命令,返回下面结果
                cmd_res = "cmd hs no output..."
            conn.send(str(len(cmd_res.encode('gbk'))).encode('gbk')) # 发送数据,首先计算cmd_res的gbk字节长度,然后转换成字符串发送给客户端,因为int型不能发送
            print(len(cmd_res.encode('gbk'))) # 输出该命令返回的长度
            conn.send(cmd_res.encode('gbk')) # 执行一个接收,截断上下两个的粘包
            conn.send(cmd_res.encode('gbk')) # 发送给客户端
        except ConnectionResetError:
            break
    conn.close() # 关闭连接
phone.close() # 关闭连接

client端

import socket
phone = socket.socket() # 实例化对象
phone.connect(('localhost',8088)) # 主动初始化TCP服务器连接,。一般address的格式为元组(hostname,port),如果连接出错,返回socket.error错误。
while 1: # 循环输入
    client_data = input(">>>")
    if not client_data: # 如果输入为空,not client_data 则为True
        print("发送的内容不能为空")
        continue
    phone.send(client_data.encode("utf-8")) # 给服务端发送信息
    if client_data.upper() == "Q": # q退出
        quit()
    else:
        server_data = phone.recv(1024) # 接收返回的字节长度
        phone.send("准备好接收了".encode('utf-8')) # 给服务端发送消息,阻塞服务端上下造成粘包的问题(low版)
        total_size = int(server_data.decode('gbk')) # 接收到的是字符串,转换成整型
        total_data = b'' # 刚开始设置为0,空字符串则为0,前面加b表示字节
        while 1: # 如果我收到的字节小于total_size,则一直循环
            if len(total_data) < total_size:
                total_data += phone.recv(1024) # 每次把信息加到total_data中
                print(len(total_data))
            else:
                break
        print(total_data.decode('gbk')) # 输出返回的结果
phone.close() # 关闭连接

struct模块解决粘包

server端

import socket
import subprocess
import struct

phone = socket.socket() # 实例化一个socket对象
phone.bind(("localhost",8088))
# 绑定地址(host,port)到套接字,在AF_INET下,以元组(host,port)的形式表示地址
phone.listen(5)
#  	开始TCP监听。backlog指定在拒绝连接之前,操作系统可以挂起的最大连接数量。该值至少为1,大部分应用程序设为5就可以了。

while 1:# 开启循环,多个用户同时连接(当然不是并发,排队连接)
    
    conn,addr = phone.accept()
    
    # 被动接受TCP客户端连接,(阻塞式)等待连接的到来,会有两个值,conn我也不明白是什么值,addr是客户端连接的地址
    print(addr)
    while 1: # 客户端与服务端多次对话,客户端多次执行循环
        try: # 异常处理,客户端直接退出ConnectionResetError错误
            from_client_data = conn.recv(1024) # 接收客户端数据
            if from_client_data.upper() == b'Q': # 判断客户端输入是否是q或Q,如果是q,直接退出当前连接
                print("客户端退出")
                break
            print(from_client_data.decode('utf-8'))  # 输出客户端转码后的数据
            obj = subprocess.Popen(from_client_data.decode('utf-8'),
                                   shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE,)
            # subprocess 模块中的代码
            # shell: 命令解释器,相当于调用cmd 执行指定的命令。
		   # stdout:正确结果丢到管道中。
		   # stderr:错了丢到另一个管道中。
		   # windows操作系统的默认编码是gbk编码。
            ret = obj.stdout.read()+obj.stderr.read() # 加的是字节
            # 正确的输出跟错误的输出加起来,因为正确输出时,错误输出为空,所以加起来相当于0+1,没什么区别
            total_size = len(ret) # 计算返回命令的长度
            header = struct.pack('i',total_size) # 将一个数字转化成等长度的bytes类型。
            print(total_size) # 打印出返回命令的长度
            conn.send(header) # 发送固定长度的报头
            conn.send(ret) # 给客户端发送返回命令的结果
        except ConnectionResetError: 
            break
    conn.close() # 关闭连接
phone.close() # 关闭连接

client端

import socket 
import struct
phone = socket.socket() # 实例化对象
phone.connect(('localhost',8088)) # 主动初始化TCP服务器连接,。一般address的格式为元组(hostname,port),如果连接出错,返回socket.error错误。
while 1: # 循环输入
    client_data = input(">>>")
    if not client_data: # 如果输入为空,not client_data 则为True
        print("发送的内容不能为空")
        continue
    phone.send(client_data.encode("utf-8")) # 给服务端发送信息
    if client_data.upper() == "Q": # q退出
        quit()
    else:
        server_data = phone.recv(4) # 接收报头长度
        total_size = struct.unpack('i',server_data)[0] # 反解报头
        total_data = b''  # 刚开始设置为0,空字符串则为0,前面加b表示字节
        while 1: # 如果我收到的字节小于total_size,则一直循环
            if len(total_data) < total_size: 
                total_data += phone.recv(1024)  # 每次把信息加到total_data中
                print(len(total_data))
            else:
                break
        print(total_data.decode('gbk')) # 输出返回的结果,windows默认gbk
        # print(int(server_data.decode('gbk')))
phone.close() # 关闭连接
原文地址:https://www.cnblogs.com/alex3174/p/11360954.html