-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathserver.py
179 lines (142 loc) · 5.23 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
168
169
170
171
172
173
174
175
176
177
178
179
# -*- coding: utf-8 -*-
# !/usr/bin/python
# file: server.py
import socket
import asyncore
from asynchat import async_chat
from asyncore import dispatcher
port = 3333
magic = 'oucb'
# magic is the Pass Phrase, 'oucb' for default, set by yourself
class EndSession(Exception):
pass
class CommandHandler(object):
def unknow(self, session, cmd):
session.push('Unknow command: %s\n' % cmd)
def handle(self, session, line):
if not line.strip():
return
rec = line.split(' ', 1)
cmd = rec[0]
try:
line = rec[1].strip()
except IndexError:
line = ''
meth = getattr(self, 'do_' + cmd, None)
try:
meth(session, line)
except TypeError:
self.unknow(session, cmd)
class ChatSession(async_chat):
def __init__(self, server, sock):
async_chat.__init__(self, sock)
self.server = server
self.set_terminator('\n')
self.data = []
self.name = None
self.enter(LoginRoom(server))
def enter(self, room):
try:
cur = self.room
except AttributeError:
pass
else:
cur.remove(self)
self.room = room
room.add(self)
def collect_incoming_data(self, data):
self.data.append(data)
def found_terminator(self):
line = ''.join(self.data)
self.data = []
try:
self.room.handle(self, line)
# enter中定义了self.room属性,因此调用handle方法时,getattr方法查找相应的self.room中的方法属性
# 从客户端发送过来的命令处理后要能在相应的self.room实例中找到对于的方法属性,或是在父类Room中存在
except EndSession:
self.handle_close()
def handle_close(self):
async_chat.handle_close(self)
self.enter(LogoutRoom(self.server))
# 在chatRoom时,查找do_logout方法属性,由于是在父类存在,所以可以调用
class Room(CommandHandler):
def __init__(self, server):
self.server = server
self.session = []
def add(self, session):
self.session.append(session)
def remove(self, session):
self.session.remove(session)
def broadcast(self, line):
for session in self.session:
session.push(line)
def do_logout(self, session, line):
raise EndSession
class LoginRoom(Room):
def add(self, session):
Room.add(self, session)
# 为了从loginRoom进入chatRoom时执行的self.room.remove不会报错,若不调用add方法,session属性为空列表,执行remove会报错
session.push('Connect Success')
def do_login(self, session, line):
rec = line.split('+', 1)
magic = rec[0]
try:
name = rec[1]
except IndexError:
name = ''
if not magic:
session.push('Passphrase Empty')
elif magic != self.server.magic:
session.push('Passphrase Incorrect')
else:
if not name:
session.push('User name Empty')
elif name in self.server.users:
session.push('User name Exit')
else:
session.name = name
session.enter(self.server.main_room)
# 这里保证了进入共用的chatRoom实例,而后后调用相关的方法,能使用全局同步的session与server属性,如果换成chatRoom(self.server),
# 则相当于每次调用enter都对chatRoom进行实例化,并进入各自相应的实例,session属性都将重置为空列表
class ChatRoom(Room):
def add(self, session):
session.push('Login Success!')
self.broadcast(session.name + ' has enter the room.\n')
self.server.users[session.name] = session
Room.add(self, session)
def remove(self, session):
Room.remove(self, session)
self.broadcast(session.name + ' has left the room.\n')
def do_say(self, session, line):
self.broadcast(session.name + ': ' + line + '\n')
def do_look(self, session, line):
session.push('Online users:\n')
for other in self.session:
session.push(other.name + '\n')
class LogoutRoom(Room):
def add(self, session):
try:
del self.server.users[session.name]
except KeyError:
pass
class ChatServer(dispatcher):
def __init__(self, port, magic):
dispatcher.__init__(self)
self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
self.set_reuse_addr()
self.bind(('', port))
self.listen(5)
self.users = {}
self.main_room = ChatRoom(self)
self.magic = magic
# 预先实例化chatRoom,由于没有__init__()方法,因此执行父类Room的初始化方法,拥有server与session属性,供后面每个会话从loginRoom
# 进入chatRoom时引用,从而为每个会话提供全局同步的server与session属性,这样每个新加入chatRoom的用户可查询到其它的用户
def handle_accept(self):
con, addr = self.accept()
ChatSession(self, con)
if __name__ == '__main__':
s = ChatServer(port, magic)
try:
asyncore.loop()
except KeyboardInterrupt:
print