在《UNIX網絡編程 卷1》這一節 中,我們曾用C語言實現了一個使用epoll的TCP回顯服務器程序。現在我們用 Python 來實現同樣的功能,代碼如下:
# -*- coding: utf-8 -*-
import os
import socket
import select
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.setblocking(False)
s.bind(('127.0.0.1', 22222))
s.listen(5)
sockets = {}
epoll = select.epoll()
epoll.register(s.fileno(), select.EPOLLIN)
while True:
events = epoll.poll()
for fd, event in events:
if fd == s.fileno(): # 新的連接請求
c, addr = s.accept()
c.setblocking(False) # 非阻塞
sockets[c.fileno()] = c
epoll.register(c.fileno(), select.EPOLLIN) # 水平觸發的方式
else: # 新的數據
c = sockets[fd]
close = False
try:
data = c.recv(1024)
except socket.error:
close = True
if data:
try:
c.send(data)
except socket.error:
close = True
else:
close = True
if close: # socket上出現錯誤或對端正常關閉連接時,關閉socket
epoll.unregister(fd)
del sockets[fd]
c.close()
注意,上述實現使用的是epoll的水平觸發方式,如果要使用epoll的邊沿觸發方式,則代碼如下:
# -*- coding: utf-8 -*-
import os
import errno
import socket
import select
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.setblocking(False)
s.bind(('127.0.0.1', 22222))
s.listen(5)
sockets = {}
epoll = select.epoll()
epoll.register(s.fileno(), select.EPOLLIN)
while True:
events = epoll.poll()
for fd, event in events:
if fd == s.fileno(): # 新的連接請求
c, addr = s.accept()
c.setblocking(False) # 非阻塞
sockets[c.fileno()] = c
epoll.register(c.fileno(), select.EPOLLIN | select.EPOLLET) # 邊沿觸發的方式
else: # 新的數據
c = sockets[fd]
close = False
while True:
try:
data = c.recv(1024)
except socket.error, err:
if err[0] != errno.EAGAIN:
close = True
break
if data:
try:
c.send(data)
except socket.error, err:
if err[0] != errno.EAGAIN:
close = True
break
else:
close = True
break
if close: # socket上出現錯誤或對端正常關閉連接時,關閉socket
epoll.unregister(fd)
del sockets[fd]
c.close()
上述的代碼只是個參考,還有一些細節問題沒有考慮。比如,send
發送數據時,如果返回EAGAIN
,正確的情形下該如何處理?