前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >python 网络编程学习 非阻塞soc

python 网络编程学习 非阻塞soc

作者头像
py3study
发布2020-01-07 11:57:21
9800
发布2020-01-07 11:57:21
举报
文章被收录于专栏:python3python3

主要学习服务器的异步使用

SocketServer简化了网络服务器的编写。它有4个类:TCPServer,UDPServer,UnixStreamServer,UnixDatagramServer。这4个类是同步进行处理的,另外通过ForkingMixIn和ThreadingMixIn类来支持异步。

创建服务器的步骤

  1. 创建一个请求处理类,它是BaseRequestHandler的子类并重载其handle()方法。
  2. 实例化一个服务器类,传入服务器的地址和请求处理程序类。
  3. 调用handle_request()(一般是调用其他事件循环或者使用select())或serve_forever()。

集成ThreadingMixIn类时需要处理异常关闭。daemon_threads指示服务器是否要等待线程终止,要是线程互相独立,必须要设置为True,默认是False。

使用基于多进程,ForkingMinIn 支持异步

代码语言:javascript
复制
import os                                                                                                                                                                          
import socket                                                                       
import threading                                                                    
import SocketServer                                                                 
                                                                                    
# 构造客户端                                                                               
class ForkingClient():                                                              
    def __init__(self, ip, port):                                                   
        self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)     
        self.sock.connect((ip, port))                                               
                                                                                    
    def run(self):                                                                  
        crtpid = os.getpid()                                                        
        print "PID: %s " % crtpid                                                    
        send_length = self.sock.send("hello, world!")                               
        print "sent %d characters..." % send_length                                 
        res = self.sock.recv(2048)                                                  
        print "PID %s received: %s" % (crtpid, res)                                 
                                                                                    
    def shutdown(self):                                                             
        self.sock.close()                                                           
                                                                                    
# 写服务端处理函数                                                                                
class ForkingServerRequestHandler(SocketServer.BaseRequestHandler):                 
    def handle(self):                                                               
        data = self.request.recv(2048)                                              
        crtpid = os.getpid()                                                        
        res = "%s: %s" % (crtpid, data)                                             
        print res                                                                   
        self.request.send(res)                                                      
        return                                                                      
                                                                                    
# 继承                                                                                  
class ForkingServer(SocketServer.ForkingMixIn, SocketServer.TCPServer):             
    pass                                                                            
                                                                                    
                                                                                    
def main():                                                                         
    server = ForkingServer(('localhost', 8989), ForkingServerRequestHandler)        
    ip, port = server.server_address                                                
    server_thread = threading.Thread(target=server.serve_forever)                   
    server_thread.setDaemon(True)                                                   
    server_thread.start()
    print 'Server loop running PID: %s' % os.getpid()

    client_1 = ForkingClient(ip, port)
    client_2 = ForkingClient(ip, port)

    client_1.run()
    client_2.run()
                                                                                    
    client_1.shutdown()
    client_2.shutdown()
    server.socket.close()

                                                                          
if __name__ == '__main__':
    main()
代码语言:javascript
复制
执行可以看到

duck@duck:~/sockdir/chapter_2$ python forkser.py 
Server loop running PID: 22649
PID: %s  22649
sent 13 characters...
22651: hello, world!
PID 22649 received: 22651: hello, world!
PID: %s  22649
sent 13 characters...
22652: hello, world!
PID 22649 received: 22652: hello, world!

现在用ThreadingMixIn

优势:线程之间共享应用状态容易,避免进程间通信的复杂操作。

import os                                                                                                                                                                          
import socket                                                                       
import threading                                                                    
import SocketServer                                                                 
                                                                                    
                                                                                    
class ForkingClient():                                                              
    def __init__(self, ip, port):                                                   
        self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)               
        self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)             
        self.sock.connect((ip, port))                                               
                                                                                    
    def run(self):                                                                  
        crtpid = os.getpid()                                                        
        print "PID: %s " % crtpid                                                   
        send_length = self.sock.send("hello, world!")                               
        print "sent %d characters..." % send_length                                 
        res = self.sock.recv(2048)                                                  
        print "PID %s received: %s" % (crtpid, res)                                 
                                                                                    
    def shutdown(self):                                                             
        self.sock.close()                                                           
                                                                                    
                                                                                    
