作为 Python 的初学者,学习别人的代码也是必要过程,本文主要是针对 《 Python中使用epoll开发服务端程序 》中的代码,进行源码注释说明。从代码中至少可以学习到如下三点:
- logging 模块的使用;
- epoll 的使用;
- 如何利用 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
|
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]
#
|
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
|
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
|
============ 我是分割线 ==============