-
Notifications
You must be signed in to change notification settings - Fork 5
/
Copy pathserver.py
167 lines (144 loc) · 4.11 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
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
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
#!/usr/bin/python
# -*- coding=utf-8 -*-
__author__ = ['"wuyadong" <[email protected]>']
import socket
from poll import SelectPoll, Poll
from client import CloseClientException
from http import HttpClient
from util import except_info, console_logger
class Server(object):
def __init__(self, POLL_CLASS=SelectPoll, CLIENT_CLASS=HttpClient,
logger=console_logger, LISTEN_COUNT=10, MAX_CLIENTS=100):
# settings
self.POLL_CLASS = POLL_CLASS
self.CLIENT_CLASS = CLIENT_CLASS
self.LISTEN_COUNT = LISTEN_COUNT
self.MAX_CLIENTS = MAX_CLIENTS
self.server_sock = None
self.poll = self.POLL_CLASS()
self.logger = logger
self.clients = {}
self.logger.info("init server\nPOLL_CLASS:%s\nCLIENT_CLASS:%s\n"
"LISTEN_COUNT:%s\nMAX_CLIENT:%s\n"
% (POLL_CLASS, CLIENT_CLASS, LISTEN_COUNT, MAX_CLIENTS))
def start(self, address):
try:
# server socket
self.server_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.server_sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
self.server_sock.setblocking(0)
self.server_sock.bind(address)
self.server_sock.listen(self.LISTEN_COUNT)
self.poll.register(self.server_sock.fileno(), Poll.READ | Poll.ERROR)
self.logger.debug("info:\nserver_sock:%d" % (self.server_sock.fileno()))
# loop
self.loop()
except:
self.logger.error(except_info())
# close socket
self._clean()
try:
self.poll.unregister(self.server_sock.fileno())
self.server_sock.close()
except:
# ignore
pass
self.server_sock = None
def loop(self):
"""dispatch socket to handler
:return:
"""
while True:
try:
events = self.poll.poll(0.01)
for fd, event in events:
# connect
if fd == self.server_sock.fileno():
if event & Poll.ERROR:
self.logger.debug("fd:%d event:ERROR" % fd)
self.logger.error("server socket error")
break
elif event & Poll.READ:
self.logger.debug("fd:%d event:READ" % fd)
self._connect()
elif event & Poll.READ:
self.logger.debug("fd:%d event:READ" % fd)
self._read(fd)
elif event & Poll.WRITE:
self.logger.debug("fd:%d event:WRITE" % fd)
self._write(fd)
elif event & Poll.ERROR:
self.logger.debug("fd:%d event:ERROR" % fd)
if fd in self.clients:
self.clients[fd].close()
del self.clients[fd]
self._update(fd)
except:
self.logger.error(except_info())
break
def _connect(self):
"""build client, and register
:return:
"""
connection, address = self.server_sock.accept()
connection.setblocking(0)
self.logger.debug("client sock:%d" % connection.fileno())
self.logger.info("Peer [%s:%s] connected" % (address[0], address[1]))
self.clients[connection.fileno()] = self.CLIENT_CLASS(connection)
self.poll.register(connection.fileno(), Poll.READ | Poll.WRITE | Poll.ERROR)
def _read(self, fd):
"""
:param fd:
:return:
"""
client = self.clients.get(fd, None)
if client is not None:
try:
client.read()
except CloseClientException:
self.poll.unregister(fd)
self.clients[fd]
client.close()
except:
self.logger.warn("one client read error: %s" % except_info())
self.poll.unregister(fd)
del self.clients[fd]
client.close()
elif fd in self.clients:
del self.clients[fd]
def _write(self, fd):
"""write
:param fd:
:return:
"""
client = self.clients.get(fd, None)
if client is not None:
try:
client.write()
except:
self.logger.warn("one client write error: %s" % except_info())
self.poll.unregister(fd)
del self.clients[fd]
client.close()
elif fd in self.clients:
del self.clients[fd]
def _update(self, fd):
client = self.clients.get(fd, None)
if client is not None:
try:
client.process()
except:
self.logger.warn("one client update error: %s" % except_info())
self.poll.unregister(fd)
del self.clients[fd]
client.close()
elif fd in self.clients:
del self.clients[fd]
def _clean(self):
for fd, client in self.clients.items():
self.poll.unregister(fd)
try:
client.close()
except:
pass
del self.clients[fd]