Python32 Socket Serv

发布时间:2019-09-21 11:05:47编辑:auto阅读(1467)

    类型

    • socketserver有几种类型:

      class socketserver.TCPServer:用于TCP
      class socketserver.UDPServer:用于UDP
      class socketserver.UnixStreamServer:用于Unix的TCP
      class socketserver.UnixDatagramServer:用于Unix的UDP

    image_1c5nj6qe0gc61p8p17phkk3sgm9.png-83.3kB
    TCPServer继承了BaseServer
    UnixStreamServer继承了TCPServer

    • 创建socket server至少分以下几步:

      1、必须创建一个请求处理类,并且这个类要继承BaseRequestHandler,还要重写父类里的handle方法(跟客户端所有的交互都是在handle中写的)。
      2、必须要实例化一个协议server(如TCPServer),并且传递server ip和你上面创建的请求处理类,给这个TCPServer(实例化的时候将IP和请求处理类传给TCPServer)。
      3、然后可以根据上面的实例来处理请求:
      server.handle_request() #只处理一个请求
      server.handle_forever() #处理多个请求,永远执行(一般都是用这个)
      4、调用server_close()去关闭

    server端:
    
    import socketserver
    
    class MyTCPHandler(socketserver.BaseRequestHandler):
        '''第1步:创建处理类,继承Base。 客户端每一次请求过来都会实例化这个类'''
    
        def handle(self):
            '''第1步:重写handle方法,handle默认存在父类中代码是空的。 客户端所有交互都在handle中完成'''
    
            while True: #使其可以循环发送数据
                self.data = self.request.recv(1024).decode().strip()
                #这里的self.request.recv相当于之前用的conn.recv
    
                print ("{} wrote:".format(self.client_address[0]))
                #打印客户端的IP地址信息
    
                print (self.data)
                #打印数据信息
    
                self.request.send(self.data.upper().encode('utf-8'))
                #传回数据给客户端,只是upper了一下
                #sendall就是重复调用send
    
    if __name__ == "__main__":
        HOST,PORT = "localhost",9999
    
        server = socketserver.TCPServer((HOST,PORT),MyTCPHandler)
        #第2步:实例化TCPServer,并将IP和MyTCPHandler当做参数传给请求处理类
        #监听客户端的每一个请求,就会实例化MyTCPHandler这个类,拿MyTCPHandler的handle与客户端交互。
    
        server.serve_forever()
        #第3步:允许永远处理多个请求
    
    client端:
    
    import socket
    
    client = socket.socket()
    client.connect(('localhost',9999))
    
    while True:
    
        msg = input(">>:").strip()
        if len(msg) == 0:continue
        client.send(msg.encode('utf-8'))
        data = client.recv(1024)
        print ("recv:",data.decode())
    
    client.close()
    
    client执行结果:
    >>:abc
    recv: ABC
    >>:efg
    recv: EFG
    >>:hhh
    recv: HHH
    >>:
    
    server执行结果:
    127.0.0.1 wrote:
    abc
    127.0.0.1 wrote:
    efg
    127.0.0.1 wrote:
    hhh
    
    client断开后server报错:
    ----------------------------------------
    Exception happened during processing of request from ('127.0.0.1', 53933)
    Traceback (most recent call last):
      File "D:\python37\lib\socketserver.py", line 313, in _handle_request_noblock
        self.process_request(request, client_address)
      File "D:\python37\lib\socketserver.py", line 344, in process_request
        self.finish_request(request, client_address)
      File "D:\python37\lib\socketserver.py", line 357, in finish_request
        self.RequestHandlerClass(request, client_address, self)
      File "D:\python37\lib\socketserver.py", line 712, in __init__
        self.handle()
      File "E:/python/代码练习/A2.py", line 11, in handle
        self.data = self.request.recv(1024).decode().strip()
    ConnectionResetError: [WinError 10054] 远程主机强迫关闭了一个现有的连接。
    ----------------------------------------
    #可以看到这里报错ConnectionResetError: [WinError 10054]
    
    修改server端:
    import socketserver
    
    class MyTCPHandler(socketserver.BaseRequestHandler):
    
        def handle(self):
    
            while True: #使其可以循环发送数据
               try: #使用try的话就不需要使用 if not data的方式来判断客户端断开时,无数据的情况了
                    self.data = self.request.recv(1024).decode().strip()
                    #这里的self.request.recv相当于之前用的conn.recv
    
                    print ("{} wrote:".format(self.client_address[0]))
    
                    print (self.data)
    
                    self.request.send(self.data.upper().encode('utf-8'))
               except ConnectionResetError as e:
                   print ("err:",e)
                   break    #这里一定要break,不然就会一直死循环
    
    if __name__ == "__main__":
        HOST,PORT = "localhost",9999
    
        server = socketserver.TCPServer((HOST,PORT),MyTCPHandler)
    
        server.serve_forever()
    
    server执行结果:
    127.0.0.1 wrote:
    abc
    127.0.0.1 wrote:
    123
    err: [WinError 10054] 远程主机强迫关闭了一个现有的连接。
    #客户端断开连接后,就通过断言的方式抓到错误了。
    

    上面的代码目前还不能支持多并发,如果有多个并发,后面的并发就会被挂起; 如果要并发的话,需要修改一下代码。

    image_1c5pi0sn51denhou1t7b14k91tlcm.png-5.3kB
    通过ctrl点TCPServer
    image_1c5pi2lgi1gj1qgo18hn8a01th013.png-4.3kB
    可以看到TCPServer是继承了BaseServer
    image_1c5pi3dgaqh51lvc1co817q5vn71g.png-3.8kB

    修改server端:
    
    import socketserver
    
    class MyTCPHandler(socketserver.BaseRequestHandler):
    
        def handle(self):
    
            while True: #使其可以循环发送数据
               try: #使用try的话就不需要使用 if not data的方式来判断客户端断开时,无数据的情况了
                    self.data = self.request.recv(1024).decode().strip()
    
                    print ("{} wrote:".format(self.client_address[0]))
    
                    print (self.data)
    
                    self.request.send(self.data.upper().encode('utf-8'))
    
               except ConnectionResetError as e:
                   print ("err:",e)
                   break    
    if __name__ == "__main__":
        HOST,PORT = "localhost",9999
    
        server = socketserver.ThreadingTCPServer((HOST,PORT),MyTCPHandler)
        # 上面的代码需要使用ThreadingTCPServer
    
        server.serve_forever()
    
    client 1执行结果:
    >>:test 1
    recv: TEST 1
    >>:
    
    client 2执行结果:
    >>:test 2
    recv: TEST 2
    >>:
    
    client 3执行结果:
    >>:test 3
    recv: TEST 3
    >>:
    
    server 执行结果:
    127.0.0.1 wrote:
    test 1
    127.0.0.1 wrote:
    test 2
    127.0.0.1 wrote:
    test 3
    

    image_1c5phqhfeomh1fbhptttajvl29.png-16.4kB
    可以看到server端现在支持多并发,没有被挂起;每来一个请求会开启一个新线程与server交互;每个线程都是独立的,10个线程,就可以做10件事情。

    通过ctrl点ThreadingTCPServer
    image_1c5piagcm85rqmh56f1a2l161v1t.png-12.3kB
    可以看到将TCPServer这个类传了进去
    同时还传了ThreadingMixIn;TCPServer是负责与客户端交互,而多线程都是由ThreadingMixIn实现的。

    ctrl点ThreadingMixIn
    image_1c5pigho4pp5tcd6adtt1btr2a.png-30.6kB
    这部分就是多线程的主要代码

关键字