Python之socketserver

import threading
from socketserver import ThreadingTCPServer,BaseRequestHandler
import sys
import logging

FORMAT = '%(asctime)s %(threadName)d %(thread)d %(message)s'
logging.basicConfig(level=logging.INFO,format=FORMAT)

class ChatHandler(BaseRequestHandler): #一对一,相当于receive函数
    clients = {}

    def setup(self):
        self.event = threading.Event()
        self.clients[self.client_address] = self.request
    def finish(self):
        self.clients.pop(self.client_address)
        self.event.set()

    def handle(self):
        print(self.request) #new_socket 用来recv
        while not self.event.is_set():
            data = self.request.recv(1024)
            print(data,self.client_address)
            msg = '{}.ack'.format(data).encode()
            #如何实现一对多,多在哪里,如何获得
            for c in self.clients.values():
                c.send(msg)
addr = ('0.0.0.0',9999)
server = ThreadingTCPServer(addr,ChatHandler)  #相当于tcp总的socket
print(server)
t = threading.Thread(target=server.serve_forever,name='chatserver')
t.start()
try:
    while True:
        cmd = input(">>>")
        if cmd.strip() == 'quit':
            break
except Exception as e:
    print(e)
except KeyboardInterrupt:
    pass
finally:
    print('exit')
    sys.exit(0)

 类的继承关系

BaseServer------>TCPServer---------->UDPServer
                       |                  |
               UnixStreamServer         UnixDatagramServer
socketserver有四个同步类
TCPServer,UDPAServer,UnixStreamServer,UnixDatagramServer
2个mixin类,ForkingMixin,ThreadingMixin类用来支持异步

class ForkingUDPServer(ForkingMixin,UDPServer):pass
class ForkingTCPServer(ForkingMixin,TCPServer):pass
class ThreadingUDPServer(ThreadingMixin,UDPServer):pass
class ThreadingTCPServer(ThreadingMixin,TCPServer):pass
fork创建多进程,thread是创建多线程
BaseRequestHandler类
它是用户连接的用户请求处理类的基类,定义为BaseRequestHandler(request,client_address,server)
服务端server实例接收用户请求后,最后会实例化这个类
它被实例化时,送入3个构建参数,request,client,address,server自身
以后就可以在BaseRequestHandler类的实例上使用一下属性
self.request是和客户端连接的socket对象
self.server是tcpserver本身
self.client_address是客户端的地址


def __init__(self, request, client_address, server):
    self.request = request
    self.client_address = client_address
    self.server = server
    self.setup()
    try:
        self.handle()
    finally:
        self.finish()

def setup(self): 没一个连接初始化
    pass

def handle(self): 每一次请求处理
    pass

def finish(self): 每一个连接清理
    pass  

官方文档例子

import socketserver

class MyHandler(socketserver.BaseRequestHandler):#相当于socket的recv方法
    def handle(self):
        print(self.request) #new_socket
        print(self.client_address)  #('127.0.0.1', 62685)
        print(self.server)  #<socketserver.ThreadingTCPServer object at 0x033CFA50>

server = socketserver.ThreadingTCPServer(('0.0.0.0',9999),MyHandler)
print(server)  #<socketserver.ThreadingTCPServer object at 0x033CFA50>
server.serve_forever()

 实现群聊

import threading
from socketserver import ThreadingTCPServer,BaseRequestHandler
import sys
import logging

FORMAT = '%(asctime)s %(threadName)d %(thread)d %(message)s'
logging.basicConfig(level=logging.INFO,format=FORMAT)

class ChatHandler(BaseRequestHandler): #一对一,相当于receive函数
    clients = {}

    def setup(self):
        self.event = threading.Event()
        self.clients[self.client_address] = self.request
    def finish(self):
        self.clients.pop(self.client_address)
        self.event.set()

    def handle(self):
        print(self.request) #new_socket 用来recv
        while not self.event.is_set():
            data = self.request.recv(1024)
            print(data,self.client_address)
            msg = '{}.ack'.format(data).encode()
            #如何实现一对多,多在哪里,如何获得
            for c in self.clients.values():
                c.send(msg)
addr = ('0.0.0.0',9999)
server = ThreadingTCPServer(addr,ChatHandler)  #相当于tcp总的socket
print(server)
t = threading.Thread(target=server.serve_forever,name='chatserver')
t.start()
try:
    while True:
        cmd = input(">>>")
        if cmd.strip() == 'quit':
            break
