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 ...
随机推荐
- Chome——扩展程序,获取更多扩展程序报错
修改/替换hosts文件 地址:c:/windows/system32/drivers/etc hosts:可从网上搜索下载或网盘下载(链接: http://pan.baidu.com/s/1bpu6 ...
- kappa系数在评测中的应用
◆版权声明:本文出自胖喵~的博客,转载必须注明出处. 转载请注明出处:http://www.cnblogs.com/by-dream/p/7091315.html 前言 最近打算把翻译质量的人工评测好 ...
- Maven 中央仓库及阿里云仓库地址
Maven 中央仓库地址: 1. http://www.sonatype.org/nexus/ 2. http://mvnrepository.com/ 3. http://repo1.maven.o ...
- MySQL的JOIN(二):JOIN原理
表连接算法 Nested Loop Join(NLJ)算法: 首先介绍一种基础算法:NLJ,嵌套循环算法.循环外层是驱动表,循坏内层是被驱动表.驱动表会驱动被驱动表进行连接操作.首先驱动表找到第一条记 ...
- sql执行机制
详见:http://blog.yemou.net/article/query/info/tytfjhfascvhzxcytp38 sql执行机制 1.对于普通的sql语句只有where条件的执行机制 ...
- 对于c语言存储分配程序(malloc函数)实现的理解
内容主要出自<The C Programming Language>一书,不得不说这是一本程序员必读的书,我大二读了前面几章就扔到一边了,直到最近才又拿起来再读,找不到言语来形容我现在后悔 ...
- [我所理解的REST] 3.基于网络应用的架构
上篇中解释到什么是架构风格和应该以怎样的视角来理解REST(Web的架构风格).本篇来介绍一组自洽的术语,用它来描述和解释软件架构:以及列举下对于基于网络的应用来说,哪些点是需要我们重点关注的. 1 ...
- 命令导入导出oracle库
目前还是新手:所以记录下来最笨的方式,留用 一.从服务器先把库导出来 exp sys/mima@orcl file = "d:\pybghs.dmp" full=y 二.从服 ...
- python之面向对象2
一.类命名空间与对象.实例的命名空间 常见一个类就会创建一个类的名称空间,用来储存类中定义的所有名字,这些名字成为类的属性 而类有两种属性:静态属性和动态属性 静态属性就是直接在类中定义的变量 ...
- java向前引用
根据看书和看得文章,引出了一个关于"向前引用"的问题: public class InstanceInitTest { static { // { a = 6; System.ou ...