zoukankan      html  css  js  c++  java
  • 网络通信

    基于TCP网络通信

    服务器端先初始化Socket,然后与端口绑定(bind),对端口进行监听(listen),调用accept阻塞,等待客户端连接。在这时如果有个客户端初始化一个Socket,然后连接服务器(connect),如果连接成功,这时客户端与服务器端的连接就建立了。客户端发送数据请求,服务器端接收请求并处理请求,然后把回应数据发送给客户端,客户端读取数据,最后关闭连接,一次交互结束
    简易版网络通信模拟xshell
    服务端

    # 服务端server
    import subprocess
    import socket
    phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)  # 拿到一个socket对象
    
    # 调用sock.setsockopt设置这个socket选项,本例中把socket.SO_REUSEADDR设置为1,表示服务器端进程终止后,
    # 操作系统会为它绑定的端口保留一段时间,以防其他进程在它结束后抢占这个端口。
    phone.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
    
    phone.bind(("127.0.0.1", 8080)) # 绑定唯一一个软件的地址
    
    # 调用sock.listen通知系统开始侦听来自客户端的连接,参数是在队列中的最大连接数。
    phone.listen(5)
    print("starting...")
    while True:           # 服务器循环
        conn,addr=phone.accept() # 卡在这里直到,客户端返回一个元组,里面包含了socket对象以及客户端的地址
        # print("客户端对象", conn)
        print("客户端的ip地址", addr)
        while True:       # 通信循环
            try:
                data = conn.recv(1024)
                print("客户端发送的消息是", data)
                # conn.send(data.upper())
                res = subprocess.Popen(data.decode("gbk"), shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
                data1 = res.stderr.read()
                data2 = res.stdout.read()
                conn.send(data1)
                conn.send(data2)
            except Exception:
                break
        conn.close()    #  关闭客户端套接字对象
    phone.close()       # 关闭服务端套接字对象
    

    客户端

    # 客户端Client
    import socket
    phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM)  # 创建客户端套接字对象
    phone.connect(("127.0.0.1", 8080))     # 尝试连接服务器
    while True:                           # 通讯循环,没有监听循环
        msg = input(">>:").strip()
        if not msg: continue
        phone.send(msg.encode("utf-8"))
        data2 = phone.recv(1024)     # 指定从缓存中取数据的最大字节
        print(data2.decode("gbk"))
    phone.close()     # 关闭客户端套接字对象
    

    基于UDP网络通信

    UDP服务端

    # UDP通信之服务端
    import socket
    udpserver = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    # udpserver.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
    udpserver.bind(("127.0.0.1",8080))
    
    while True:   # 通信循环
        data, client_addr = udpserver.recvfrom(1024)
        print(data.decode("utf-8"))    # dada
        print(client_addr)             # ('127.0.0.1', 60167)
        udpserver.sendto(data.upper(), client_addr)
    

    UDP客户端

    # UDP通信之客户端
    import socket
    udpclient = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    
    server_ip_port = ("127.0.0.1", 8080)
    while True:
        inp = input(">>:")
        udpclient.sendto(inp.encode("utf-8"), server_ip_port)
        data,server_addr = udpclient.recvfrom(1024)
        print(data.decode("utf-8"))
    

    粘包现象

    只有TCP有粘包现象,UDP永远不会粘包
    在上述简易通信模型里,在客户端执行ipconfig,在执行cls。发现并未一次取完ipconfig的执行结果,执行cls时,仍打印ipconfig的结果,那是因为ipconfig执行结果大于1024字节,而客户端收数据时,只收取1024个字节。切客户段服务端都是从操作系统的缓存中拿数据。

    粘包问题主要还是因为接收方不知道消息之间的界限,不知道一次性提取多少字节的数据所造成的
    用json和struct解决方案
    struct模块
    该模块可以把一个类型,如数字,转成固定长度的bytes

    # 把数字转换成四个字节的bytes,数字须小于int最大长度
    struct.pack('i',12345678)   # b'Naxbcx00'
    

    解决粘包
    服务端

    # 服务端(解决粘包问题)
    import subprocess
    import socket
    import struct
    import json
    phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)  # 买手机
    phone.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
    phone.bind(("127.0.0.1", 8080)) # 绑定手机卡
    phone.listen(5)
    print("starting...")
    while True:
        conn,addr=phone.accept() # c端连接成功返回一个元组,里面包含了socket对象以及客户端的地址
        # print("电话线路是", conn)
        print("客户端的手机号是", addr)
        while True:
            try:
                data = conn.recv(1024)
                print("客户端发送的消息是", data)
                # conn.send(data.upper())
                res = subprocess.Popen(data.decode("utf-8"), shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
                data1 = res.stderr.read()  # dos错误的运行结果
                data2 = res.stdout.read()  # dos正确的运行结果
    
                data_size = len(data1)+len(data2)   # 得到原始数据总大小
                print(data_size)
                data_dic = {"size": data_size}   # 为避免粘包,自定制报头
    
                data_json = json.dumps(data_dic)   # 序列号报头
                data_json_bytes = data_json.encode("utf-8")    # 序列化并转成bytes,用于传输
                print(data_json)
                data_len = struct.pack("i", len(data_json_bytes)) # 打包得到报头长度
                # part1: 先发送报头的长度
                conn.send(data_len)
                # part2: 发送报头
                conn.send(data_json_bytes)
                # part3: 发送原始数据
                conn.send(data1)
                conn.send(data2)
            except Exception:
                break
        conn.close()
    phone.close()
    

    客户端

    # 客户端(解决粘包问题)
    import socket
    import struct
    import json
    phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    phone.connect(("127.0.0.1", 8080))
    while True:
        msg = input(">>:").strip()
        if not msg: continue
        phone.send(msg.encode("utf-8"))
        head_dic_len = phone.recv(4)          # part1: 接受报头的长度
        head_dic_size = struct.unpack("i", head_dic_len)[0]  # 取出报头长度
    
    
        head_json = phone.recv(head_dic_size)   # part2: 接受报头
        head_dic = json.loads(head_json.decode("utf-8"))  # 反序列化拿到报头数据
        data_size = head_dic["size"]   # 拿出原始数据大小
    
    
        recv_size = 0
        recv_data = b""
        while data_size > recv_size:
            data = phone.recv(1024)   # part3: 接受原始数据
            recv_size += len(data)
            recv_data += data
    
        print(recv_data.decode("gbk"))
    phone.close()
    

    socketserver实现并发

    TCP服务端

    # 基于socketserver并发TCP通信
    # 服务端
    import  socketserver
    class FtpServer(socketserver.BaseRequestHandler):
        def handle(self):
            # TCP下的request就是conn,客户端套接字
            print(self.request)   #  return self.socket.accept()
            while True:
                data=self.request.recv(1024)
                print(data.decode("utf-8"))
                self.request.send(data.upper())
    
    
    if __name__ == '__main__':
        obj = socketserver.ThreadingTCPServer(("127.0.0.1",8080),FtpServer)
        print(obj.server_address)
        # print(obj.RequestHandlerClass)
        # print(obj.socket)
        obj.serve_forever()  # 链接循环
    

    TCP客户端

    # 于socketserver并发TCP通信
    # 客户端
    import  socket
    server_addr = ("127.0.0.1",8080)
    phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    phone.connect(server_addr)
    while True:  # 通讯循环
        inp = input(">>:")
        phone.send(inp.encode("utf-8"))
        data = phone.recv(1024)
        print(data)
    

    并发UDP服务端

    # UDP通信并发
    # 服务端
    import socketserver
    class UdpServer(socketserver.BaseRequestHandler):
        def handle(self):
            # UDP协议下的request=(rec_data, self.socket)
            # data, client_addr = self.socket.recvfrom(self.max_packet_size)
            # return (data, self.socket), client_addr
            print(self.request[0])   # (data, self.socket)
            self.request[1].sendto(self.request[0].upper(), self.client_address)
    
    
    if __name__ == '__main__':
        server_ip_port = ("127.0.0.1", 8080)
        obj = socketserver.ThreadingUDPServer(server_ip_port, UdpServer)
        print(obj.socket)
        obj.serve_forever()
    

    并发UDP客户端

    # UDP通信并发
    # 客户端
    import socket
    udpclient = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    
    server_ip_port = ("127.0.0.1", 8080)
    while True:
        inp = input(">>:")
        udpclient.sendto(inp.encode("utf-8"), server_ip_port)
        data,server_addr = udpclient.recvfrom(1024)
        print(data.decode("utf-8"))
    

    作业

    简易FTP实现
    服务端

    # ftp上传和下载(面向对象)
    # 服务端
    import socket
    import json
    import os
    import struct
    class FtpServer:
        address_family = socket.AF_INET
        socket_type = socket.SOCK_STREAM
        allow_reuse_address = False
        max_packet_size = 8192
        coding = "utf-8"
        request_queue_size = 5
        server_dir = r"C:\Users\Zou\PycharmProjects\py_fullstack_s4\day37\粘包"
    
        def __init__(self, server_address, bind_and_activate=True):
            """构造函数"""
            self.server_address = server_address
            self.phone = socket.socket(self.address_family,self.socket_type)
    
            if bind_and_activate:
                try:
                    self.server_bind()
                    self.server_activate()
                except:
                    self.server_close()
                    raise
    
        def server_bind(self):
            """绑定socket对象"""
            if self.allow_reuse_address:
                self.phone.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR, 1)
            self.phone.bind(self.server_address) # 绑定地址和端口号
            self.server_address = self.phone.getsockname()
    
    
        def server_activate(self):
            """服务端对象listen"""
            self.phone.listen(self.request_queue_size)
    
    
        def server_close(self):
            """关闭服务端socket对象"""
            self.phone.close()
    
    
        def get_request(self):
            """服务端开始监听accept"""
            return self.phone.accept()
    
    
        def close_request(self, request):
            """关闭客户socket对象"""
            request.close()
    
    
        def run(self):
            while True:   # 连接循环
                self.conn, self.client_addr = self.get_request()
                print("客户端地址:", self.client_addr)
                while True:   # 通信循环
                    try:
                        head_struct = self.conn.recv(4)
                        print(head_struct)
                        if not head_struct:break
    
                        head_len = struct.unpack("i",head_struct)[0] # 拿到报头长度
                        head_json = self.conn.recv(head_len).decode(self.coding) # 拿到json字符串
                        head_dic = json.loads(head_json)
                        print(head_dic)  # 打印用户字典
                        # head_dic = {"cmd":"put", "file_name":"a.txt", "file_size":12345}
                        cmd = head_dic["cmd"]
                        if hasattr(self, cmd):
                            func = getattr(self,cmd)
                            func(head_dic)
                    except Exception:
                        break
    
        def put(self, dic):
            """文件上传函数"""
            file_path = os.path.normpath(os.path.join(
                self.server_dir,
                dic["file_name"]
            ))
            file_size = dic["file_size"]
            recv_size = 0
            print("-------",file_path)
            with open(file_path,"wb") as f:
                while recv_size < file_size:
                    recv_data = self.conn.recv(self.max_packet_size)
                    f.write(recv_data)
                    recv_size += len(recv_data)
                    print("recvsize:%s filesize:%s"% (recv_size, file_size))
    
    
    tcpserver1 = FtpServer(("127.0.0.1",8080))
    tcpserver1.run()
    

    客户端

    # FTP客户端(面向对象)
    import socket
    import struct
    import json
    import os
    
    class FtpClient:
        address_family = socket.AF_INET
        socket_type = socket.SOCK_STREAM
        allow_reuse_address = False
        max_packet_size = 8192
        coding = "utf-8"
        request_queue_size = 5
    
    
        def __init__(self, server_address, connect=True):
            self.server_address = server_address
            self.phone = socket.socket(self.address_family, self.socket_type)
    
            if connect:
                try:
                    self.client_connect()
                except Exception:
                    self.client_close()
    
    
        def client_connect(self):
            self.phone.connect(self.server_address)
    
    
        def client_close(self):
            self.phone.close()
    
    
        def run(self):
            """客户端主逻辑"""
            while True:
                inp = input(">>:").strip()   # put a.txt
                if not inp:continue
                l = inp.split()
                cmd = l[0]
                if hasattr(self, cmd):
                    func = getattr(self, cmd)
                    func(l)
    
    
        def put(self,l):
            cmd = l[0]
            filename = l[1]
            if not os.path.isfile(filename):
                print("%s文件不存在" % filename)
                return
    
            filesize = os.path.getsize(filename)
            # head_dic = {"cmd":"put", "file_name":"a.txt", "file_size":12345}
            head_dict = {"cmd":cmd, "file_name":filename, "file_size":filesize}
            head_json = json.dumps(head_dict).encode(self.coding)
            head_len = struct.pack("i", len(head_json))
            self.phone.send(head_len)
            self.phone.send(head_json)
            send_size = 0
            with open(filename, "rb") as f:
                for line in f:
                    self.phone.send(line)
                    send_size += len(line)
                    print(send_size)
                else:
                    print("文件上传成功")
    
    
    client = FtpClient(("127.0.0.1",8080))
    client.run()
    
  • 相关阅读:
    Date类型转换成LocalDateTime 类型
    连接mysql数据库执行写入语句
    排序的的值为非数字时的处理方法
    git所遇到的问题
    visual studio快捷键
    Win10编译chromium
    下载chromium CIPD client失败的解决办法
    Linux内核源代码情景分析
    【赵强老师】史上最详细的PostgreSQL体系架构介绍
    3.Consul 安装配置(Proxysql+mgr)
  • 原文地址:https://www.cnblogs.com/zouruncheng/p/6814167.html
Copyright © 2011-2022 走看看