zoukankan      html  css  js  c++  java
  • Python3 网络基础基础2

    subprocess

    • 可以通过代码执行操作系统的终端命令, 并返回终端执行命令后的结果
    import subprocess
    
    cmd = input('请输入指令; ').strip()
    
    # stdout 返回执行成功结果  stderr 返回保错结果
    obj = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    
    res = obj.stdout.read() + obj.stderr.read()
    
    print(res.decode('gbk'))
    

    粘包问题

    问题原因

    • 无法预测需要接收数据的长度, 服务端前一次发送的数据, 客户端无法精确一次性接受完毕, 下一次发送的数据与上一次未发送的数据粘在一起
    • TCP协议: TCP协议是一个流式协议, 会将多次连续发生的数据量小的, 时间间隔短的数据打包一次性发送

    解决问题

    • struct模块: 将数据长度压缩成固定长度的一个标记(数据报头)发生给对方, 告诉对方即将发送的数据的真实长度
    • 先定义报头, 发送报头, 再发送真实数据
    # server.py
    import socket
    import subprocess
    import struct
    
    server = socket.socket()
    
    server.bind(
        ('127.0.0.1', 8888)
    )
    
    server.listen(5)
    
    while True:
        conn, addr = server.accept()
        print(addr)
    
        while True:
            try:
                # 接收指令
                cmd = conn.recv(1024).decode('utf-8')
    
                obj = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    
                # 返回终端结果
                res = obj.stdout.read() + obj.stderr.read()
    
                # 将result长度压缩, 得到报头
                headers = struct.pack('i', len(res))
    
                # 将报头发送给客户端
                conn.send(headers)
    
                # 将结果发送给客户端
                conn.send(res)
    
            except Exception as e:
                print(e)
                break
    
        conn.close()
    
    # client.py
    import socket
    import struct
    
    client = socket.socket()
    
    client.connect(
        ('127.0.0.1', 8888)
    )
    
    while True:
        cmd = input('请输入指令: ').strip()
    
        if cmd == 'q':
            break
    
        # 向服务端发送指令
        client.send(cmd.encode('utf-8'))
    
        # 接收报头
        headers = client.recv(4)
    
        # 解压报头(得到元祖), 获取数据长度
        res_len = struct.unpack('i', headers)[0]
    
        # 按数据长度接收服务端返回结果
        res = client.recv(res_len)
    
        print(res.decode('gbk'))
    
    client.close()
    

    上传大文件

    • 先发送一个报头, 包含字典长度
    • 再发送该字典, 包含文件名和文件长度信息
    • 最后发送该文件
    # server.py
    import socket
    import struct
    import json
    
    server = socket.socket()
    
    server.bind(
        ('127.0.0.1', 8888)
    )
    
    server.listen(5)
    
    while True:
        conn, addr = server.accept()
    
        try:
            # 接收字典报头
            headers = conn.recv(4)
    
            # 解压获取字典长度
            dict_len = struct.unpack('i', headers)[0]
    
            # 接收字典
            bytes_dict = conn.recv(dict_len)
            json_dict = bytes_dict.decode('utf-8')
            file_info_dict = json.loads(json_dict)
    
            print(file_info_dict)
    
            # 获取文件名, 文件长度
            file_name = file_info_dict.get('file_name')
            file_len = file_info_dict.get('file_len')
    
            init_len = 0
            count = 1
            with open(file_name, 'wb') as f:
    
                while init_len < file_len:
                    # 接收视频文件数据
                    file_data = conn.recv(1024)
                    # 写入视频文件
                    f.write(file_data)
                    print(f'第{count}次接收')
    
                    init_len += len(file_data)
                    count += 1
    
                print(f'{file_name}接收完毕!')
    
        except Exception as e:
            print(e)
            break
    
    conn.close()
    
    # client.py
    import socket
    import struct
    import json
    
    client = socket.socket()
    
    client.connect(
        ('127.0.0.1', 8888)
    )
    
    # 读取视频文件数据
    with open(r'C:UsersBlackDesktopclient_video.mp4', 'rb') as f:
        video_data = f.read()
    
        # 视频文件信息字典
        file_info_dict = {
            'file_name': 'client_video.mp4',
            'file_len': len(video_data)
        }
    
    # 将字典序列化以便获取其长度
    json_dict = json.dumps(file_info_dict)
    bytes_dict = json_dict.encode('utf-8')
    
    # 字典长度报头
    headers = struct.pack('i', len(bytes_dict))
    
    # 发送字典报头
    client.send(headers)
    
    # 发送字典
    client.send(bytes_dict)
    
    with open(r'C:UsersBlackDesktopclient_video.mp4', 'rb') as f:
        init_len = 0
        count = 1
        while init_len < len(video_data):
            # 一次读取1024字节
            file_data = f.read(1024)
            # 分多次发送文件数据
            client.send(file_data)
            print(f'第{count}次发送')
    
            init_len += len(file_data)
            count += 1
    
        print('文件发送完毕!')
    
    client.close()
    

    UDP协议

    • User Datagram Protocol 用户数据报协议

    • 不需要建立双向通道

    • 不会粘包

    • 客户端给服务端发送数据, 不需要等待服务端返回接收成功

    • 面向数据包的不可靠传输层通信协议

    # server.py
    import socket
    
    # 默认为type=socket.SOCK_STREAM, 既TCP协议
    server = socket.socket(type=socket.SOCK_DGRAM)
    
    server.bind(
        ('127.0.0.1', 8888)
    )
    
    # 不需要半连接池和accept建立连接, 直接接收
    msg, addr = server.recvfrom(1024)
    
    print(msg, addr)
    
    # client.py
    import socket
    
    # 默认为type=socket.SOCK_STREAM, 既TCP协议
    client = socket.socket(type=socket.SOCK_DGRAM)
    
    server_ip_port = ('127.0.0.1', 8888)
    
    # 直接向指定IP和PORT发送即可
    client.sendto(b'hello', server_ip_port)
    
    • QQ聊天室
    # server.py
    import socket
    
    server = socket.socket(type=socket.SOCK_DGRAM)
    
    server.bind(
        ('127.0.0.1', 8888)
    )
    
    
    while True:
        recv_msg1, addr1 = server.recvfrom(1024)
        # recv_msg2, addr2 = server.recvfrom(1024)
        # recv_msg3, addr3 = server.recvfrom(1024)
    
        print(recv_msg1.decode('utf-8'))
        # print(recv_msg2.decode('utf-8'))
        # print(recv_msg3.decode('utf-8'))
    
        send_msg = input('输入消息: ')
    
        server.sendto(send_msg.encode('utf-8'), addr1)
        # server.sendto(send_msg.encode('utf-8'), addr2)
        # server.sendto(send_msg.encode('utf-8'), addr3
    
    
    # client1.py
    import socket
    
    client = socket.socket(type=socket.SOCK_DGRAM)
    
    server_ip_port = ('127.0.0.1', 8888)
    
    while True:
        send_msg = input('输入消息:')
    
        client.sendto(send_msg.encode('utf-8'), server_ip_port)
    
        recv_msg, addr = client.recvfrom(1024)
    
        print(recv_msg.decode('utf-8'))
    
    

    SocketServer

    • Python内置模块, 可以简化socket套接字服务端的代码
    • 简化TCP与UDP服务端代码
    • 必须要创建一个类
    # server.py
    import socketserver
    
    
    # 定义类, 继承socketserver.BaseRequestHandler类
    class MyTcpServer(socketserver.BaseRequestHandler):
    
        # 重写父类的handle方法
        def handle(self):
            # 1.接收消息
            data = self.request.recv(1024)  # conn.recv(1024)
            print(data)
    
            # 2.发送消息
            send_msg = input('from server:')
            self.request.send(send_msg.encode('utf-8'))
    
    
    if __name__ == '__main__':
        socketserver.TCPServer.allow_reuse_address = True
        server = socketserver.TCPServer(
            ('127.0.0.1', 8888), MyTcpServer
        )
    
        # 永久执行服务
        server.serve_forever()
    
  • 相关阅读:
    Java泛型
    pyhthon 求GPA平均学分绩点
    机器学习 数据预处理之独热编码(One-Hot Encoding)
    python 取整itertools
    python 输入输出
    jQuery插件开发模式
    shiro 与 web 的结合
    java.lang.NoClassDefFoundError: javax/transaction/UserTransaction
    使用quartz 定时任务
    EasyIcon:免费图标搜索和下载平台
  • 原文地址:https://www.cnblogs.com/bigb/p/11700551.html
Copyright © 2011-2022 走看看