【资料整理】Python - 简单的epoll server代码解读

深入解析Python中epoll开发服务端程序的代码实践
本文详细解读了Python中使用epoll开发服务端程序的源码,包括logging模块的使用、epoll的实现及处理‘读穿’的技巧。通过示例代码,学习者能掌握如何在服务端程序中高效地处理并发连接。

      作为 Python 的初学者,学习别人的代码也是必要过程,本文主要是针对 《   Python中使用epoll开发服务端程序   》中的代码,进行源码注释说明。从代码中至少可以学习到如下三点:
  1. logging 模块的使用;
  2. epoll 的使用;
  3. 如何利用 try...catch 来处理“读穿”。
============ 我是分割线 ==============  

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
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
#!/usr/bin/python
#-*- coding:utf-8 -*-
 
import socket, logging
import select, errno
     
logger = logging.getLogger( "network-server" )
     
def InitLog():
     logger.setLevel(logging.DEBUG)
     
     fh = logging.FileHandler( "network-server.log" )
     fh.setLevel(logging.DEBUG)
     ch = logging.StreamHandler()
     ch.setLevel(logging.ERROR)
     
     formatter = logging.Formatter( "%(asctime)s - %(name)s - %(levelname)s - %(message)s" )
     ch.setFormatter(formatter)
     fh.setFormatter(formatter)
     
     logger.addHandler(fh)
     logger.addHandler(ch)
         
         
if __name__ = = "__main__" :
     InitLog()
 
     try :
         # 创建 TCP socket 作为监听 socket
         listen_fd = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0 )
     except socket.error, msg:
         logger.error( "create socket failed" )
 
     try :
         # 设置 SO_REUSEADDR 选项
         listen_fd.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1 )
     except socket.error, msg:
         logger.error( "setsocketopt SO_REUSEADDR failed" )
 
     try :
         # 进行 bind -- 此处未指定 ip 地址,即 bind 了全部网卡 ip 上
         listen_fd.bind(('', 2003 ))
     except socket.error, msg:
         logger.error( "bind failed" )
 
     try :
         # 设置 listen 的 backlog 数
         listen_fd.listen( 10 )
     except socket.error, msg:
         logger.error(msg)
     
     try :
         # 创建 epoll 句柄
         epoll_fd = select.epoll()
         # 向 epoll 句柄中注册 监听 socket 的 可读 事件
         epoll_fd.register(listen_fd.fileno(), select.EPOLLIN)
     except select.error, msg:
         logger.error(msg)
         
     connections = {}
     addresses = {}
     datalist = {}
     while True :
         # epoll 进行 fd 扫描的地方 -- 未指定超时时间则为阻塞等待
         epoll_list = epoll_fd.poll()
 
         for fd, events in epoll_list:
             # 若为监听 fd 被激活
             if fd = = listen_fd.fileno():
                 # 进行 accept -- 获得连接上来 client 的 ip 和 port,以及 socket 句柄
                 conn, addr = listen_fd.accept()
                 logger.debug( "accept connection from %s, %d, fd = %d" % (addr[ 0 ], addr[ 1 ], conn.fileno()))
                 # 将连接 socket 设置为 非阻塞
                 conn.setblocking( 0 )
                 # 向 epoll 句柄中注册 连接 socket 的 可读 事件
                 epoll_fd.register(conn.fileno(), select.EPOLLIN | select.EPOLLET)
                 # 将 conn 和 addr 信息分别保存起来
                 connections[conn.fileno()] = conn
                 addresses[conn.fileno()] = addr
             elif select.EPOLLIN & events:
                 # 有 可读 事件激活
                 datas = ''
                 while True :
                     try :
                         # 从激活 fd 上 recv 10 字节数据
                         data = connections[fd].recv( 10 )
                         # 若当前没有接收到数据,并且之前的累计数据也没有
                         if not data and not datas:
                             # 从 epoll 句柄中移除该 连接 fd
                             epoll_fd.unregister(fd)
                             # server 侧主动关闭该 连接 fd
                             connections[fd].close()
                             logger.debug( "%s, %d closed" % (addresses[fd][ 0 ], addresses[fd][ 1 ]))
                             break
                         else :
                             # 将接收到的数据拼接保存在 datas 中
                             datas + = data
                     except socket.error, msg:
                         # 在 非阻塞 socket 上进行 recv 需要处理 读穿 的情况
                         # 这里实际上是利用 读穿 出 异常 的方式跳到这里进行后续处理
                         if msg.errno = = errno.EAGAIN:
                             logger.debug( "%s receive %s" % (fd, datas))
                             # 将已接收数据保存起来
                             datalist[fd] = datas
                             # 更新 epoll 句柄中连接d 注册事件为 可写
                             epoll_fd.modify(fd, select.EPOLLET | select.EPOLLOUT)
                             break
                         else :
                             # 出错处理
                             epoll_fd.unregister(fd)
                             connections[fd].close()
                             logger.error(msg)
                             break
             elif select.EPOLLHUP & events:
                 # 有 HUP 事件激活
                 epoll_fd.unregister(fd)
                 connections[fd].close()
                 logger.debug( "%s, %d closed" % (addresses[fd][ 0 ], addresses[fd][ 1 ]))
             elif select.EPOLLOUT & events:
                 # 有 可写 事件激活
                 sendLen = 0
                 # 通过 while 循环确保将 buf 中的数据全部发送出去       
                 while True :
                     # 将之前收到的数据发回 client -- 通过 sendLen 来控制发送位置
                     sendLen + = connections[fd].send(datalist[fd][sendLen:])
                     # 在全部发送完毕后退出 while 循环
                     if sendLen = = len (datalist[fd]):
                         break
                 # 更新 epoll 句柄中连接 fd 注册事件为 可读
                 epoll_fd.modify(fd, select.EPOLLIN | select.EPOLLET)
             else :
                 # 其他 epoll 事件不进行处理
                 continue
