同步异步,阻塞和非阻塞以及select模块代码例子

同步,异步,阻塞和非阻塞
  1. 同步,就是我调用一个功能,该功能没有结束前,我死等结果。
  2. 异步,就是我调用一个功能,不需要知道该功能结果,该功能有结果后通知我(我有callback回调通知)
  3. 阻塞,就是调用我(函数),我(函数)没有接收完数据或者没有得到结果之前,我不会返回。
  4. 非阻塞,就是调用我(函数),我(函数)立即返回,通过select通知调用者
    同步IO和异步IO的区别就在于:数据拷贝的时候进程是否阻塞!
    阻塞IO和非阻塞IO的区别就在于:应用程序的调用是否立即返回!
    同步:提交请求->等待服务器处理->处理完毕返回这个期间客户端浏览器不能干任何事
    异步:请求通过事件触发->服务器处理(这是浏览器仍然可以作其他事情)->处理完毕

select实例代码

Code: Select Server

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
import select
import socket
import sys
import queue

# 创建一个tcp的socket对象
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

# 设置非阻塞
server.setblocking(False)

# 定义server对象主机ip和端口
server_address = ('localhost', 10000)
# 打印出“服务器服务端监听元素就是定义的主机ip和端口”
print(sys.stderr, 'starting up on %s port %s' % server_address)
# socket绑定监听
server.bind(server_address)

# 设置监听队列为5
server.listen(5)

# 接收client数据的对象数组-就是输入数组,把server的连接句柄append到数据中用于当客户端连接进来时,accept()
inputs = [ server ]

# 返回输出到clinet的对象数组
outputs = [ ]
# 消息队列
message_queues = {}
# 进入loop循环遍历,只有当inputs里面还有数据的情况下,才需要去处理socket,先进入处理readalbe 在进行处理writeable
while inputs:

# 持续打印打印输出消息
print( '\nwaiting for the next event')
# 把输入,输出,以及输入分别作为select.select()的参数进行传递 返回的是三个list,分别是输入,输出,和异常,timeout是2秒超时
# 把input作为exception参数交给select()方法用于判断客户端连接是否有异常
readable, writable, exceptional = select.select(inputs, outputs, inputs,2)
"""
readable,代表可读也就是socket连接有数据可以被读取,也就是说可以被recv
writeable,代表数据可写,就是有数据要被返回给客户端,也就是send
exceptional socket出现错误就把erro存放exceptional
"""


"""
readable中的socket有三种状态分别是,man server socket :代表server端已经ready来接受数据(新连接)
"""

for s in readable:

if s is server: # 新连接进来
# s.accept() 返回socket句柄,和客户端连接信息(地址和端口)
connection, client_address = s.accept()
# 打印出客户端的连接的地址和端口
print('new connection from', client_address)
# 设置新连接connection的socket句柄为非阻塞
connection.setblocking(False)
# 把新连接新增到inputs中,如果不存的话,就要立刻进行处理就会没有办法对下一个server进行处理
inputs.append(connection)

"""
为每个连接connection开辟一个队列,就是开辟一个缓存区,由于等一下要吧client发送过来的数据再send回去,这个queue就是为了接收客户端
发送过来的数据,等一下用send出去
"""

message_queues[connection] = queue.Queue()
else: # 不是新连接
# 使用recv接收数据
data = s.recv(1024)
if data: # 正确接收客户端传递数据
# A readable client socket has data
print(sys.stderr, 'received "%s" from %s' % (data, s.getpeername()) )
# 把数据放入连接专有的队列
message_queues[s].put(data)
# 如果连接还没有返回,就把连接放入到可写的列表,等一下将调用send进行发送给客户端,如果这里直接send返回就等于会阻塞了
if s not in outputs:
outputs.append(s)
else: # 接收客户端数据传递有异常或者没收到数据,关闭链接
# Interpret empty result as closed connection
print('closing', client_address, 'after reading no data')
# Stop listening for input on the connection
# 如果socket在outputs里面,那就需要先移除掉 因为等一下要关闭连接
if s in outputs:
outputs.remove(s) #既然客户端都断开了,我就不用再给它返回数据了,所以这时候如果这个客户端的连接对象还在outputs列表中,就把它删掉
inputs.remove(s) #inputs中也删除掉
s.close() #把这个连接关闭掉

# 前面有client connection 的队列也删除
del message_queues[s]
# 现在来处理需要send 的连接
for s in writable:
try:
# 从队列中不阻塞的方式获取数据
next_msg = message_queues[s].get_nowait()
except queue.Empty:
# 如果出现这个异常代表没有数据进行send 把连接从writeable中剔除
print('output queue for', s.getpeername(), 'is empty')
outputs.remove(s)
else: #从Queue中成功获取到数据了
print( 'sending "%s" to %s' % (next_msg, s.getpeername()))
s.send(next_msg) #调用send进行发送给客户端

# 最后是对异常的处理了,比如客户端断开连接
for s in exceptional:
print('handling exceptional condition for', s.getpeername() )
# 如果在一个socket句柄在异常list中,就要进行关闭,从inputs里面移除,从outputs里面异常,然后关闭连接,删除队列
inputs.remove(s)
if s in outputs:
outputs.remove(s)
s.close()

# Remove message queue
del message_queues[s]

Code: Select Clinet

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
import socket
import sys

# 将要发送给server的消息,
messages = [ 'This is the message. ',
'It will be sent ',
'in parts.',
]
# 服务端的连接信息定义
server_address = ('localhost', 10000)

# 创建socket对象 4个连接
socks = [ socket.socket(socket.AF_INET, socket.SOCK_STREAM),
socket.socket(socket.AF_INET, socket.SOCK_STREAM),
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(sys.stderr, 'connecting to %s port %s' % server_address)
# 先和服务端进行4个连接,
for s in socks:
s.connect(server_address)

# 给服务端发送消息
for message in messages:

# 循环客户端的连接池,进行发送
for s in socks:
print(sys.stderr, '%s: sending "%s"' % (s.getsockname(), message))
s.send(bytes(message,encoding='utf-8'))

# 接收服务端的返回
for s in socks:
data = s.recv(1024) #每次接收1024个字节
print(sys.stderr, '%s: received "%s"' % (s.getsockname(), data))
if not data: #服务端没有数据返回就断开连接
print(sys.stderr, 'closing socket', s.getsockname())
s.close()
文章目录
  1. 1. 同步,异步,阻塞和非阻塞
  2. 2. select实例代码