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()
    
  • 相关阅读:
    UVA 11488 Hyper Prefix Sets (字典树)
    UVALive 3295 Counting Triangles
    POJ 2752 Seek the Name, Seek the Fame (KMP)
    UVA 11584 Partitioning by Palindromes (字符串区间dp)
    UVA 11100 The Trip, 2007 (贪心)
    JXNU暑期选拔赛
    计蒜客---N的-2进制表示
    计蒜客---线段的总长
    计蒜客---最大质因数
    JustOj 2009: P1016 (dp)
  • 原文地址:https://www.cnblogs.com/zouruncheng/p/6814167.html
Copyright © 2011-2022 走看看