-
Notifications
You must be signed in to change notification settings - Fork 4
/
chat_server.py
119 lines (113 loc) · 5.42 KB
/
chat_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
#coding:utf8
import socket
import Queue
import select
import traceback
import time
class ChatServer():
def __init__(self):
self.dict_name = {}#客户端
self.dict_msg = {}#消息队列
self.listen_fd = 0#初始化监听的文件描述符
self.inputs = []#可读状态列表
self.outputs = []#可写状态列表
def run(self):
''' 用select监听socket'''
s = socket.socket()#默认ip tcp、协议
s.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)#地址复用
s.bind(('localhost',10001))
s.listen(1)
self.inputs.append(s)#将s加入到监听的列表
self.listen_fd = s#将要监听的文件描述符覆给listen_fd
while True:
list_r,list_w,list_e = select.select(self.inputs,self.outputs,self.inputs)
'''
监听刚刚创建的文件描述符(inputs列表中),如果有连接到socket就select将s写到lis_r中
如果有数据发送加入lis_w中进行操作,如果有异常在lis_e中操作
确定第一次select检测到的状态肯定是新客户端连接进来了
'''
if self.listen_fd in list_r:#如果s在可写状态可能是有连接或者有数据
conn,cli = self.listen_fd.accept()#新建一个文件描述符 来进行客户端与服务器的通信
self.dict_name[conn] = '' #新的文件描述符加入到字典
self.dict_msg[conn] = Queue.Queue()#将客户端加入到对应的消息队列
self.inputs.append(conn) #将新的文件描述符加入到监听列表中
print 'new connects',conn.getpeername() #getpeername():获取已连接成功之 Socket 的对方位址。
'''
连接成功后开始处理 select流
'''
self.doExcept(list_e)
self.doRead(list_r)
self.doWrite(list_w)
time.sleep(0.1)
def doRead(self,list_r=[]):
for ts in list_r:
if ts is self.listen_fd:continue#如果是监听的socket 就继续
try:
msg = ts.recv(1024) #select检查到有数据时 除了连接就是接收数据
if msg:#如果有数据
print 'read[%s]'%msg
cmd,tmp = msg.split(None,1)#以一个空值分离一次 客户端cmd 的命令和数据
print 'split[%s][%s]'%(cmd,tmp)
if cmd == 'name':#客户端登陆聊天室
self.doName(ts,tmp)
elif cmd == 'pm':#发送给某人
self.doPm(ts,tmp)
elif cmd == 'show':
self.doShow(ts,tmp)
else:
self.doMsg(ts,tmp)
if ts not in self.outputs:#准备发送给客户端
self.outputs.append(ts)
else:
self.doExcept([ts])
print 'read',ts.fileno(),len(msg) #打开文件描述符
except:
traceback.print_exc()
def doWrite(self,list_w=[]):
for ts in list_w:
try:
if not self.dict_msg[ts].empty():
msg = self.dict_msg[ts].get_nowait()#不等待,没有数据则直接引发异常
if msg:
ts.send(msg)
print 'write',ts.fileno(),len(msg)
except:
traceback.print_exc()
def doExcept(self,list_e=[]):#异常的处理
for ts in list_e:
if ts in self.inputs:#如果异常则不再监听将各个队列的文件描述符删除
self.inputs.remove(ts)
if ts in self.outputs:
self.outputs.remove(ts)
if ts in self.dict_name:
del self.dict_name[ts]
if ts in self.dict_msg:
del self.dict_msg[ts]
print 'except',ts.fileno()
def doName(self,ts='',tmp=''):#ts接收发过的数据 tmp 登陆聊天室
self.dict_name[ts] = tmp#客户端对应发送的消息聊天人的姓名
users = [k for k in self.dict_name if self.dict_name[k]] #如果连接的客户端有名字
for s in self.dict_msg:#给聊天室每个消息队列发送一条消息
self.dict_msg[s].put('welcome %s, there are %s friends now' % (tmp, len(users)))
def doMsg(self,ts='',msg=''):#群发消息
name = self.dict_name[ts]
for s in self.dict_msg:#新的文件描述符在消息队列中
self.dict_msg[s].put('from %s:%s'%(name,msg))
def doShow(self,ts='',msg=''):#查看在线人数
users = [self.dict_name[k] for k in self.dict_name if self.dict_name[k]]
if ts in self.dict_msg:
msg = 'there are %s users\n %s'%(len(users),users)
self.dict_msg[ts].put(msg)
def doPm(self,ts='',msg=''):#对某人发
user,tmp = msg.split(None,1)
find_user = 0
for s in self.dict_name:
if self.dict_name[s] == user: #当对某人发时取得某人的消息队列
self.dict_msg[s].put('from %s:%s' % (self.dict_name[ts], tmp))#发送用户名 消息
find_user = 1
break
if not find_user:
self.dict_msg[ts].put('user [%s] not found, and private msg not sent successfully' % find_user)
if __name__=='__main__':
cs = ChatServer()
cs.run()