使用異步io實現只使用單進程,單線程的socket。處理多個用戶的請求。
先簡單說明一下select異步io工作原理:
select 是一個跨平台的異步io模型,由於我們計算機的硬盤速度,遠遠低於我們內存的速度,因此如果同時有多個用戶來請求數據時候,io效率就會變得更慢。
而select 是一直不斷在監控我們網絡接口的。如果我們的服務器網絡接收到,一個用戶的請求,就會在系統的某個路徑下面,生成一個文件描述符,select就是不斷的在監控這些文件描述符(即每個用戶的連接),如果連接進來了,就會調用內核將這個文件描述符標志位改為ready,從而我們的select就可以操作這文件描述符了。
回到我們的程序上來,異步的socket 使用select 就是一直在循環,不斷的去監視文件描述符,當有新的用戶來連接時候,直接把新連接的用戶保存到一個列表當中。如果循環到不是新的用戶則對用戶的操作進行處理
socket 服務器端
#!/usr/bin/env python
# _*_ coding:utf-8 _*_
import select
import socket
import sys
import Queue
# Create a TCP/IP socket
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.setblocking(False)
# Bind the socket to the port
server_address = ('localhost', 10000)
print(sys.stderr, 'starting up on %s port %s' % server_address)
server.bind(server_address)
# Listen for incoming connections
server.listen(5)
# Sockets from which we expect to read
inputs = [server]
# Sockets to which we expect to write
outputs = []
message_queues = {}
while inputs:
# Wait for at least one of the sockets to be ready for processing
print('\nwaiting for the next event')
readable, writable, exceptional = select.select(inputs, outputs, inputs)
# Handle inputs
for s in readable:
if s is server:
# A "readable" server socket is ready to accept a connection
connection, client_address = s.accept()
print('new connection from', client_address)
connection.setblocking(False)
inputs.append(connection)
# Give the connection a queue for data we want to send
message_queues[connection] = Queue.Queue()
else:
data = s.recv(1024)
if data:
# A readable client socket has data
print(sys.stderr, 'received "%s" from %s' % (data, s.getpeername()))
message_queues[s].put(data)
# Add output channel for response
if s not in outputs:
outputs.append(s)
else:
# Interpret empty result as closed connection
print('closing', client_address, 'after reading no data')
# Stop listening for input on the connection
if s in outputs:
outputs.remove(s) # 既然客戶端都斷開了,我就不用再給它返回數據了,所以這時候如果這個客戶端的連接對象還在outputs列表中,就把它刪掉
inputs.remove(s) # inputs中也刪除掉
s.close() # 把這個連接關閉掉
# Remove message queue
del message_queues[s]
# Handle outputs
for s in writable:
try:
next_msg = message_queues[s].get_nowait()
except Queue.Empty:
# No messages waiting so stop checking for writability.
print('output queue for', s.getpeername(), 'is empty')
outputs.remove(s)
except KeyError:
continue
else:
print('sending "%s" to %s' % (next_msg, s.getpeername()))
s.send(next_msg)
# Handle "exceptional conditions"
for s in exceptional:
print('handling exceptional condition for', s.getpeername())
# Stop listening for input on the connection
inputs.remove(s)
if s in outputs:
outputs.remove(s)
s.close()
# Remove message queue
del message_queues[s]
socket 客戶端:
#!/usr/bin/env python
# -*- coding:utf-8 -*-
__author__ = 'admin'
import socket
import sys
def select_sock(ip, port):
msg = u'俺老孫來也!'.encode('utf-8')
sock = [socket.socket(socket.AF_INET,socket.SOCK_STREAM),
socket.socket(socket.AF_INET,socket.SOCK_STREAM),
socket.socket(socket.AF_INET,socket.SOCK_STREAM),
socket.socket(socket.AF_INET,socket.SOCK_STREAM),
socket.socket(socket.AF_INET,socket.SOCK_STREAM),
socket.socket(socket.AF_INET,socket.SOCK_STREAM),
socket.socket(socket.AF_INET,socket.SOCK_STREAM),
]
for s in sock:
s.connect((ip, port))
s.send(msg)
print >> sys.stderr ,'%s send :%s' % (s.getpeername(), msg)
for i in sock:
data = i.recv(1024)
print >> sys.stderr, '%s recv:%s ' % (i.getpeername(), data)
if not data:
print >> sys.stderr, 'connect close!', i.getpeername()
if __name__ == '__main__':
server_addr = '192.168.1.111'
server_port = 2288
select_sock(server_addr, server_port)