socket及其相关(续篇)
IO 多路复用
基本概念
IO多路复用是指内核一旦发现进程指定的一个或者多个IO条件准备读取,它就通知该进程。IO多路复用适用如下场合:
(1)当客户处理多个描述字时(一般是交互式输入和网络套接口),必须使用I/O复用。
(2)当一个客户同时处理多个套接口时,而这种情况是可能的,但很少出现。
(3)如果一个TCP服务器既要处理监听套接口,又要处理已连接套接口,一般也要用到I/O复用。
(4)如果一个服务器即要处理TCP,又要处理UDP,一般要使用I/O复用。
(5)如果一个服务器要处理多个服务或多个协议,一般要使用I/O复用。
与多进程和多线程技术相比,I/O多路复用技术的最大优势是系统开销小,系统不必创建进程/线程,也不必维护这些进程/线程,从而大大减小了系统的开销。
总而言之,指通过一种机制,可以监视多个描述符,一旦某个描述符就绪(一般是读就绪或者写就绪),能够通知程序进行相应的读写操作。
Python中有一个select模块,其中提供了:select、poll、epoll三个方法,分别调用系统的 select,poll,epoll 从而实现IO多路复用。
windows python :
只支持 select 方法
mac python :
只支持 select 方法
Linux python :
支持 select poll epoll 三种方法
说了一大堆,估计大家也被整晕了,我们还是直接上示例吧~~
终端操作示例:
std.py
#f = file() , obj = socket(), sys.stdin = 终端输入
#select.select监听用户输入,如果用户输入内容,select 会感知 sys.sdtin 改变,将改变的文件句柄保存至列表,并将列表作为select第一个参数返回,如果用户未输入内容,select 第一个参数 = [],
import select
import threading
import sys
while True:
readable, writeable, error = select.select([sys.stdin,],[],[],1)
if sys.stdin in readable:
print 'select get stdin',sys.stdin.readline()
执行结果:
go go go #等待用户输入,用户输入: go go go
select get stdin go go go
ok #用户输入 ok
select get stdin ok
socket操作示例
回顾之前socket参数之 -----> sk.setblocking(bool)
是否阻塞(默认True),<阻塞>,如果设置False,<不阻塞>,那么accept和recv时一旦无数据,则报错。
请看如下示例:
server.py
import socket
import time
sk1 = socket.socket()
sk1.bind(('127.0.0.1',8001))
sk1.listen(5)
sk1.setblocking(False) #这里设置False,<不阻塞>
while True:
try: #获取异常,如果不做这一步,获取不到数据则报错
conn,addr = sk1.accept()
conn.close()
print addr
except Exception,e:
print e
time.sleep(2)
在客户端执行,如果客户端没有请求(用浏览器输入:http://127.0.0.1:8001),则报错,如有输入,则获取到值,如下:
Errno 35] Resource temporarily unavailable #没有客户端请求时,报错
[Errno 35] Resource temporarily unavailable
[Errno 35] Resource temporarily unavailable
[Errno 35] Resource temporarily unavailable
[('127.0.0.1', 62393) #获取到值
('127.0.0.1', 62394)
[Errno 35] Resource temporarily unavailable
('127.0.0.1', 62395)
select 示例1:
select.py
import socket
import time
import select
sk1 = socket.socket()
sk1.bind(('127.0.0.1',8001))
sk1.listen(5)
sk1.setblocking(False)
while True:
#readable_list :如果有客户端连接,则有值,否则空列表
readable_list, writeable_list, error_list = select.select([sk1,],[],[],2) #不是空列表,select感知,sk1获取到值
for r in readable_list: #不是空列表,循环执行
conn,addr = r.accept()
print addr
执行: python select.py
在浏览器输入: http://127.0.0.1:8001
执行结果:(服务器端打印日志)
('127.0.0.1', 62205)
('127.0.0.1', 62206)
接下来,再多监听一个端口(监听多端口):
import select
import socket
sk1 = socket.socket()
sk1.bind(('127.0.0.1',8001)) #监听8001端口
sk1.listen(5)
sk1.setblocking(False)
#新监听8002端
sk2 = socket.socket()
sk2.bind(('127.0.0.1',8002)) #监听8002端口 -->新监听的端口
sk2.listen(5)
sk2.setblocking(False)
while True:
readable_list, writeable_list, error_list = select.select([sk1,sk2],[],[],1) #新增:[sk1,sk2]
for r in readable_list:
conn,addr = r.accept()
print addr
再次在浏览器器输入新监听的端口,你会发现原来服务端只支持处理一个客户端请求,如今可以支持处理多个客户端请求:
http://127.0.0.1:8002,执行结果如下:
('127.0.0.1', 62222)
('127.0.0.1', 62223)
select 示例2:
服务端:server.py
sk1 = socket.socket()
sk1.bind(('127.0.0.1',8001))
sk1.listen(5)
sk1.setblocking(False)
inputs = [sk1,]
#将上例中select.select([sk1,]...) 中[sk1,] 赋值给 inputs
#sk1 , conn 都是socket对象(文件描述符)
while True:
readable_list, writeable_list, error_list = select.select(inputs,[],[],1) #请看这里的修改
time.sleep(2) #因为执行处理速度太快,这里sleep 2s
print "inputs:",inputs #打印inputs ,方便执行时观察变化
print "res:",readable_list #打印readable_list ,方便执行时查看变化
for r in readable_list:
if r == sk1:
#判断是服务端还是客户端,如果是服务端才进行下面的操作,因为客户端没有accept方法
conn,addr = r.accept()
inputs.append(conn)
print addr
else:
#如果是客户端,接受和返回数据
client_data = r.recv(1024)
r.sendall(client_data)
上面的server端详解如下:
#第一次请求进来(第一个客户端进来,只是连接,没有操作): readable_list = [sk1,], 第一次执行完后: inputs = [sk1,] ---> inputs = [sk1,conn1]
#第二次请求进来(第二个客户端进来,也只是连接,没有操作):readable_list = [sk1,] ,执行完后:inputs = [sk1,conn1,] ---> inputs = [sk1,conn1,conn2]
#如果第一个客户端发送一条数据,服务端的socket<sk1>不变,只是客户端的socket<即conn1>变化 :readable_list = [conn1,] , inputs = [sk1,conn1,conn2]
#<conn1 应该是conn,这里conn1 代表第一个客户端进来>
----
#第一个参数,监听的句柄序列
#如果第二参数有参数,即只要不是空列表,select就能感知,然后writeabled_list就能获取值
#第三个参数监听描述符,监听是否出错,如果出错,则摘除
#第四个参数,阻塞时间,如 1秒(这个如果不写,select会阻塞住,直到监听的描述符发生变化才继续往下执行)
readable_list, writeable_list, error_list = select.select(inputs,[],[],1)
客户端: client.py
import select
import socket
client = socket.socket()
client.connect(('127.0.0.1',8001))
client.settimeout(10)
while True:
client_input = raw_input('please input:').strip()
client.sendall(client_input)
server_data = client.recv(1024)
print server_data
client.close()
服务端执行结果:
inputs: [<socket._socketobject object at 0x104cbbf30>]
res: []
inputs: [<socket._socketobject object at 0x104cbbf30>]
res: []
客户端连接之后,服务端日志(但是还没有输入)
inputs: [<socket._socketobject object at 0x104cbbf30>]
res: [<socket._socketobject object at 0x104cbbf30>]
('127.0.0.1', 62815)
inputs: [<socket._socketobject object at 0x104cbbf30>, <socket._socketobject object at 0x104cbbfa0>]
res: []
第二个客户端端连接:
inputs: [<socket._socketobject object at 0x104cbbf30>]
res: [<socket._socketobject object at 0x104cbbf30>]
('127.0.0.1', 62815)
inputs: [<socket._socketobject object at 0x104cbbf30>, <socket._socketobject object at 0x104cbbfa0>]
res: []
在客户端输入(其中一个client):
inputs: [<socket._socketobject object at 0x104cbbf30>, <socket._socketobject object at 0x104cbbfa0>, <socket._socketobject object at 0x104cfc050>]
res: []
inputs: [<socket._socketobject object at 0x104cbbf30>, <socket._socketobject object at 0x104cbbfa0>, <socket._socketobject object at 0x104cfc050>]
res: [<socket._socketobject object at 0x104cbbfa0>]
inputs: [<socket._socketobject object at 0x104cbbf30>, <socket._socketobject object at 0x104cbbfa0>, <socket._socketobject object at 0x104cfc050>]
res: []
注意:
我们知道,上面的例子,如客户端中途断开,在服务端连接并没有释放
对于上面的例子,做如下修改(下面的例子,客户端断开后,直接释放)
服务端修改如下
server.py
import select
import socket
import time
sk1 = socket.socket()
sk1.bind(('127.0.0.1',8001))
sk1.listen(5)
sk1.setblocking(False)
inputs = [sk1,]
outputs = [] #这里修改
while True:
readable_list, writeable_list, error_list = select.select(inputs,outputs,[],2) #情况这里修改
time.sleep(2)
print "inputs:",inputs
print "res:",readable_list
print "wri",writeable_list
for r in readable_list:
if r == sk1:
conn,addr = r.accept()
inputs.append(conn)
outputs.append(conn) #append句柄
print addr
else:
client_data = r.recv(1024)
if client_data:
r.sendall(client_data)
else:
inputs.remove(r) #如果没有收到客户端端数据,则移除客户端句柄
执行结果如下:
连个客户端连接:
断开其中一个客户端
此时,发现断开的那个客户端被自动剔除
下面是讲解 readable_list, writeable_list 读写拆分的示例
import select
import socket
import time
sk1 = socket.socket()
sk1.bind(('127.0.0.1',8001))
sk1.listen(5)
sk1.setblocking(False)
inputs = [sk1,]
outputs = []
while True:
readable_list, writeable_list, error_list = select.select(inputs,outputs,[],1)
#文件描述符可读 readable_list 只有变化的时候,感知
#文件描述符可写 writeable_list 只要有,感知
time.sleep(2)
print "inputs:",inputs
print "res:",readable_list
print "wri",writeable_list
for r in readable_list:
if r == sk1:
conn,addr = r.accept()
inputs.append(conn)
print addr
else:
client_data = r.recv(1024)
#如果接受到数据,则将数据添加到outputs,writeable则能感知
if client_data:
outputs.append(r)
for w in writeable_list:
#如果列表有数据,则给客户端发送一条
w.sendall('1234')
#循环列表时,outputs获取文件句柄,只要outputs有客户端文件句柄,每一次列表循环都能获取数据,即第一次写完之后,不在给客户端发送数据
outputs.remove(w)
Queue 队列
import Queue
q = Queue.Queue()
q.put(1)
q.put(2)
q.put(3)
print '第一个:',q.get()
print '第二个:',q.get()
执行结果:
import Queue
q = Queue.Queue()
q.put(1) #put数据到队列
q.put(2)
q.put(3)
print '第一个:',q.get() #队列是先进先出,这里get了两次,则分别取出了1,2
print '第二个:',q.get()
如果队列没有数据,这时get数据,则会等待
import Queue
q = Queue.Queue()
q.get()
q.put(1)
q.put(2)
q.put(3)
上面的方式没有结果:(处于等待,没有输出)
下面的例子,如果没有数据,通过 get_nowait(),不会等待,但是会报错
import Queue
q = Queue.Queue()
q.get_nowait()
q.get()
q.put(1)
q.put(2)
结果如下:(报错)
/usr/bin/python /Users/yangallen214/PycharmProjects/ob_11/day10/que_demo.py
Traceback (most recent call last):
File "/Users/yangallen214/PycharmProjects/ob_11/day10/que_demo.py", line 20, in <module>
q.get_nowait()
File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/Queue.py", line 190, in get_nowait
return self.get(False)
File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/Queue.py", line 165, in get
raise Empty
Queue.Empty
通过try获取异常:
import Queue
q = Queue.Queue()
try:
q.get_nowait()
q.get()
q.put(1)
q.put(2)
q.put(3)
except Queue.Empty:
print "err"
上面的put 时也可以使用 q.put_nowait() 方法,这里不多说
readable_list, writeable_list 读写拆分的示例
import select
import socket
import Queue
sk1 = socket.socket()
sk1.bind(('127.0.0.1',8001))
sk1.listen(5)
sk1.setblocking(False)
inputs = [sk1,]
outputs = []
message = {}
#message = {
# 'c1':队列,
# 'c2':队列,【b,bb,bbb】
#}
while True:
readable_list, writeable_list, error_list = select.select(inputs,outputs,[],1)
#文件描述符可读 readable_list 只有变化,感知
#文件描述符可写 writeable_list 只要有,感知
for r in readable_list:
if r == sk1:
conn,addr = r.accept()
inputs.append(conn)
message[conn] = Queue.Queue()
else:
client_data = r.recv(1024)
if client_data:
#获取数据
outputs.append(r)
#在指定队列中插入数据
message[r].put(client_data)
else:
inputs.remove(r)
#如果空队列则删除
del message[r]
for w in writeable_list:
#去指定队列取数据
try:
data = message[w].get_nowait()
w.sendall(data)
except Queue.Empty:
pass
outputs.remove(w)
#如果空队列则删除
#del message[w]
select、多路复用 先讲这么多,下次继续更新...
更多连接: http://www.cnblogs.com/wupeiqi
socket及其相关(续篇)的更多相关文章
- Java和C#的socket通信相关(转)
这几天在博客园上看到好几个写Java和C#的socket通信的帖子.但是都为指出其中关键点. C# socket通信组件有很多,在vs 使用nuget搜索socket组件有很多类似的.本人使用的是自己 ...
- socket编程相关的结构体和字节序转换、IP、PORT转换函数
注意:结构体之间不能直接进行强制转换, 必须先转换成指针类型才可以进行结构体间的类型转换, 这里需要明确的定义就是什么才叫强制转换. 强制转换是将内存中一段代码以另一种不同类型的方式进行解读, 因此转 ...
- socket的相关知识理解
http://blog.csdn.net/feiniu55662/article/details/16948639 https://www.baidu.com/baidu?tn=monline_3_d ...
- socket编程相关阐述
一.socket初识 ①服务端 import socket server = socket.socket() server.bind(('127.0.0.1', 8080)) server.liste ...
- 一只简单的网络爬虫(基于linux C/C++)————socket相关及HTTP
socket相关 建立连接 网络通信中少不了socket,该爬虫没有使用现成的一些库,而是自己封装了socket的相关操作,因为爬虫属于客户端,建立套接字和发起连接都封装在build_connect中 ...
- Socket
Socket又称"套接字",应用程序通常通过"套接字"向网络发出请求或者应答网络请求. 以J2SDK-1.3为例,Socket和ServerSocket类库位于 ...
- JAVA Socket 实现HTTP与HTTPS客户端发送POST与GET方式请求
JAVA Socket 实现HTTP与HTTPS客户端发送POST与GET方式请求 哇,一看标题怎么这么长啊,其实意思很简单,哥讨厌用HTTP Client做POST与GET提交 觉得那个毕竟是别人写 ...
- 使用socket.io打造公共聊天室
最近的计算机网络课上老师开始讲socket,tcp相关的知识,当时脑袋里就蹦出一个想法,那就是打造一个聊天室.实现方式也挺多的,常见的可以用C++或者Java进行socket编程来构建这么一个聊天室. ...
- python【第八篇】socket网络编程
内容大纲 1.socke基础 两个程序通过一个双向的通信连接实现数据的交换,这个连接的一端称为一个socket. 建 立网络通信连接至少要一对端口号(socket).socket本质是编程接口(API ...
随机推荐
- 比较两个date返回日期相差天数
public static int daydiff(Date fDate, Date oDate) { Calendar aCalendar = Calendar.getInstance(); aCa ...
- log4j与log4j.properties的配置
详见:http://blog.yemou.net/article/query/info/tytfjhfascvhzxcyt233 摘要: 一.配置步骤 1. 在应用程序中使用log4j 2. 把l ...
- 浅谈MVC
一.MVC的产生之源 MVC是Model.View.Controller这三个英文单词的缩写,从这三个单词的含义中我们可以看出MVC的核心是什么?显而易见,Model指的是模型,在某些地方你也可以理解 ...
- C# 引用类型之特例string
在C#编程的时候经常会使用字符串(string)类型,它也是引用类型,但是处处都不作为引用的用法来使用,实属特例,下来我一一罗列出来,供自己记忆方便: 1)字符串的直接赋值:本身字符串就是引用类型,应 ...
- 201521123122 《java程序设计》第七周学习总结
201521123122 <java程序设计>第七周实验总结 1. 本周学习总结 以你喜欢的方式(思维导图或其他)归纳总结集合相关内容. 2. 书面作业 ArrayList代码分析 1.1 ...
- 201521123097《Java程序设计》第三周学习总结
1. 本周学习总结 2. 书面作业 1.代码阅读 public class Test1 { private int i = 1;//这行不能修改 private static int j = 2; p ...
- SharePoint备份文件
stp文件:SharePoint的.stp文件 在做一个和SharePoint有关的项目时,由于对SharePoint的unfamiliar,所以客户发了几个后缀为.stp的文件将我纳闷了半天,不 ...
- Hyperledger Fabric 1.0 从零开始(五)——运行测试e2e
3:运行测试e2e 3.1.运行fabric-samples的问题说明 该问题说明能够解决6.1.平台特定使用的二进制文件配置第一步的问题.可以选择继续阅读该说明,或者等参考到6.1小节时再反向阅读本 ...
- 网络基础之IP地址与子网划分
IP地址 Ipv4地址格式:点分十进制 IP地址的分类 A类 B类 C类: D类:组播 E类: 公共IP地址 私有IP地址 特殊地址 保留地址 子网掩码 什么是子网掩码 CIDR表示法 子网划分 为啥 ...
- Spring-Hibernate-web的延迟加载方案
1,现象与问题 /** * 由于Hibernate存在延迟加载问题,当Dao事务提交之后,session就关闭: * 此时如果到显示层就没有办法获取对象,使用openSessionInViewer是解 ...