python socket套接字编程

内容概要

  • socket套接字编程
  • 通信循环
  • 代码健壮性校验
  • 链接循环
  • TCP黏包现象(流式协议)
  • 报头制作、struct模块、封装形式
  • UDP代码编写

内容详细

socket套接字编程

        我们自己想写一款可以数据交互的程序
        # 只要涉及到远程数据交互必须要操作OSI七层 所以有现成的模块直接实现

        socket模块

        架构启动肯定是先启动服务端再启动客户端

简易代码

        import socket
        """
        导入模块的两种方式
            import句式
            from...import...句式
        第三方模块下载
            pip3 install 模块名==版本号 -i 仓库地址
        """
        server = socket.socket()  # 默认就是基于网络的TCP传输协议   买手机
        server.bind(('127.0.0.1', 8080))  # 绑定ip和port         插电话卡
        server.listen(5)  # 半连接池            开机(过渡)
        sock, address = server.accept()  # 监听   三次握手的listen态
        print(address)  # 客户端地址
        data = sock.recv(1024)  # 接收客户端发送的消息  听别人说话
        print(data)
        sock.send(b'hello my big baby~~~')  # 给别人回话
        sock.close()  # 挂电话
        server.close()  # 关机


        import socket
        client = socket.socket()  # 买手机
        client.connect(('127.0.0.1', 8080))  # 拨号
        # 说话
        client.send(b'hello big DSB DSB DSB!')
        # 听他说
        data = client.recv(1024)
        print(data)
        client.close()

通信循环及代码优化

        1.客户端校验消息不能为空
        2.服务端添加兼容性代码(mac linux)
        3.服务端重启频繁报端口占用错误
            from socket import SOL_SOCKET, SO_REUSEADDR
            server.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)  # 在bind前加
        4.客户端异常关闭服务端报错的问题
            异常捕获
        5.服务端链接循环
        6.半连接池
            设置可以等待的客户端数量

黏包现象

        数据管道的数据没有被完全取出

        TCP协议有一个特性
            """
            当数据量比较小 且时间间隔比较短的多次数据
            那么TCP会自动打包成一个数据包发送
            """

        报头
            能够标识即将到来的数据具体信息
                eg:数据量多大 
            # 报头的长度必须是固定的

struct模块

        import struct
        import json


        d = {
            'file_name': '很好看.mv',
            'file_size': 1231283912839123123424234234234234234324324912,
            'file_desc': '拍摄的很有心 真的很好看!!!',
            'file_desc2': '拍摄的很有心 真的很好看!!!'
        }
        d = json.dumps(d)
        res = struct.pack('i',len(d))
        print(len(res))
        res1 = struct.unpack('i',res)[0]
        print(res1)

简易版本报头

        import socket
        import subprocess
        import json
        import struct

        server = socket.socket()
        server.bind(('127.0.0.1', 8080))
        server.listen(5)

        while True:
            sock, address = server.accept()
            while True:
                data = sock.recv(1024)  # 接收cmd命令
                command_cmd = data.decode('utf8')
                sub = subprocess.Popen(command_cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
                res = sub.stdout.read() + sub.stderr.read()  # 结果可能很大
                # 1.制作报头
                data_first = struct.pack('i', len(res))
                # 2.发送报头
                sock.send(data_first)
                # 3.发送真实数据
                sock.send(res)

        import socket
        import struct

        client = socket.socket()  # 买手机
        client.connect(('127.0.0.1', 8080))  # 拨号

        while True:
            msg = input('请输入cmd命令>>>:').strip()
            if len(msg) == 0:
                continue
            client.send(msg.encode('utf8'))
            # 1.先接收固定长度为4的报头数据
            recv_first = client.recv(4)
            # 2.解析报头
            real_length = struct.unpack('i',recv_first)[0]
            # 3.接收真实数据
            real_data = client.recv(real_length)
            print(real_data.decode('gbk'))

上传文件数据

        import json
        import socket
        import struct
        import os

        client = socket.socket()  # 买手机
        client.connect(('127.0.0.1', 8080))  # 拨号

        while True:
            data_path = r'D:\金牌班级相关资料\网络并发day01\视频'
            # print(os.listdir(data_path))  # [文件名称1 文件名称2 ]
            movie_name_list = os.listdir(data_path)
            for i, j in enumerate(movie_name_list, 1):
                print(i, j)
            choice = input('请选择您想要上传的电影编号>>>:').strip()
            if choice.isdigit():
                choice = int(choice)
                if choice in range(1, len(movie_name_list) + 1):
                    # 获取文件名称
                    movie_name = movie_name_list[choice - 1]
                    # 拼接文件绝对路径
                    movie_path = os.path.join(data_path, movie_name)
                    # 1.定义一个字典数据
                    data_dict = {
                        'file_name': 'XXX老师合集.mp4',
                        'desc': '这是非常重要的数据',
                        'size': os.path.getsize(movie_path),
                        'info': '下午挺困的,可以提神醒脑'
                    }
                    data_json = json.dumps(data_dict)
                    # 2.制作字典报头
                    data_first = struct.pack('i', len(data_json))
                    # 3.发送字典报头
                    client.send(data_first)
                    # 4.发送字典
                    client.send(data_json.encode('utf8'))
                    # 5.发送真实数据
                    with open(movie_path,'rb') as f:
                        for line in f:
                            client.send(line)


        # 1.先接收固定长度为4的字典报头数据
                recv_first = sock.recv(4)
                # 2.解析字典报头
                dict_length = struct.unpack('i', recv_first)[0]
                # 3.接收字典数据
                real_data = sock.recv(dict_length)
                # 4.解析字典(json格式的bytes数据 loads方法会自动先解码 后反序列化)
                real_dict = json.loads(real_data)
                # 5.获取字典中的各项数据
                data_length = real_dict.get('size')
                file_name = real_dict.get("file_name")

                recv_size = 0
                with open(file_name,'wb') as f:
                    while recv_size < data_length:
                        data = sock.recv(1024)
                        recv_size += len(data)
                        f.write(data)

UDP代码编写

        import socket
        udp_sk = socket.socket(type=socket.SOCK_DGRAM)  # UDP协议
        udp_sk.bind(('127.0.0.1',9000))  # 绑定地址
        msg,addr = udp_sk.recvfrom(1024)
        udp_sk.sendto(b'hi',addr)             
        udp_sk.close() 


        import socket
        ip_port=('127.0.0.1',9000)
        udp_sk=socket.socket(type=socket.SOCK_DGRAM)
        udp_sk.sendto(b'hello',ip_port)
        back_msg,addr=udp_sk.recvfrom(1024)
        print(back_msg.decode('utf-8'),addr)

扩展知识

        在阅读源码的时候
            1.变量名后面跟冒号 表示的意思是该变量名需要指代的数据类型
            2.函数后更横杆加大于号表示的意思是该函数的返回值类型
posted @ 2022-01-13 17:18  风花雪月*  阅读(54)  评论(0编辑  收藏  举报