本例子实现一个回显服务器,当客户端发送一串字符到服务器后,服务器将该串字符返回到客户端。客户端的实现:
#!/usr/bin/env python
# encoding: utf-8
import socket
import sys
messages = [ 'This is the message. ']
server_address = ('localhost', 10000)
#创建一批套接字
socks = list(socket.socket(socket.AF_INET, socket.SOCK_STREAM) for i in range(100))
#每个套接字都连接到服务器
for s in socks:
try:
s.connect(server_address)
print >>sys.stderr, 'connecting to %s port %s' % server_address
except Exception as e:
print e.message
# errno = s.connect_ex(server_address)
# print 'errno=%d'%errno
for message in messages:
#往服务器发送消息
for s in socks:
print >>sys.stderr, '%s: sending "%s"' % \
(s.getsockname(), message)
s.send(message)
#接受服务器返回的消息
for s in socks:
data = s.recv(1024)
print >>sys.stderr, '%s: received "%s"' % \
(s.getsockname(), data)
if not data:
print >>sys.stderr, 'closing socket', s.getsockname()
s.close()
服务器的实现(采用select方式)
#!/usr/bin/env python
# encoding: utf-8
import select
import socket
import sys
import Queue
# 创建套接字并设置该套接字为非阻塞模式
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.setblocking(0)
# 绑定套接字
server_address = ('localhost', 10000)
print >>sys.stderr, 'starting up on %s port %s' % server_address
server.bind(server_address)
# 将该socket变成服务模式
# backlog等于5,表示内核已经接到了连接请求,但服务器还没有调用accept进行处理的连接个数最大为5
# 这个值不能无限大,因为要在内核中维护链接队列
server.listen(5)
#初始化读取数据的监听列表,最开始时我们希望从server这个套接字上读取数据
inputs = [ server ]
#初始化写入数据的监听列表,最开始时并没有客户端连接进来,所以列表为空
outputs = [ ]
#要发往客户端的数据
message_queues = {}
while inputs:
print >>sys.stderr, 'waiting for the next event'
#调用select监听所有监听列表中的套接字,并将准备好的套接字加入到对应的列表中
readable, writable, exceptional = select.select(inputs,
outputs,
inputs)
#处理可读的套接字
"""
如果server这个套接字可读,则说明有新链接到来
此时在server套接字上调用accept,生成一个与客户端通讯的套接字
并将与客户端通讯的套接字加入inputs列表,下一次可以通过select检查该链接是否可读
然后在发往客户端的缓冲中加入一项,键名为:与客户端通讯的套接字,键值为:空队列
"""
"""
若可读的套接字不是server套接字,有两种情况:一种是有数据到来,另外一种是链接断开
如果有数据到来,先接收数据,然后将收到的数据填入发往客户端的缓存区中的对应位置,最后
将于客户端通讯的套接字加入到写数据的监听列表;
如果套接字可读,但是没有接收到数据,则说明客户端已经断开。这时要关闭与客户端连接的套接字,并进行资源清理
"""
for s in readable:
if s is server:
connection, client_address = s.accept()
print >>sys.stderr, ' connection from', client_address
connection.setblocking(0)
inputs.append(connection)
message_queues[connection] = Queue.Queue()
else:
data = s.recv(1024)
if data:
print >>sys.stderr, ' received "%s" from %s' % \
(data, s.getpeername())
message_queues[s].put(data)
if s not in outputs:
outputs.append(s)
else:
print >>sys.stderr, ' closing', client_address
if s in outputs:
outputs.remove(s)
inputs.remove(s)
s.close()
del message_queues[s]
#处理可写的套接字
"""
在发送缓冲区中取出响应的数据 ,发往客户端。
如果没有数据需要写,则将套接字从发送对列中移除,select中不再监视
"""
for s in writable:
try:
next_msg = message_queues[s].get_nowait()
except Queue.Empty:
print >>sys.stderr, ' ', s.getpeername(), 'queue empty'
outputs.remove(s)
else:
print >>sys.stderr, ' sending "%s" to %s' % \
(next_msg, s.getpeername())
s.send(next_msg)
# 处理异常情况
for s in exceptional:
print >>sys.stderr, 'exception condition on', s.getpeername()
inputs.remove(s)
if s in outputs:
outputs.remove(s)
s.close()
del message_queues[s]
#end while
服务器的实现(采用poll方式,只能在linux下运行)
#!/usr/bin/env python
# encoding: utf-8
import select
import socket
import sys
import Queue
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.setblocking(0)
server_address = ('localhost', 10000)
print >>sys.stderr, 'starting up on %s port %s' % server_address
server.bind(server_address)
server.listen(5)
message_queues = {}
TIMEOUT = 1000
"""
poll的底层实现比select高效,但是poll在windows下不被支持
"""
# poll监听5种事件
# POLLIN:输入准备好
# POLLPRI:带外数据可读
# POLLOUT:准备好接受数据
# POLLERR:有错误发生
# POLLHUP:通道关闭
# POLLVAL:通道未打开
#只读事件组合
READ_ONLY = ( select.POLLIN |
select.POLLPRI |
select.POLLHUP |
select.POLLERR )
#读写事件组合
READ_WRITE = READ_ONLY | select.POLLOUT
#得到poll对象
poller = select.poll()
#在poll对象中注册server套接字,并让poller监听该套接字的READ_ONLY事件
poller.register(server, READ_ONLY)
#由于poller.poll返回的是元素为(fd,flag)的列表,所以我们必须保存fd与相应socket对象的映射
fd_to_socket = { server.fileno(): server,
}
while True:
#监听已注册的socket事件,返回元素为(描述符,事件标志)的列表
print >>sys.stderr, 'waiting for the next event'
events = poller.poll(TIMEOUT)
for fd, flag in events:
s = fd_to_socket[fd]
#处理输入事件
if flag & (select.POLLIN | select.POLLPRI):
#当有新连接到来
if s is server:
connection, client_address = s.accept()
print >>sys.stderr, ' connection', client_address
connection.setblocking(0)
fd_to_socket[ connection.fileno() ] = connection
#将与客户端通讯的socket对象注册到poll对象中,并让poll监听该对象的只读属性
poller.register(connection, READ_ONLY)
message_queues[connection] = Queue.Queue()
#客户端有数据到来
else:
data = s.recv(1024)
if data:
print >>sys.stderr, ' received "%s" from %s' % \
(data, s.getpeername())
message_queues[s].put(data)
# 让poll对象监听该套接字对象的读写属性
poller.modify(s, READ_WRITE)
#客户端断开
else:
print >>sys.stderr, ' closing', client_address
poller.unregister(s)
s.close()
del message_queues[s]
#套接字可写
elif flag & select.POLLOUT:
# Socket is ready to send data, if there is any to send.
try:
next_msg = message_queues[s].get_nowait()
except Queue.Empty:
# No messages waiting so stop checking
print >>sys.stderr, s.getpeername(), 'queue empty'
#如果没数据可写,则不监听写事件
poller.modify(s, READ_ONLY)
else:
print >>sys.stderr, ' sending "%s" to %s' % \
(next_msg, s.getpeername())
s.send(next_msg)
#通道关闭
elif flag & select.POLLHUP:
# Client hung up
print >>sys.stderr, ' closing', client_address, '(HUP)'
poller.unregister(s)
s.close()
del message_queues[s]
#发生错误
elif flag & select.POLLERR:
print >>sys.stderr, ' exception on', s.getpeername()
poller.unregister(s)
s.close()
del message_queues[s]