天天看点

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

logging 模块的使用;

epoll 的使用;

如何利用 try...catch 来处理“读穿”。

============ 我是分割线 ============== 

server端代码: 

<a href="http://my.oschina.net/moooofly/blog/147297#">?</a>

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

<code>#!/usr/bin/python</code>

<code>#-*- coding:utf-8 -*-</code>

<code>import</code> <code>socket, logging</code>

<code>import</code> <code>select, errno</code>

<code>    </code> 

<code>logger</code><code>=</code> <code>logging.getlogger(</code><code>"network-server"</code><code>)</code>

<code>def</code> <code>initlog():</code>

<code>    </code><code>logger.setlevel(logging.debug)</code>

<code>    </code><code>fh</code><code>=</code> <code>logging.filehandler(</code><code>"network-server.log"</code><code>)</code>

<code>    </code><code>fh.setlevel(logging.debug)</code>

<code>    </code><code>ch</code><code>=</code> <code>logging.streamhandler()</code>

<code>    </code><code>ch.setlevel(logging.error)</code>

<code>    </code><code>formatter</code><code>=</code> <code>logging.formatter(</code><code>"%(asctime)s - %(name)s - %(levelname)s - %(message)s"</code><code>)</code>

<code>    </code><code>ch.setformatter(formatter)</code>

<code>    </code><code>fh.setformatter(formatter)</code>

<code>    </code><code>logger.addhandler(fh)</code>

<code>    </code><code>logger.addhandler(ch)</code>

<code>        </code> 

<code>if</code> <code>__name__</code><code>=</code><code>=</code> <code>"__main__"</code><code>:</code>

<code>    </code><code>initlog()</code>

<code>    </code><code>try</code><code>:</code>

<code>        </code><code># 创建 tcp socket 作为监听 socket</code>

<code>        </code><code>listen_fd</code><code>=</code> <code>socket.socket(socket.af_inet, socket.sock_stream,</code><code>0</code><code>)</code>

<code>    </code><code>except</code> <code>socket.error, msg:</code>

<code>        </code><code>logger.error(</code><code>"create socket failed"</code><code>)</code>

<code>        </code><code># 设置 so_reuseaddr 选项</code>

<code>        </code><code>listen_fd.setsockopt(socket.sol_socket, socket.so_reuseaddr,</code><code>1</code><code>)</code>

<code>        </code><code>logger.error(</code><code>"setsocketopt so_reuseaddr failed"</code><code>)</code>

<code>        </code><code># 进行 bind -- 此处未指定 ip 地址,即 bind 了全部网卡 ip 上</code>

<code>        </code><code>listen_fd.bind(('',</code><code>2003</code><code>))</code>

<code>        </code><code>logger.error(</code><code>"bind failed"</code><code>)</code>

<code>        </code><code># 设置 listen 的 backlog 数</code>

<code>        </code><code>listen_fd.listen(</code><code>10</code><code>)</code>

<code>        </code><code>logger.error(msg)</code>

<code>        </code><code># 创建 epoll 句柄</code>

<code>        </code><code>epoll_fd</code><code>=</code> <code>select.epoll()</code>

<code>        </code><code># 向 epoll 句柄中注册 监听 socket 的 可读 事件</code>

<code>        </code><code>epoll_fd.register(listen_fd.fileno(), select.epollin)</code>

<code>    </code><code>except</code> <code>select.error, msg:</code>

<code>    </code><code>connections</code><code>=</code> <code>{}</code>

<code>    </code><code>addresses</code><code>=</code> <code>{}</code>

<code>    </code><code>datalist</code><code>=</code> <code>{}</code>

<code>    </code><code>while</code> <code>true</code><code>:</code>

<code>        </code><code># epoll 进行 fd 扫描的地方 -- 未指定超时时间则为阻塞等待</code>

<code>        </code><code>epoll_list</code><code>=</code> <code>epoll_fd.poll()</code>

<code>        </code><code>for</code> <code>fd, events</code><code>in</code> <code>epoll_list:</code>

<code>            </code><code># 若为监听 fd 被激活</code>

<code>            </code><code>if</code> <code>fd</code><code>=</code><code>=</code> <code>listen_fd.fileno():</code>

<code>                </code><code># 进行 accept -- 获得连接上来 client 的 ip 和 port,以及 socket 句柄</code>

<code>                </code><code>conn, addr</code><code>=</code> <code>listen_fd.accept()</code>

<code>                </code><code>logger.debug(</code><code>"accept connection from %s, %d, fd = %d"</code> <code>%</code> <code>(addr[</code><code>0</code><code>], addr[</code><code>1</code><code>], conn.fileno()))</code>

