-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathserver.py
101 lines (96 loc) · 3.7 KB
/
server.py
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
# Server side
import socket
import select
import queue
# 创建socket对象
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 设置IP地址复用
server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
# ip地址和端口号
server_address = ("127.0.0.1", 8888)
# 绑定IP地址
server_socket.bind(server_address)
# 监听,并设置最大连接数
server_socket.listen(10)
print(
"服务器启动成功,监听IP:", server_address)
# 服务端设置非阻塞
server_socket.setblocking(False)
# 超时时间
timeout = 10
# 创建epoll事件对象,后续要监控的事件添加到其中
epoll = select.epoll()
# 注册服务器监听fd到等待读事件集合
epoll.register(server_socket.fileno(), select.EPOLLIN)
# 保存连接客户端消息的字典,格式为{}
message_queues = {}
# 文件句柄到所对应对象的字典,格式为{句柄:对象}
fd_to_socket = {server_socket.fileno(): server_socket, }
while True:
# print(
# "等待活动连接......")
# 轮询注册的事件集合,返回值为[(文件句柄,对应的事件),(...),....]
events = epoll.poll(timeout)
if not events:
print(
"epoll超时无活动连接,重新轮询......")
continue
# print(
# "有", len(events), "个新事件,开始处理......")
for fd, event in events:
socket = fd_to_socket[fd]
# 如果活动socket为当前服务器socket,表示有新连接
if socket == server_socket:
connection, address = server_socket.accept()
print(
"新连接:", address)
# 新连接socket设置为非阻塞
connection.setblocking(False)
# 注册新连接fd到待读事件集合
epoll.register(connection.fileno(), select.EPOLLIN)
# 把新连接的文件句柄以及对象保存到字典
fd_to_socket[connection.fileno()] = connection
# 以新连接的对象为键值,值存储在队列中,保存每个连接的信息
message_queues[connection] = queue.Queue()
# 关闭事件
elif event & select.EPOLLHUP:
print(
'client close')
# 在epoll中注销客户端的文件句柄
epoll.unregister(fd)
# 关闭客户端的文件句柄
fd_to_socket[fd].close()
# 在字典中删除与已关闭客户端相关的信息
del fd_to_socket[fd]
# 可读事件
elif event & select.EPOLLIN:
# 接收数据
data = socket.recv(1024)
if data:
print(
"收到数据:", data, "客户端:", socket.getpeername())
# 将数据放入对应客户端的字典
message_queues[socket].put(data)
# 修改读取到消息的连接到等待写事件集合(即对应客户端收到消息后,再将其fd修改并加入写事件集合)
epoll.modify(fd, select.EPOLLOUT)
# 可写事件
elif event & select.EPOLLOUT:
try:
# 从字典中获取对应客户端的信息
msg = message_queues[socket].get_nowait()
except queue.Empty:
print(
socket.getpeername(), " queue empty")
# 修改文件句柄为读事件
epoll.modify(fd, select.EPOLLIN)
else:
print(
"发送数据:", msg, "客户端:", socket.getpeername())
# 发送数据
socket.send(msg)
# 在epoll中注销服务端文件句柄
epoll.unregister(server_socket.fileno())
# 关闭epoll
epoll.close()
# 关闭服务器socket
server_socket.close()