python 网络编程学习 非阻塞socket

来源:互联网 发布:js遍历set集合对象 编辑:程序博客网 时间:2024/05/21 17:35

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

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 支持异步

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()

执行可以看到

duck@duck:~/sockdir/chapter_2$ python forkser.py Server loop running PID: 22649PID: %s  22649sent 13 characters...22651: hello, world!PID 22649 received: 22651: hello, world!PID: %s  22649sent 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: 8989Now 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

0 0
原创粉丝点击