client 端代码(比较简单,未加注释):  
?
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
import socket
import time
import logging
 
logger = logging.getLogger( "network-client" )
logger.setLevel(logging.DEBUG)
 
fh = logging.FileHandler( "network-client.log" )
fh.setLevel(logging.DEBUG)
ch = logging.StreamHandler()
ch.setLevel(logging.ERROR)
 
formatter = logging.Formatter( "%(asctime)s - %(name)s - %(levelname)s - %(message)s" )
ch.setFormatter(formatter)
fh.setFormatter(formatter)
 
logger.addHandler(fh)
logger.addHandler(ch)
 
if __name__ = = "__main__" :
     try :
         connFd = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0 )
     except socket.error, msg:
         logger.error(msg)
 
     try :
         connFd.connect(( "127.0.0.1" , 2003 ))
         logger.debug( "connect to network server success" )
     except socket.error,msg:
         logger.error(msg)
 
     for i in range ( 1 , 11 ):
         data = "The Number is %d" % i
         if connFd.send(data) ! = len (data):
             logger.error( "send data to network server failed" )
             break
         readData = connFd.recv( 1024 )
         print readData
         time.sleep( 1 )
 
     connFd.close()
运行结果:  

client 命令行输出。  
?
1
2
3
4
5
6
7
8
9
10
11
12
[root@Betty Python] # python epoll_client.py
The Number is 1
The Number is 2
The Number is 3
The Number is 4
The Number is 5
The Number is 6
The Number is 7
The Number is 8
The Number is 9
The Number is 10
[root@Betty Python] #
client 日志文件。  
?
1
2
3
[root@Betty Python] # vi network-client.log
 
2013-07-26 15:36:43,995 - network-client - DEBUG - connect to network server success
server 日志文件。  
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
[root@Betty Python] # vi network-server.log
 
2013-07-26 15:36:43,995 - network-server - DEBUG - accept connection from 127.0.0.1, 34861, fd = 6
2013-07-26 15:36:43,995 - network-server - DEBUG - 6 receive The Number is 1
2013-07-26 15:36:44,996 - network-server - DEBUG - 6 receive The Number is 2
2013-07-26 15:36:45,996 - network-server - DEBUG - 6 receive The Number is 3
2013-07-26 15:36:46,996 - network-server - DEBUG - 6 receive The Number is 4
2013-07-26 15:36:47,996 - network-server - DEBUG - 6 receive The Number is 5
2013-07-26 15:36:48,997 - network-server - DEBUG - 6 receive The Number is 6
2013-07-26 15:36:49,998 - network-server - DEBUG - 6 receive The Number is 7
2013-07-26 15:36:50,999 - network-server - DEBUG - 6 receive The Number is 8
2013-07-26 15:36:52,000 - network-server - DEBUG - 6 receive The Number is 9
2013-07-26 15:36:53,001 - network-server - DEBUG - 6 receive The Number is 10
2013-07-26 15:36:54,002 - network-server - DEBUG - 127.0.0.1, 34861 closed

============ 我是分割线 ==============  
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值