비동기 네트워크는 네트워크 서버의 연결 속도를 크게 향상시킬 수 있다고 하는데, 비동기 네트워크를 배우고 이해하기 위한 주제를 작성하려고 합니다. Python에는 매우 유명한 비동기 Lib: Twisted가 있어서 Python을 사용하여 완성했습니다.
좋아요, 먼저 pythone 소켓의 서버 세그먼트를 작성하고 10000, 10001, 10002의 3개 포트를 엽니다. krondo의 예에서 각 서버는 테스트 중에 3개의 셸을 열어야 합니다. 각각 별도로 실행하는 것은 너무 번거롭기 때문에 이러한 서비스를 실행하려면 3개의 스레드를 사용하세요.
import optparse import os import socket import time from threading import Thread import StringIO txt = '''1111 2222 3333 4444 ''' def server(listen_socket): while True: buf = StringIO.StringIO(txt) sock, addr = listen_socket.accept() print 'Somebody at %s wants poetry!' % (addr,) while True: try: line = buf.readline().strip() if not line: sock.close() break sock.sendall(line) # this is a blocking call print 'send bytes to client:%s' % line #sock.close() except socket.error: sock.close() break time.sleep(1) #server和client连接后,server会故意每发送一个单词后等待一秒钟后再发送另一个单词 def main(): ports = [10000, 10001, 10002] for port in ports: listen_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) listen_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) addres = (str('127.0.0.1'), port) listen_socket.bind(addres) listen_socket.listen(5) print "start listen at:%s" % (port,) worker = Thread(target = server, args = [listen_socket]) worker.setDaemon(True) worker.start() if __name__ == '__main__': main() while True: time.sleep(0.1) #如果不sleep的话,CPU会被Python完全占用了 pass
다음은 비동기 네트워크가 없는 경우 3개의 포트로 서버에 연결합니다.
import socket if __name__ == '__main__': ports = [10000, 10001, 10002] for port in ports: address = (str('127.0.0.1'), port) sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.connect(address) poem = '' while True: data = sock.recv(4) if not data: sock.close() break poem += data print poem
읽기에는 비동기 클라이언트가 사용됩니다. 코드는 다음과 같습니다.
import datetime, errno, optparse, select, socket def connect(port): """Connect to the given server and return a non-blocking socket.""" address = (str('127.0.0.1'), port) sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.connect(address) sock.setblocking(0) return sock def format_address(address): host, port = address return '%s:%s' % (host or '127.0.0.1', port) if __name__ == '__main__': ports = [10000, 10001, 10002] start = datetime.datetime.now() sockets = map(connect, ports) poems = dict.fromkeys(sockets, '') # socket -> accumulated poem # socket -> task numbers sock2task = dict([(s, i + 1) for i, s in enumerate(sockets)]) sockets = list(sockets) # make a copy while sockets: #运用select来确保那些可读取的异步socket可以立即开始读取IO #OS不停的搜索目前可以read的socket,有的话就返回rlist rlist, _, _ = select.select(sockets, [], []) for sock in rlist: data = '' while True: try: new_data = sock.recv(1024) except socket.error, e: if e.args[0] == errno.EWOULDBLOCK: break raise else: if not new_data: break else: print new_data data += new_data task_num = sock2task[sock] if not data: sockets.remove(sock) sock.close() print 'Task %d finished' % task_num else: addr_fmt = format_address(sock.getpeername()) msg = 'Task %d: got %d bytes of poetry from %s' print msg % (task_num, len(data), addr_fmt) poems[sock] += data elapsed = datetime.datetime.now() - start print 'Got poems in %s' % elapsed
결과 읽기 작업을 완료하는 데 4초밖에 걸리지 않습니다. 효율성은 현재 동기화 소켓의 3배입니다. 클라이언트의 비동기 변환에는 두 가지 주요 사항이 있습니다.
동기 모드에서는 클라이언트가 소켓을 별도로 생성하고, 비동기 모드에서는 클라이언트가 처음부터 모든 소켓을 생성합니다.
"sock.setblocking(0)"을 통해 소켓을 비동기 모드로 설정합니다.
Unix 시스템 중 두 가지 선택을 통해 읽을 수 있는 IO를 반환합니다
가장 핵심적인 것은 26행과 29행입니다. 특히 29행의 선택 작업은 읽을 소켓 목록을 반환합니다.