<code>                </code><code># 将连接 socket 设置为 非阻塞</code>

<code>                </code><code>conn.setblocking(</code><code>0</code><code>)</code>

<code>                </code><code># 向 epoll 句柄中注册 连接 socket 的 可读 事件</code>

<code>                </code><code>epoll_fd.register(conn.fileno(), select.epollin | select.epollet)</code>

<code>                </code><code># 将 conn 和 addr 信息分别保存起来</code>

<code>                </code><code>connections[conn.fileno()]</code><code>=</code> <code>conn</code>

<code>                </code><code>addresses[conn.fileno()]</code><code>=</code> <code>addr</code>

<code>            </code><code>elif</code> <code>select.epollin &amp; events:</code>

<code>                </code><code># 有 可读 事件激活</code>

<code>                </code><code>datas</code><code>=</code> <code>''</code>

<code>                </code><code>while</code> <code>true</code><code>:</code>

<code>                    </code><code>try</code><code>:</code>

<code>                        </code><code># 从激活 fd 上 recv 10 字节数据</code>

<code>                        </code><code>data</code><code>=</code> <code>connections[fd].recv(</code><code>10</code><code>)</code>

<code>                        </code><code># 若当前没有接收到数据,并且之前的累计数据也没有</code>

<code>                        </code><code>if</code> <code>not</code> <code>data</code><code>and</code> <code>not</code> <code>datas:</code>

<code>                            </code><code># 从 epoll 句柄中移除该 连接 fd</code>

<code>                            </code><code>epoll_fd.unregister(fd)</code>

<code>                            </code><code># server 侧主动关闭该 连接 fd</code>

<code>                            </code><code>connections[fd].close()</code>

<code>                            </code><code>logger.debug(</code><code>"%s, %d closed"</code> <code>%</code> <code>(addresses[fd][</code><code>0</code><code>], addresses[fd][</code><code>1</code><code>]))</code>

<code>                            </code><code>break</code>

<code>                        </code><code>else</code><code>:</code>

<code>                            </code><code># 将接收到的数据拼接保存在 datas 中</code>

<code>                            </code><code>datas</code><code>+</code><code>=</code> <code>data</code>

<code>                    </code><code>except</code> <code>socket.error, msg:</code>

<code>                        </code><code># 在 非阻塞 socket 上进行 recv 需要处理 读穿 的情况</code>

<code>                        </code><code># 这里实际上是利用 读穿 出 异常 的方式跳到这里进行后续处理</code>

<code>                        </code><code>if</code> <code>msg.errno</code><code>=</code><code>=</code> <code>errno.eagain:</code>

<code>                            </code><code>logger.debug(</code><code>"%s receive %s"</code> <code>%</code> <code>(fd, datas))</code>

<code>                            </code><code># 将已接收数据保存起来</code>

<code>                            </code><code>datalist[fd]</code><code>=</code> <code>datas</code>

<code>                            </code><code># 更新 epoll 句柄中连接d 注册事件为 可写</code>

<code>                            </code><code>epoll_fd.modify(fd, select.epollet | select.epollout)</code>

<code>                            </code><code># 出错处理</code>

<code>                            </code><code>logger.error(msg)</code>

<code>            </code><code>elif</code> <code>select.epollhup &amp; events:</code>

<code>                </code><code># 有 hup 事件激活</code>

<code>                </code><code>epoll_fd.unregister(fd)</code>

<code>                </code><code>connections[fd].close()</code>

<code>                </code><code>logger.debug(</code><code>"%s, %d closed"</code> <code>%</code> <code>(addresses[fd][</code><code>0</code><code>], addresses[fd][</code><code>1</code><code>]))</code>

<code>            </code><code>elif</code> <code>select.epollout &amp; events:</code>

<code>                </code><code># 有 可写 事件激活</code>

<code>                </code><code>sendlen</code><code>=</code> <code>0</code>

<code>                </code><code># 通过 while 循环确保将 buf 中的数据全部发送出去       </code>

<code>                    </code><code># 将之前收到的数据发回 client -- 通过 sendlen 来控制发送位置</code>

<code>                    </code><code>sendlen</code><code>+</code><code>=</code> <code>connections[fd].send(datalist[fd][sendlen:])</code>

<code>                    </code><code># 在全部发送完毕后退出 while 循环</code>

<code>                    </code><code>if</code> <code>sendlen</code><code>=</code><code>=</code> <code>len</code><code>(datalist[fd]):</code>

<code>                        </code><code>break</code>

