socket+select实现多并发的sockeserver
大家都知道socket是无法多客户端的互动的,如果要让socket能够多并发服务的话,就要用到多路复用IO的select、poll或epoll。
io模式有:
- 阻塞 I/O(blocking IO)
socket服务默认就是一个阻塞的io服务
- 非阻塞 I/O(nonblocking IO)
非阻塞只的是在将数据复制到内核空间的时候是不阻塞的,如果有数据,那么就读取,如果没有数据,就会返回错误
- I/O 多路复用( IO multiplexing)
多路复用只的是用select、poll或epoll来监听有没有连接,如果有连接返回一个相应,然后传输数据,其他这也不是完全的非阻塞,因为在传输数据的阶段有阻塞
- 信号驱动 I/O( signal driven IO)
- 异步 I/O(asynchronous IO)
异步IO是完全非阻塞的IO,没有阻塞,所以速度是很快的
下面socket+select实现多并发的socketserver的实例:
server端:
import select
import socket
import sys
import queue
server = socket.socket()
server.setblocking(0)
server_addr = ('localhost',10000)
print('starting up on %s port %s' % server_addr)
server.bind(server_addr)
server.listen(5)
inputs = [server, ] #自己也要监测呀,因为server本身也是个fd
outputs = []
message_queues = {}
while True:
print("waiting for next event...")
readable, writeable, exeptional = select.select(inputs,outputs,inputs) #如果没有任何fd就绪,那程序就会一直阻塞在这里
for s in readable: #每个s就是一个socket
if s is server: #别忘记,上面我们server自己也当做一个fd放在了inputs列表里,传给了select,如果这个s是server,代表server这个fd就绪了,
#就是有活动了, 什么情况下它才有活动? 当然 是有新连接进来的时候 呀
#新连接进来了,接受这个连接
conn, client_addr = s.accept()
print("new connection from",client_addr)
conn.setblocking(0)
inputs.append(conn) #为了不阻塞整个程序,我们不会立刻在这里开始接收客户端发来的数据, 把它放到inputs里, 下一次loop时,这个新连接
#就会被交给select去监听,如果这个连接的客户端发来了数据 ,那这个连接的fd在server端就会变成就续的,select就会把这个连接返回,返回到
#readable 列表里,然后你就可以loop readable列表,取出这个连接,开始接收数据了, 下面就是这么干 的
message_queues[conn] = queue.Queue() #接收到客户端的数据后,不立刻返回 ,暂存在队列里,以后发送
else: #s不是server的话,那就只能是一个 与客户端建立的连接的fd了
#客户端的数据过来了,在这接收
data = s.recv(1024)
if data:
print("收到来自[%s]的数据:" % s.getpeername()[0], data)
message_queues[s].put(data) #收到的数据先放到queue里,一会返回给客户端
if s not in outputs:
outputs.append(s) #为了不影响处理与其它客户端的连接 , 这里不立刻返回数据给客户端
else:#如果收不到data代表什么呢? 代表客户端断开了呀
print("客户端断开了",s)
if s in outputs:
outputs.remove(s) #清理已断开的连接
inputs.remove(s) #清理已断开的连接
del message_queues[s] ##清理已断开的连接
for s in writeable:
try :
next_msg = message_queues[s].get_nowait()
except queue.Empty:
print("client [%s]" %s.getpeername()[0], "queue is empty..")
outputs.remove(s)
else:
print("sending msg to [%s]"%s.getpeername()[0], next_msg)
s.send(next_msg.upper())
for s in exeptional:
print("handling exception for ",s.getpeername())
inputs.remove(s)
if s in outputs:
outputs.remove(s)
s.close()
del message_queues[s]
client端:
import socket
import sys
messages = [ b'This is the message. ',
b'It will be sent ',
b'in parts.',
]
server_address = ('localhost', 10000)
# Create a TCP/IP socket
socks = [ socket.socket(socket.AF_INET, socket.SOCK_STREAM),
socket.socket(socket.AF_INET, socket.SOCK_STREAM),
]
# Connect the socket to the port where the server is listening
print('connecting to %s port %s' % server_address)
for s in socks:
s.connect(server_address)
for message in messages:
# Send messages on both sockets
for s in socks:
print('%s: sending "%s"' % (s.getsockname(), message) )
s.send(message)
# Read responses on both sockets
for s in socks:
data = s.recv(1024)
print( '%s: received "%s"' % (s.getsockname(), data) )
if not data:
print(sys.stderr, 'closing socket', s.getsockname() )
浙公网安备 33010602011771号