class ThreadedTCPRequestHandler(SocketServer.BaseRequestHandler):                   
    def handle(self):                                                               
        data = self.request.recv(2048)                                              
        crtpid = threading.current_thread()                                         
        res = "%s: %s" % (crtpid, data)                                             
        print res                                                                   
        self.request.sendall("hello, client!")                                      
        return                                                                      
                                                                                    
                                                                                    
class ThreadedTCPServer(SocketServer.ThreadingMixIn, SocketServer.TCPServer):       
    pass                                                                            
                                                                                    
                                                                                    
def main():                                                                         
    server = ThreadedTCPServer(('localhost', 8989),ThreadedTCPRequestHandler)       
    ip, port = server.server_address                                                
    server_thread = threading.Thread(target=server.serve_forever)
    # 线程互相独立,必须要设置为True                   
    server_thread.setDaemon(True)                                                   
    server_thread.start()                                                           
    print 'Server loop running PID: %s' % server_thread.name                        
                                                                                    
    client_1 = ForkingClient(ip, port)                                              
    client_2 = ForkingClient(ip, port)                                              
                                                                                    
    client_1.run()                                                                  
    client_2.run() 
    server.socket.close()                                                        
                                                                                 
if __name__ == '__main__':                                                       
    main()                                                                                                                                                                        



可以看到基本套路都差不多,就是替换了一些处理类

而在大型网络服务器应用中,存在几百上千的并发连接时,为每个客户端建立单独的线程和进程不太实际。内存和主机cpu都有限,需要一种更好的办法来处理,那就是select模块。
这里可以了解一下,select,poll ,epoll三个模块
link

先用select的select模块编写一个聊天室服务器。分了三个模块
先写通讯模块

import cPickle                                                                                                                                                                     
import struct                                                                       
import socket                                                                       
                                                                                    
def send(channel, *args):                                                           
    buf = cPickle.dumps(args)                                                       
    value = socket.htonl(len(buf))                                                  
    size = struct.pack("L", value)                                                  
    channel.send(size)                                                              
    channel.send(buf)                                                               
                                                                                    
def receive(channel):                                                               
    size = struct.calcsize("L")                                                     
    size = channel.recv(size)                                                       
    try:                                                                            
        size = socket.ntohl(struct.unpack("L", size)[0])                            
    except struct.error, e:                                                         
        return ''                                                                   
    buf = ""                                                                        
    while len(buf) < size:                                                          
        buf += channel.recv(size - len(buf))                                        
    return cPickle.loads(buf)[0]


服务模块


from com_model import send, receive                                                                                                                                                
                                                                                    
import select                                                                       
import socket                                                                       
import sys                                                                          
import signal                                                                       
                                                                                    
                                                                                    
class ChatServer(object):                                                           
    def __init__(self, port, backlog=5):                                            
        self.clients = 0                                                            
        self.clientmap = {}                                                         
        self.outputs = []                                                           
        self.server = socket.socket(socket.AF_INET, socket.SOL_SOCKET)              
        self.server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)           
        self.server.bind(('localhost', port))                                       
        self.server.listen(backlog)                                                 
        signal.signal(signal.SIGINT, self.sighandler)                               
                                                                                    
    def sighandler(self, signum, frame):                                            
        print "Shutting down server...."                                            
        for output in self.outputs:                                                 
            output.close()                                                          
        self.server.close()                                                         
                                                                                    
    def get_client_name(self, client):                                              
        info = self.clientmap[client]                                               
        host, name = info[0][0], info[1]                                            
        return '@'.join((name, host))                                               
                                                                                    
    def run(self):                                                                  
        inputs = [self.server, sys.stdin]                                           
        self.outputs = []                                                           
        running = True                                                              
        while running:                                                              
            try:                                                                    
                readable, writeable, exceptional = select.select(inputs, self.outputs, []) 
            except select.error, e:                                                 
                break                                                               
                                                                                    
            for sock in readable:                                                   
                if sock == self.server:                                             
                    client, address = self.server.accept()                          
                    print "Chat server: got connection %d from %s" % (client.fileno(), address)
                    cname = receive(client).split('NAME: ')[1]                      
                                                                                    
                    self.clients += 1                                               
                    send(client, "CLIENT: " + str(address[0]))                      
                    inputs.append(client)                                           
                    self.clientmap[client] = (address, cname)                       
                    msg = "\n(Connected: New client (%d) from %s" % (self.clients, self.get_client_name(client))
                    for output in self.outputs: 
                                                                                
                elif sock == sys.stdin:                                          
                    junk = sys.stdin.readline()                                  
                    running = False                                              
                                                                                 
                else:                                                            
                    try:                                                         
                        data = receive(sock)                                     
                        if data:                                                 
                            msg = '\n#[' + self.get_client_name(sock) + ']>>' + data
                            for output in self.outputs:                          
                                if output != sock:                               
                                    send(output, msg)                            
                                                                                 
                        else:                                                    
                            print "Chat server: %d hung up" % sock.fileno()      
                            self.clients -= 1                                    
                            sock.close()                                         
                            inputs.remove(sock)                                  
                            self.outputs.remove(sock)                            
                                                                                 
                            msg = '\n(Now hung up: Client from %s)' % self.get_client_name(sock)
                            for output in self.outputs:                          
                                send(output, msg)                                
                                                                                 
                    except socket.error, e:                                      
                        inputs.remove(sock)                                      
                        self.outputs.remove(sock)                                
        self.server.close()                                                      
                                                                                 
                                                                                 
