这里总结下一个服务端与多个客户端之间的通信。
先看demo:
#/usr/bin/env python
#_*_coding:utf-8_*_
__author__ = 'ganzl'
import time
import socket
import select
sk = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
sk.bind(('127.0.0.1',6666))
sk.listen(5)
sk.setblocking(False)
inputs = [sk,] #sk就是一个监听的sorcket,放入inputs这个列表中。 while True:
readable_list, writeable_list, error_list = select.select(inputs,[],[],1) #把第一个参数设为列表动态的添加
time.sleep(2)
print "inputs :",inputs #打印inputs列表,查看执行变化
print "readable_list:",readable_list #打印readable_list ,查看执行变化 for r in readable_list:
if r == sk: #sk是一个描述符,
conn,address = r.accept()
inputs.append(conn)#conn是连接socket之后的那根通信管道。
#sk监控的是socket描述符是否有变化,conn监控的是连接后的socket之间两者服务器之间的通信是否有变化
print address
else:
#如果是客户端,接受和返回数据
client_data = r.recv(1024)
if client_data:
r.sendall(client_data)
else:
inputs.remove(r)#如果没有收到客户端端数据,则移除客户端句柄 因为,不管是正常关闭还是异常关闭,client端的系统底层都会发送一个消息
#异常关闭的时候会发送一个空格。
客户端(1)
#!/usr/bin/env python
#_*_coding:utf-8 _*_
__author__ = 'ganzl'
import socket
import time
#创建socket对象
ck = socket.socket()
#通过IP&端口连接server
ck.connect(('127.0.0.1',6666))
#发送消息给服务端
time.sleep(10)
ck.sendall('hello server, this is ganzl---test3---1-')
ck.sendall('hello server, this is ganzl---test3----')
#接收服务端的消息并打印
server_answer = ck.recv(1024)
print server_answer
#关闭连接
ck.close()
客户端(2) #!/usr/bin/env python
#_*_coding:utf-8 _*_
__author__ = 'ganzl' import socket
ck = socket.socket()
ck.connect(('127.0.0.1',6666))
ck.sendall('hello server, this is ganzl----2----')
server_answer = ck.recv(1024)
print server_answer
ck.close()
客户端(3)
通过http访问:http://127.0.0.1:6666/
------------------------------------------------------------------------
以上就是用到了IO的多路复用中的select模块。
Python中有一个select模块,其中提供了:select、poll、epoll三个方法,分别调用系统的 select,poll,epoll 从而实现IO多路复用。其他两个方法暂时还没弄懂,空了再看下源码,再百度,再请教其他人。。。
select方法总结:
句柄列表11, 句柄列表22, 句柄列表33 = select.select(句柄序列1, 句柄序列2, 句柄序列3, 超时时间)
参数: 可接受四个参数(前三个必须)
返回值:三个列表
select方法用来监视文件句柄,如果句柄发生变化,则获取该句柄。
1、当 参数1 序列中的句柄发生可读时(accetp和read),则获取发生变化的句柄并添加到 返回值1 序列中
2、当 参数2 序列中含有句柄时,则将该序列中所有的句柄添加到 返回值2 序列中
3、当 参数3 序列中的句柄发生错误时,则将该发生错误的句柄添加到 返回值3 序列中
4、当 超时时间 未设置,则select会一直阻塞,直到监听的句柄发生变化
当 超时时间 = 1时,那么如果监听的句柄均无任何变化,则select会阻塞 1 秒,之后返回三个空列表,如果监听的句柄有变化,则直接执行。
写上面这个demo的目的是啥?其实就是SocketServe源码中的最几句核心代码。
SocketServer的ThreadingTCPServer之所以可以同时处理请求得益于 select 和 Threading 两个东西,其实本质上就是在服务器端为每一个客户端创建一个线程,当前线程用来处理对应客户端的请求,所以,可以支持同时n个客户端链接(长连接)。
用一个SocketServer的实际例子来过过代码。方便以后查看,理解。
SocketServer的demo:
服务端:
#!/usr/bin/env python
#_*_coding:utf-8 _*_
__author__ = 'ganzl'
import SocketServer
class MyServer(SocketServer.BaseRequestHandler): def handle(self):
# print self.request,self.client_address,self.server
conn = self.request
conn.sendall('欢迎致电 10086,请输入1xxx,0转人工服务.')
Flag = True
while Flag:
data = conn.recv(1024)
if data == 'exit':
Flag = False
elif data == '':
conn.sendall('通过可能会被录音.balabala一大推')
else:
conn.sendall('请重新输入.')
if __name__ == '__main__':
server = SocketServer.ThreadingTCPServer(('127.0.0.1',8009),MyServer)
server.serve_forever()
客服端:
#!/usr/bin/env python
#_*_coding:utf-8 _*_
__author__ = 'ganzl'
import socket ip_port = ('127.0.0.1',8009)
sk = socket.socket()
sk.connect(ip_port)
sk.settimeout(5)
while True:
data = sk.recv(1024)
print 'receive:',data
inp = raw_input('please input:')
sk.sendall(inp)
if inp == 'exit':
break sk.close()
代码实现多个客户端可以同时跟服务器端通信的原因如下:(我自己的类继承了SocketServer.BaseRequestHandler这个类,而它封装了一些基础的类,我自己重写它的方法就好了)
我就不文字描述。 图片就是太难看。有时间再弄个好看的
上图的精简demo如下:
源码精简:(SocketServer) import socket
import threading
import select def process(request, client_address):
print request,client_address
conn = request
conn.sendall('欢迎致电 10086,请输入1xxx,0转人工服务.')
flag = True
while flag:
data = conn.recv(1024)
if data == 'exit':
flag = False
elif data == '':
conn.sendall('通过可能会被录音.balabala一大推')
else:
conn.sendall('请重新输入.') sk = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sk.bind(('127.0.0.1',8002))
sk.listen(5) while True:
r, w, e = select.select([sk,],[],[],1)
print 'looping'
if sk in r:
print 'get request'
request, client_address = sk.accept()
t = threading.Thread(target=process, args=(request, client_address))
t.daemon = False
t.start() sk.close()
这里socket用到的都是线程。一个进程中产生的多线程。至于进程线程协程,过段时间搞搞明白。