Python之基于socket和select模块实现IO多路复用

 

 
'''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学习笔记
'''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画的图

猜你喜欢

转载自www.cnblogs.com/xc1234/p/9129762.html