• Python学习第36天(socketserver模块导入)


      今天的内容其实不多,主要是因为后面讲到底层部分涉及了一些Linux知识,我这部分没看,后面的内容就没法听了,所以就先复习一下昨天关于粘包的处理方式的知识,自己重新写了一下这部分内容,然后大致的看了一下socketserver模块如何实现并发的一个过程,明天正式开始Linux的学习,已经到了不得不进行的程度了。

    一、粘包问题的解决

    low版处理:

    服务端:

    import socket
    import subprocess
    ip_port = ('127.0.0.1',8080)
    backlog = 5
    buffer_size = 1024
    
    tcp_server = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    tcp_server.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)   # 用于解决time out占用问题
    tcp_server.bind(ip_port)
    tcp_server.listen(backlog)
    
    while True:
        conn,addr = tcp_server.accept()
        print('客户端地址是:',addr)
        while True:
            msg = conn.recv(buffer_size)
            if not msg:
                break
            res = subprocess.Popen(msg.decode('utf-8'),shell = True,
                                   stdin = subprocess.PIPE,
                                   stderr = subprocess.PIPE,
                                   stdout = subprocess.PIPE)
            err = res.stderr.read()
            if err:    # 表示收到了报错信息的情况下
                ret = err
            else:
                ret =res.stdout.read()
            data_lengeth = len(ret)
            conn.send(str(data_lengeth).encode('utf-8'))
            data = conn.recv(buffer_size).decode('utf-8')   # 客户反馈收到的确认信息
            if data == 'recv_ready':
                conn.sendall(ret)
        conn.close()

    客户端:

    import socket
    ip_port = ('127.0.0.1',8080)
    buffer_size = 1024
    
    tcp_client = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    tcp_client.connect(ip_port)
    
    while True:
        msg = input('>>>').strip()
        if len(msg) == 0:
            continue
        if msg == 'quit':
            break
    
        tcp_client.send(msg.encode('utf-8'))
        length = int(tcp_client.recv(buffer_size).decode('utf-8'))
        tcp_client.send('recv_ready'.encode('utf-8'))
    
        send_size = 0
        data = ''
        while send_size < length:
            data += tcp_client.recv(buffer_size)
            send_size = len(data)
    
        print(data.decode('utf-8'))

    相对于昨天的还是进行了一些优化的,

    然后是利用stuck模块实现的操作

    服务端:

    from socket import *
    import subprocess
    import struct
    ip_port=('127.0.0.1',8080)
    back_log=5
    buffer_size=1024
    
    tcp_server=socket(AF_INET,SOCK_STREAM)
    tcp_server.bind(ip_port)
    tcp_server.listen(back_log)
    
    while True:
        conn,addr=tcp_server.accept()
        print('新的客户端链接',addr)
        while True:
            #
            try:
                cmd=conn.recv(buffer_size)
                if not cmd:break
                print('收到客户端的命令',cmd)
    
                #执行命令,得到命令的运行结果cmd_res
                res=subprocess.Popen(cmd.decode('utf-8'),shell=True,
                                     stderr=subprocess.PIPE,
                                     stdout=subprocess.PIPE,
                                     stdin=subprocess.PIPE)
                err=res.stderr.read()
                if err:
                    cmd_res=err
                else:
                    cmd_res=res.stdout.read()
    
                #
                if not cmd_res:
                    cmd_res='执行成功'.encode('gbk')
    
                length=len(cmd_res)
    
                data_length=struct.pack('i',length)
                conn.send(data_length)
                conn.send(cmd_res)
            except Exception as e:
                print(e)
                break

    客户端:

    from socket import *
    import struct
    from functools import partial
    ip_port=('127.0.0.1',8080)
    back_log=5
    buffer_size=1024
    
    tcp_client=socket(AF_INET,SOCK_STREAM)
    tcp_client.connect(ip_port)
    
    while True:
        cmd=input('>>: ').strip()
        if not cmd:continue
        if cmd == 'quit':break
    
        tcp_client.send(cmd.encode('utf-8'))
    
    
        #解决粘包
        length_data=tcp_client.recv(4)
        length=struct.unpack('i',length_data)[0]
    
        recv_size=0
        recv_data=b''
        while recv_size < length:
            recv_data+=tcp_client.recv(buffer_size)
            recv_size=len(recv_data)
        print('命令的执行结果是 ',recv_data.decode('gbk'))
    tcp_client.close()

    这样看起来就清晰的多了

    然后就要开始今天内容了,上面这些操作很显然存在一个问题,在基于tcp的时候每次只能进行一个连接,那么这样就造成了很不现实的问题,一次只能服务一个客户端,基于此种需求,这个时候就引入了socketserver模块

    先看一下,下面这个图就是大致的服务器socketserver模块的书写形式吧

     下面实际测试的部分:

    服务端

    import socket
    import socketserver
    
    class MyServer(socketserver.BaseRequestHandler):
        def handle(self):
            print('链接信息:',self.request)
            # 相当于之前的conn
            print('地址信息:',self.client_address)
            # 相当于之前的addr
            while True:
                try:
                #收消息
                    data=self.request.recv(1024)
                    if not data:break
                    print('收到客户端的消息是',data,self.client_address)
    
                    #发消息
                    self.request.sendall(data.upper())
    
                except Exception as e:
                    print(e)
                    break
    
    if __name__ == '__main__':
        s=socketserver.ThreadingTCPServer(('127.0.0.1',8080),MyServer) #多线程
        # s=socketserver.ForkingTCPServer(('127.0.0.1',8080),MyServer) #多进程
    
        # self.server_address = server_address
        # self.RequestHandlerClass = RequestHandlerClass
        # print(s.server_address)
        # print(s.RequestHandlerClass)
        # print(MyServer)
        # print(s.socket)
        # print(s.server_address)
        s.serve_forever()

    客户端:

    from socket import *
    ip_port=('127.0.0.1',8080)
    buffer_size=1024
    
    udp_client=socket(AF_INET,SOCK_DGRAM) #数据报
    
    while True:
        msg=input('>>: ').strip()
        udp_client.sendto(msg.encode('utf-8'),ip_port)
    
        data,addr=udp_client.recvfrom(buffer_size)
        # print(data.decode('utf-8'))
        print(data)

    通过socketserver模块的导入,我们在服务端建立了一个新的类MyServer,而每个与其连接的客户端都是由MyServer生成的实例化对象,而收发消息的过程则会通过调用类的handle方法来实现。

    Linux真的不学不行了,明天正式开始Linux的内容更新。

  • 相关阅读:
    【转】C#进阶系列——WebApi 接口参数不再困惑:传参详解
    微信内测小程序,苹果你怎么看?
    给你一个团队,你应该怎么管?
    ios修改产品名
    【原创】windows下搭建vue开发环境+IIS部署
    【原】“系统”重新启动
    Ubuntu root密码修改
    【转】网络编程常见问题总结
    Python + Selenium -Python 3.6 3.7 安装 PyKeyboard PyMouse
    python3 获取当前路径及os.path.dirname sys.path.dirname的使用
  • 原文地址:https://www.cnblogs.com/xiaoyaotx/p/12595984.html
Copyright © 2020-2023  润新知