except Exception as e:
    print(e)
except KeyboardInterrupt:
    pass
finally:
    print('exit')
    sys.exit(0)
 类的继承关系

BaseServer------>TCPServer---------->UDPServer
                       |                  |
               UnixStreamServer         UnixDatagramServer
socketserver有四个同步类
TCPServer,UDPAServer,UnixStreamServer,UnixDatagramServer
2个mixin类,ForkingMixin,ThreadingMixin类用来支持异步

class ForkingUDPServer(ForkingMixin,UDPServer):pass
class ForkingTCPServer(ForkingMixin,TCPServer):pass
class ThreadingUDPServer(ThreadingMixin,UDPServer):pass
class ThreadingTCPServer(ThreadingMixin,TCPServer):pass
fork创建多进程,thread是创建多线程
BaseRequestHandler类
它是用户连接的用户请求处理类的基类,定义为BaseRequestHandler(request,client_address,server)
服务端server实例接收用户请求后,最后会实例化这个类
它被实例化时,送入3个构建参数,request,client,address,server自身
以后就可以在BaseRequestHandler类的实例上使用一下属性
self.request是和客户端连接的socket对象
self.server是tcpserver本身
self.client_address是客户端的地址


def __init__(self, request, client_address, server):
    self.request = request
    self.client_address = client_address
    self.server = server
    self.setup()
    try:
        self.handle()
    finally:
        self.finish()

def setup(self): 没一个连接初始化
    pass

def handle(self): 每一次请求处理
    pass

def finish(self): 每一个连接清理
    pass  
官方文档例子

import socketserver

class MyHandler(socketserver.BaseRequestHandler):#相当于socket的recv方法
    def handle(self):
        print(self.request) #new_socket
        print(self.client_address)  #('127.0.0.1', 62685)
        print(self.server)  #<socketserver.ThreadingTCPServer object at 0x033CFA50>

server = socketserver.ThreadingTCPServer(('0.0.0.0',9999),MyHandler)
print(server)  #<socketserver.ThreadingTCPServer object at 0x033CFA50>
server.serve_forever()
利用socketserver实现群聊

import threading
import socketserver
from socketserver import ThreadingTCPServer,BaseRequestHandler
import sys
import logging

FORMAT = '%(asctime)s %(threadName)s %(thread)d %(message)s'
logging.basicConfig(level=logging.INFO,format=FORMAT)

class ChatHandler(BaseRequestHandler): #当服务端接收到新的请求后,baserequestHandler新类就会实例化 并一次执行setup ,try handle,finally finish
    clients = {}
    def setup(self):
        self.event = threading.Event()
        self.clients[self.client_address] = self.request

    def finish(self):
        self.clients.pop(self.client_address)
        self.event.set()

    def handle(self):#相当于socket的recv方法,每个不同的连接上请求过来后,生成连接的socket对象
#就是self.request,客户端地址self.client.address
        while not self.event.is_set():
            data = self.request.recv(1024).decode()
            if data =='quit':
                break
            msg = "{} {}".format(self.client_address,data).encode()
            logging.info(msg)
            for c in self.clients.values():
                self.request.send(msg)
        print('END')
addr = ('0.0.0.0',9999)
server = socketserver.ThreadingTCPServer(addr,ChatHandler)  #baseserver源码serve_forever函数有try...finally关闭连接
server_thread = threading.Thread(target=server.serve_forever,name='ChatServer',daemon=True).start()

try:
    while True:
        cmd = input("please set a exit code >>>")
        if cmd.strip() == 'quit':
            break
except Exception as e:
    print(e)
except KeyboardInterrupt:
    sys.exit(0
"""
通过打印可以看出,客户端主动断开连接,会导致revc方法立即返回一个空bytes,并没有同时抛出异常
当循环回到recv这一句话会抛出异常,所以通过判断data数据是否为空来判断客户端是否断开

"""
def handle(self):
    while not self.event.is_set():
        data = self.request.recv(1024)
        if not data or data =='quit':
            break

"""
总结:
为每一个连接提供RequestHandlerClass类实例,依次调用setup,handle,finish方法,并且使用
try。。。finally结构保证finish方法一定被调用,这些方法一次执行完成,如果想维持这个连接和客户端通信
就需要在handler中循环使用

"""
posted @ 2018-06-18 22:33  亚洲哈登  阅读(331)  评论(0编辑  收藏  举报