def main():                                                                      
    server = ChatServer(8989)                                                    
    server.run()                                                                 
                                                                                 
if __name__ == '__main__':                                                       
    main()                               



客户端模块


from com_model import send, receive                                                                                                                                                
                                                                                    
import os                                                                           
import select                                                                       
import socket                                                                       
import sys                                                                          
import signal                                                                       
                                                                                    
class ChatClient(object):                                                           
    def __init__(self, name, port, host="localhost"):                               
        self.name = name                                                            
        self.connected = False                                                      
        self.host = host                                                            
        self.port = port                                                            
        self.prompt = '[' + '@'.join((name, socket.gethostname().split('.')[0])) + ']>'
                                                                                    
        try:                                                                        
            self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)           
            self.sock.connect((host, self.port))                                    
            print "Now connected to chat server@ port %d" % self.port               
            self.connected = True                                                   
            send(self.sock, 'NAME: ' + self.name)                                   
            data = receive(self.sock)                                               
            addr = data.split('CLIENT: ')[1]                                        
            self.prompt = '[' + '@'.join((self.name, addr)) + ']> '                 
        except socket.error, e:                                                     
            print "Failed connect server @port %d" % self.port                      
            sys.exit(1)                                                             
                                                                                    
    def run(self):                                                                  
        while self.connected:                                                       
            try:                                                                    
                sys.stdout.write(self.prompt)                                       
                sys.stdout.flush()                                                  
                readable, writeable, exceptional = select.select([0, self.sock], [],[])
                for sock in readable:                                               
                    if sock == 0:                                                   
                        data = sys.stdin.readline().strip()                         
                        if data:                                                    
                            send(self.sock, data)                                   
                    elif sock == self.sock:                                         
                        data = receive(self.sock)                                   
                        if not data:                                                
                            print "Client shutting down."                           
                            self.connected = False                                  
                            break                                                   
                        else:                                                       
                            sys.stdout.write(data + '\n')                           
                            sys.stdout.flush()                                      
                                                                                    
            except KeyboardInterrupt:                                               
                print "interupt"                                                     
                self.sock.close()                                                
                break                                                            
def main():                                                                      
    cli_name = str(os.getpid()) + 'client'                                       
    cli_port = raw_input("port: ")                                               
    client = ChatClient(cli_name, int(cli_port))                                 
    client.run()                                                                 
                                                                                 
if __name__ == '__main__':                                                       
    main()  

 先启动服务端,然后启动客户端,输入服务端的port,这里写死成8989了,所以写入8989就可以连入通信了


duck@duck:~/sockdir/chapter_2/select_ex$ python cli_chat.py 
port: 8989
Now connected to chat server@ port 8989
[25395client@127.0.0.1]> haha
[25395client@127.0.0.1]> 
(Connected: New client (2) from 25415client@127.0.0.1
[25395client@127.0.0.1]> 
#[25415client@127.0.0.1]>>hee
[25395client@127.0.0.1]> nihao
接下来将用epoll写一个简单的web服务器,熟悉一下什么叫I/O多路复用,这名字翻译的也是一脸蒙逼
看看大神的们的解释,秒懂
link
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2019-09-10 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档