python 网络编程学习 非阻塞socket
来源:互联网 发布:js遍历set集合对象 编辑:程序博客网 时间:2024/05/21 17:35
主要学习服务器的异步使用
SocketServer简化了网络服务器的编写。它有4个类:TCPServer,UDPServer,UnixStreamServer,UnixDatagramServer。这4个类是同步进行处理的,另外通过ForkingMixIn和ThreadingMixIn类来支持异步。
创建服务器的步骤
- 创建一个请求处理类,它是BaseRequestHandler的子类并重载其handle()方法。
- 实例化一个服务器类,传入服务器的地址和请求处理程序类。
- 调用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
- python 网络编程学习 非阻塞socket
- Python网络编程基础笔记-poll实现非阻塞socket
- [python&php 网络编程]把socket改成阻塞或非阻塞模式
- python使用socket非阻塞编程
- UNIX网络编程--socket中的同步/异步 阻塞/非阻塞
- UNIX网络编程--socket中的同步/异步 阻塞/非阻塞
- 网络编程Socket的阻塞和非阻塞IO
- 非阻塞socket编程
- 非阻塞socket编程
- 非阻塞socket编程
- 非阻塞Socket编程
- 非阻塞socket编程
- 网络编程学习笔记(非阻塞connect)
- 网络编程学习笔记(非阻塞accept)
- Unix网络编程学习日记(一):半双工非阻塞socket客户端的实现
- 网络编程(2)Socket同步非阻塞工作模式
- 网络编程之非阻塞socket的连接
- 网络编程之非阻塞socket的连接
- 【STL】list的常用方法
- poj1609 Tiling Up Blocks(LIS)
- 定时器
- 解决TextView富文本显示时emoj或图片和文字不对齐的问题
- 80386ASM程序设计基础
- python 网络编程学习 非阻塞socket
- 广播接收者
- poj 2352 Stars
- Atom代码美化,atom-beautify和php-cs-fixer
- 【C】C语言中sin和cos的用法
- 1046 划拳
- 存储段描述符
- 深拷贝 —— String类的实现
- 编程之美读书笔记-不要被阶乘吓倒