'''IO指的是输入输出,一部分指的是文件操作,还有一部分
网络传输操作,例如soekct就是其中之一;多路复用指的是
利用一种机制,同时使用多个IO,例如同时监听多个文件句
柄(socket对象一旦传送或者接收信息),一旦文件句柄出
现变化就会立刻感知到
'''
1、下面通过IO多路复用实现多人同时连接socket服务器
这是服务端代码
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
|
import
socket
sk1
=
socket.socket()
#sk1,sk2,sk3这就是一个文件描述符
sk1.bind((
'127.0.0.1'
,
8002
))
sk1.listen()
sk2
=
socket.socket()
sk2.bind((
'127.0.0.1'
,
8003
))
sk2.listen()
sk3
=
socket.socket()
sk3.bind((
'127.0.0.1'
,
8004
))
sk3.listen()
inputs
=
[sk1,sk2,sk3]
import
select
while
True
:
'''[sk1,sk2,sk3],select内部会自动监听sk1,sk21,sk3三个对象
一旦某个句柄发生变化就会被监听到
'''
#如果有人链接sk1,则会被添加进列表,r_list = [sk1]
r_list,w_list,e_list
=
select.select(inputs,[],[],
1
)
#1表示等一秒,在while执行到这里的时候监测一秒,没有人来链接的话就接着循环
#print(r_list,sk1.listen())
for
sk
in
r_list:
conn,address
=
sk.accept()
#print(conn,address)
conn.sendall(bytes(
'你好'
,encoding
=
'utf-8'
))
conn.close()
|
客户端的代码都是一样的,就差个端口
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
|
#客户端一
import
socket
obj
=
socket.socket()
obj.connect((
'127.0.0.1'
,
8002
))
content
=
str
(obj.recv(
1024
),encoding
=
'utf-8'
)
print
(content)
obj.close()
#客户端二
import
socket
obj
=
socket.socket()
obj.connect((
'127.0.0.1'
,
8003
))
content
=
str
(obj.recv(
1024
),encoding
=
'utf-8'
)
print
(content)
obj.close()
#客户端三
import
socket
obj
=
socket.socket()
obj.connect((
'127.0.0.1'
,
8004
))
content
=
str
(obj.recv(
1024
),encoding
=
'utf-8'
)
print
(content)
obj.close()
|
执行结果:只要启动服务器端,然后不同的客户端多次启动都能收到信息,多个端口成功被监听
2、下面使用select模块实现多路复用,使同一个端口同时接收多个链接
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
|
import
socket
sk1
=
socket.socket()
#sk1,sk2,sk3这就是一个文件描述符
sk1.bind((
'127.0.0.1'
,
8002
))
sk1.listen()
#
# sk2 = socket.socket()
# sk2.bind(('127.0.0.1',8003))
# sk2.listen()
#
# sk3 = socket.socket()
# sk3.bind(('127.0.0.1',8004))
# sk3.listen()
inputs
=
[sk1]
import
select
#epoll效率更高,但是Windows不支持,它是谁有问题就告诉它,不用循坏
while
True
:
'''[sk1,sk2,sk3],select内部会自动监听sk1,sk21,sk3三个对象
一旦某个句柄发生变化(某人来链接)就会被监听到
'''
#如果有人链接sk1,则会被添加进列表,r_list = [sk1]
r_list,w_list,e_list
=
select.select(inputs,[],[],
1
)
'''第三个参数是监听错误的,只要有错误出现,就会被监听到,返回e_list
第二个参数返回给w_list,只要传了什么,就原封不动的传给w_list'''
print
(
'正在监听 %s 多少个对象'
%
len
(inputs))
for
sk
in
r_list:
if
sk
=
=
sk1:
#句柄跟服务器端的对象一样,表示有新用户来链接了
conn,address
=
sk.accept()
inputs.append(conn)
#加入去之后,inputs有一个链接对象和服务器对象
else
:
#有老用户发消息了
try
:
data_byte
=
sk.recv(
1024
)
data_str
=
str
(data_byte,encoding
=
'utf-8'
)
sk.sendall(bytes(data_str
+
'收到了'
,encoding
=
'utf-8'
))
except
:
#空信息表示客户端断开链接,所以要在监听中移除
inputs.remove(sk)
#这里的sk就是之前传进去的conn,因为r_list接收的是有变化的值
|
启动这个服务端之后,就可以实现多路复用了,可以接收多个客户端同时连接
3、下面介绍一些多路操作里面的读写分离
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
|
import
socket
sk1
=
socket.socket()
#sk1就是一个文件描述符
sk1.bind((
'127.0.0.1'
,
8002
))
sk1.listen()
inputs
=
[sk1]
#被检测发生变动的句柄放这里
outputs
=
[]
#用来记录谁给服务端发过信息,以便回复
message_dict
=
{}
#接收信息的,根据句柄形成键值对
import
select
#epoll效率更高,但是Windows不支持,它是谁有问题就告诉它,不用循坏
while
True
:
'''[sk1,sk2,sk3],select内部会自动监听sk1,sk21,sk3三个对象
一旦某个句柄发生变化(某人来链接)就会被监听到
'''
#如果有人链接sk1,则会被添加进列表,r_list = [sk1]
r_list,w_list,e_list
=
select.select(inputs,outputs,inputs,
1
)
'''第三个参数是监听错误的,只要有错误出现,就会被监听到,返回e_list
第二个参数返回给w_list,只要传了什么,就原封不动的传给w_list'''
print
(
'正在监听 %s 多少个对象'
%
len
(inputs))
for
sk
in
r_list:
if
sk
=
=
sk1:
#句柄跟服务器端的对象一样,表示有新用户来链接了
conn,address
=
sk.accept()
inputs.append(conn)
#加入去之后,inputs有一个链接对象和服务器对象
message_dict[conn]
=
[]
#字典的key是具体的链接对象
else
:
#有老用户发消息了
try
:
data_byte
=
sk.recv(
1024
)
except
Exception as e:
#空信息表示客户端断开链接,所以要在监听中移除
print
(e)
inputs.remove(sk)
#这里的sk就是之前传进去的conn,因为r_list接收的是有变化的值
else
:
data_str
=
str
(data_byte, encoding
=
'utf-8'
)
message_dict[sk].append(data_str)
outputs.append(sk)
#把传送数据的句柄添加进去第二个参数
for
conn
in
w_list:
recv_str
=
message_dict[conn][
0
]
#拿到我们收到了的信息
del
message_dict[conn][
0
]
#删除信息,防止下次出现一样的消息
print
(recv_str)
conn.sendall(bytes(recv_str
+
'收到了'
,encoding
=
'utf-8'
))
outputs.remove(conn)
#回复完成之后在列表删除
for
sk_or_conn
in
e_list:
e_list.remove(sk_or_conn)
|
这样可以形成简单的读写分离操作
对于select里面的参数关系,这里有个武sir画的图
分类:
Python学习笔记