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 & 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 & 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 & 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>