<code>                </code><code># 更新 epoll 句柄中连接 fd 注册事件为 可读</code>

<code>                </code><code>epoll_fd.modify(fd, select.epollin | select.epollet)</code>

<code>            </code><code>else</code><code>:</code>

<code>                </code><code># 其他 epoll 事件不进行处理</code>

<code>                </code><code>continue</code>

client 端代码(比较简单,未加注释): 

<code>import</code> <code>socket</code>

<code>import</code> <code>time</code>

<code>import</code> <code>logging</code>

<code>logger</code><code>=</code> <code>logging.getlogger(</code><code>"network-client"</code><code>)</code>

<code>logger.setlevel(logging.debug)</code>

<code>fh</code><code>=</code> <code>logging.filehandler(</code><code>"network-client.log"</code><code>)</code>

<code>fh.setlevel(logging.debug)</code>

<code>ch</code><code>=</code> <code>logging.streamhandler()</code>

<code>ch.setlevel(logging.error)</code>

<code>formatter</code><code>=</code> <code>logging.formatter(</code><code>"%(asctime)s - %(name)s - %(levelname)s - %(message)s"</code><code>)</code>

<code>ch.setformatter(formatter)</code>

<code>fh.setformatter(formatter)</code>

<code>logger.addhandler(fh)</code>

<code>logger.addhandler(ch)</code>

<code>        </code><code>connfd</code><code>=</code> <code>socket.socket(socket.af_inet, socket.sock_stream,</code><code>0</code><code>)</code>

<code>        </code><code>connfd.connect((</code><code>"127.0.0.1"</code><code>,</code><code>2003</code><code>))</code>

<code>        </code><code>logger.debug(</code><code>"connect to network server success"</code><code>)</code>

<code>    </code><code>except</code> <code>socket.error,msg:</code>

<code>    </code><code>for</code> <code>i</code><code>in</code> <code>range</code><code>(</code><code>1</code><code>,</code><code>11</code><code>):</code>

<code>        </code><code>data</code><code>=</code> <code>"the number is %d"</code> <code>%</code> <code>i</code>

<code>        </code><code>if</code> <code>connfd.send(data) !</code><code>=</code> <code>len</code><code>(data):</code>

<code>            </code><code>logger.error(</code><code>"send data to network server failed"</code><code>)</code>

<code>            </code><code>break</code>

<code>        </code><code>readdata</code><code>=</code> <code>connfd.recv(</code><code>1024</code><code>)</code>

<code>        </code><code>print</code> <code>readdata</code>

<code>        </code><code>time.sleep(</code><code>1</code><code>)</code>

<code>    </code><code>connfd.close()</code>

运行结果: 

client 命令行输出。 

<code>[root@betty python]</code><code># python epoll_client.py</code>

<code>the number is 1</code>

<code>the number is 2</code>

<code>the number is 3</code>

<code>the number is 4</code>

<code>the number is 5</code>

<code>the number is 6</code>

<code>the number is 7</code>

<code>the number is 8</code>

<code>the number is 9</code>

<code>the number is 10</code>

<code>[root@betty python]</code><code>#</code>

client 日志文件。 

<code>[root@betty python]</code><code># vi network-client.log</code>

<code>2013-07-26 15:36:43,995 - network-client - debug - connect to network server success</code>

server 日志文件。 

<code>[root@betty python]</code><code># vi network-server.log</code>

<code>2013-07-26 15:36:43,995 - network-server - debug - accept connection from 127.0.0.1, 34861, fd = 6</code>

<code>2013-07-26 15:36:43,995 - network-server - debug - 6 receive the number is 1</code>

<code>2013-07-26 15:36:44,996 - network-server - debug - 6 receive the number is 2</code>

<code>2013-07-26 15:36:45,996 - network-server - debug - 6 receive the number is 3</code>

<code>2013-07-26 15:36:46,996 - network-server - debug - 6 receive the number is 4</code>

<code>2013-07-26 15:36:47,996 - network-server - debug - 6 receive the number is 5</code>

<code>2013-07-26 15:36:48,997 - network-server - debug - 6 receive the number is 6</code>

<code>2013-07-26 15:36:49,998 - network-server - debug - 6 receive the number is 7</code>

<code>2013-07-26 15:36:50,999 - network-server - debug - 6 receive the number is 8</code>

<code>2013-07-26 15:36:52,000 - network-server - debug - 6 receive the number is 9</code>

<code>2013-07-26 15:36:53,001 - network-server - debug - 6 receive the number is 10</code>

<code>2013-07-26 15:36:54,002 - network-server - debug - 127.0.0.1, 34861 closed</code>