• 24、解决粘包问题的方法、UDP套接字简单示例、多并发问题


    一、解决粘包问题的方法(简单版)

    • 服务端
    from socket import *
    from subprocess import PIPE, Popen
    import struct
    
    server = socket(AF_INET, SOCK_STREAM)
    server.bind(('127.0.0.1', 8000))
    server.listen(5)
    
    while True:
        conn, client_addr = server.accept()
        print(client_addr)
    
        while True:  # 通信循环
            try:
                cmd = conn.recv(8096)
                if len(cmd) == 0:  # 针对于linux系统
                    break
                obj = Popen(cmd.decode('utf-8'),
                            shell=True,
                            stderr=PIPE,
                            stdout=PIPE, )
                res1 = obj.stdout.read()
                res2 = obj.stderr.read()
                total_size = len(res1) + len(res2)
    
                # 先把数据的长度给发过去,长度为4
                header = struct.pack('i', total_size)
                conn.send(header)
    
                # 再发送真正的数据
                conn.send(res2)  # 利用TCP协议的特性
                conn.send(res1)  # nagle算法规定,TCP协议会将数据量较小、
                # 时间间隔短的数据合并为一条发送给客户端
            except Exception:
                break
        conn.close()  # 异常断开后回收资源
    
    • 客户端
    from socket import *
    from subprocess import PIPE, Popen
    import struct
    
    client = socket(AF_INET, SOCK_STREAM)
    client.connect(('127.0.0.1', 8000))
    
    while True:
        cmd = input('>>>:').strip()
        if len(cmd) == 0:
            continue
        client.send(cmd.encode('utf-8'))
    
        # 先接收数据的长度
        header = client.recv(4)
        total_size = struct.unpack('i', header)[0]  # 提取出字节长度
    
        # 接收真正的数据
        recv_size = 0
        res = b''
        while recv_size < total_size:
            data = client.recv(1024)
            recv_size += len(data)
            res += data
        print(res.decode('gbk'))
    

    二、解决粘包问题的方法(优化版)

    • 服务端
    import json
    from socket import *
    import struct
    from subprocess import PIPE, Popen
    
    server = socket(AF_INET, SOCK_STREAM)
    server.bind(('127.0.0.1', 8000))
    server.listen(5)
    
    while True:  # 链接循环
        conn, client_addr = server.accept()
        print(client_addr)
    
        while True:  # 通信循环
            try:
                cmd = conn.recv(8096)
                if len(cmd) == 0:
                    break
                obj = Popen(cmd.decode('utf-8'),
                            shell=True,
                            stdout=PIPE,
                            stderr=PIPE)
                res1 = obj.stdout.read()
                res2 = obj.stderr.read()
    
                header_dic = {
                    'filename': 'a.txt',
                    'total_size': len(res1) + len(res2),
                    'md5': 'qwe165qwqwe65456qw5'
                }
                header_json = json.dumps(header_dic)
                header_bytes = header_json.encode('utf-8')
    
                # 先发四个字节
                conn.send(struct.pack('i', len(header_bytes)))
                # 再发报头字典
                conn.send(header_bytes)
    
                # 最后发真正的数据
                conn.send(res1)
                conn.send(res2)
            except Exception:
                break
        conn.close()  # 关闭窗口来回收资源
    
    
    • 客户端
    from socket import *
    import struct
    import json
    
    client = socket(AF_INET, SOCK_STREAM)
    client.connect(('127.0.0.1', 8000))
    
    while True:
        cmd = input('>>>: ').strip()
        if len(cmd) == 0:
            continue
        client.send(cmd.encode('utf-8'))
    
        # 先接收四个字节,提取header_bytes的长度
        header_bytes_len = struct.unpack('i', client.recv(4)[0])
        # 再收header_bytes,提取header_dic
        header_bytes = client.recv(header_bytes_len)
        header_json = header_bytes.decode('utf-8')
        header_dic = json.loads(header_json)
        print(header_json)
        total_size = header_dic['total_size']
    
        # 再接收真正的数据
        recv_size = 0
        res = b''
        while recv_size < total_size:
            data = client.recv(1024)
            recv_size += len(data)
            res += data
    
        print(res.decode('gbk'))
    
    

    三、基于UDP套接字的编写

    • 服务端
    import socket
    
    server = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)  # 造手机
    server.bind(('127.0.0.1', 8000))  # 需要绑定,不许要监听
    while True:  # 通信循环
        data, client_addr = server.recvfrom(1024)
        # 解压赋值(文本,发送端的端口号)
        print(data)
        server.sendto(data.upper(), client_addr)
    
    • 客户端
    from socket import *
    
    client = socket(AF_INET, SOCK_DGRAM)  # 造手机
    while True:  # 输入循环
        msg = input('输入:').strip()
        client.sendto(msg.encode('utf8'), ('127.0.0.1', 8000))
        # sendto(发送内容二进制,接收端口)
        data, client_addr = client.recvfrom(1024)
        # 解压赋值(接收的文本,端口)
        print(data.decode('utf-8'))
    
    • UDP协议一般不会用于大数据的传输
    • UDP套接字虽然没有粘包的问题,但是不能代替TCP套接字,因为UDP协议有一个缺陷,如果数据发送的途中,数据丢失,则数据就丢失了,而TCP协议则不会有这种缺陷,因此一般的UDP套接字用户无关紧要的数据发送,例如QQ、微信聊天等.

    四、socketserver模块

    • 基本使用框架
    import socketserver
    
    
    class MyHandler(socketserver.BaseRequestHandler):
        # 通信循环
        def handle(self):  # 调用的话以上不变
            # data = self.request.recv(1024)  # self.recv(1024)
            # print(data)
            # self.request.send(data.upper())
    
            if __name__ == '__main__':
                server = socketserver.ThreadingTCPServer(('127.0.0.1', 8080), MyHandler, bind_and_activate=True)
                server.serve_forever()
    
    
    • 多并发——服务端
    # 同一时刻有多个人在接听
    import socketserver
    import json
    from subprocess import Popen, PIPE
    import struct
    
    
    class MyHandler(socketserver.BaseRequestHandler):
        # 通信循环
        def handle(self):  # 调用的话以上不变
            # data = self.request.recv(1024)  # self.recv(1024)
            # print(data)
            # self.request.send(data.upper())
            try:
                cmd = self.request.recv(8096)
                obj = Popen(cmd.decode('utf-8'),
                            shell=True,
                            stdout=PIPE,
                            stderr=PIPE,
                            )
    
                res1 = obj.stdout.read()
                res2 = obj.stderr.read()
    
                header_dic = {
                    'filename': "a.txt",
                    'total_size': len(res1) + len(res2),
                    'md5': '123dfsfsaf123213'
                }
    
                header_json = json.dumps(header_dic)
                header_bytes = header_json.encode('utf-8')
    
                # 先发4个字节
                self.request.send(struct.pack('i', len(header_bytes)))
                # 再发报头字典
                self.request.send(header_bytes)
    
                # 最后发送真正的数据
                self.request.send(res1)
                self.request.send(res2)
            except Exception:
                self.request.close()
    
    
    # 使用socketserver的连接循环(并发),但是使用了自己的通信循环
    
    if __name__ == '__main__':
        server = socketserver.ThreadingTCPServer(('127.0.0.1', 8080), MyHandler, bind_and_activate=True)
        server.serve_forever()
    
    # 调用的话只需要将conn改成self.request即可
    
    
    • 多并发——客户端1
    from socket import *
    import struct
    import json
    
    client = socket(AF_INET, SOCK_STREAM)
    client.connect(('127.0.0.1', 8080))
    
    while True:
        cmd = input(">>>: ").strip()
        if len(cmd) == 0:
            continue
        client.send(cmd.encode('utf-8'))
    
        # 先收4个字节,提取header_bytes的长度
        header_bytes_len = struct.unpack('i', client.recv(4))[0]
    
    
        # 再收header_bytes,提取header_dic
        header_bytes = client.recv(header_bytes_len)
        header_json = header_bytes.decode('utf-8')
        header_dic = json.loads(header_json)
    
        print(header_dic)
    
        total_size = header_dic['total_size']
    
        # 再接收真正的数据
        recv_size = 0
        res = b''
        while recv_size < total_size:
            data = client.recv(1024)
            recv_size += len(data)
            res += data
    
        print(res.decode('gbk'))
    
    • 多并发——客户端2
    from socket import *
    import struct
    import json
    
    client = socket(AF_INET, SOCK_STREAM)
    client.connect(('127.0.0.1', 8080))
    
    while True:
        cmd = input(">>>: ").strip()
        if len(cmd) == 0:
            continue
        client.send(cmd.encode('utf-8'))
    
        # 先收4个字节,提取header_bytes的长度
        header_bytes_len = struct.unpack('i', client.recv(4))[0]
    
    
        # 再收header_bytes,提取header_dic
        header_bytes = client.recv(header_bytes_len)
        header_json = header_bytes.decode('utf-8')
        header_dic = json.loads(header_json)
    
        print(header_dic)
    
        total_size = header_dic['total_size']
    
        # 再接收真正的数据
        recv_size = 0
        res = b''
        while recv_size < total_size:
            data = client.recv(1024)
            recv_size += len(data)
            res += data
    
        print(res.decode('gbk'))
    
    
  • 相关阅读:
    设计模式-抽象工厂
    设计模式-工厂方法
    设计模式-简单工厂
    设计模式-单例模式
    设计模式使用指南
    适合Java程序员看的UML学习手册
    第六周 Java8新特性
    deepin15.11系统使用罗技k380键盘
    动态规划系列之六01背包问题
    《比勤奋更重要的是底层思维》
  • 原文地址:https://www.cnblogs.com/zhaokunhao/p/14298301.html
Copyright © 2020-2023  润新知