170214

来源:互联网 发布:linux mv移动多个文件 编辑:程序博客网 时间:2024/06/05 02:48

1625-5 王子昂 总结《2017年2月14日》 【连续第137天总结】

A.python多人聊天室90%

B.多对多的聊天实际上只需要在全双工聊天程序的基础上,把服务器添加转发机制即可。即把收到的消息再广播给除了发送者意外的所有连接者。另外由于有多个连接,所以需要创建多组套接字和线程。使用列表来描述它们非常方便。而客户端一边可以完全不用修改,因为同样还是发送和接收两个线程。

一步一步完成,首先是套接字列表。之前因为是一对一连接,所以可以使用Send.join()方法阻塞住等待该连接断开后再等待下一个连接。现在是多对多连接,那么阻塞的地方应该是accept方法。即主线程一直在等待新客户端的接入,并为每个客户端创建两个套接字和线程来通讯。

不过有一个问题,当某个客户端退出以后,如何识别它的退出消息来清除列表中其的套接字和线程呢。在调试窗口直接关闭的话,服务器端会收到None和一个错误信息:Traceback (most recent call last):
  File "F:/python/chatroom/.idea/ser.py", line 30, in run
    data = self.tcpCliSock.recv(self.BUFSIZ)
error: [Errno 10054] 

百度之后得知,Errno 10054的意思是远程主机强迫关闭了一个现有连接。也就是说这个错误可以识别客户端退出,但是如何使用try-except来接收这种error呢?百度了一下是用socket.error,但是自己用的时候会报AttributeError: type object '_socketobject' has no attribute 'error'翻书上偶然在创建异常的章节看到了“socket模块生成的异常叫做socket.error”,也就是说这个用法应该没错,那么为什么会报错呢…突然想起之前导入模块时使用的是书上的方法:from socket import * ,这个之前虽然注意到了但是一直没改,会不会跟这个有关呢……把from socket import * 改为import socket,并将相关的函数socket前加上模块名sokcet;果然成功了。查了一下书上关于模块的知识,“from-import"是将指定模块的内容导入到当前作用域,也就是说socket.error省略了'socket.'的部分,因此应该直接使用except error。

这样就可以识别客户端断开的问题了。清理工作分别是结束套接字和两个子线程。套接字很好结束,直接调用close()方法即可,难办的是两个线程。因为之前是使用join方法来等待其的自然结束。查到了event对象可以进行进程管理,如暂停,继续和停止。然而无论使用return,break还是event机制,查看发现它们都仍然存活。测试了良久以后发现,查看的其实是列表里的对象,只需要用remove方法清除即可。

解决完退出问题,想办法进行各个客户端的标识,也就是每个客户端应该有名字。最初的想法是把名字放在客户端的信息里,把名字视作发送信息的一部分即可。但是这样的话,在客户端断开的时候就不可能广播“用户XXX退出了房间”这样的信息,因此必须将信息存放于服务器当中。使用列表又有点麻烦,直接将名字保存在类内作为一个成员即可。调用的时候可以正常取出,清除的时候也可以跟随类一同被释放。

服务器代码:

#encoding:utf-8import socketfrom time import ctimeimport threadingimport tracebackfrom os import _exitclass Conn(threading.Thread):    def __init__(self, func,socket,name='未命名'):        threading.Thread.__init__(self)        self.name = name        self.func = func        self.tcpCliSock=socket        self.BUFSIZ=1024    def run(self):        if self.func == 'send':            while (True):                data = raw_input('> ')                if data=='exit':                    break                try:                    for client in cli:                        client.send('[system message][%s]%s' % (ctime(), data))                except error:                    print 'error:no connection'        else:            while (True):                try:                    data = self.tcpCliSock.recv(self.BUFSIZ)                    if data[:6] =='_name=':                        self.name=data[6:]                        continue                except socket.error:#有用户退出                    self.tcpCliSock.close()                    n=cli.index(self.tcpCliSock)                    cli.remove(self.tcpCliSock)                    Send.remove(Send[n])                    Recv.remove(Recv[n])                    print '[%s]%s退出了' % (ctime(), self.name)                    for client in cli:#广播退出                        client.send('[system message][%s]%s退出了' % (ctime(), self.name))                    break                cli.remove(self.tcpCliSock)                for client in cli:                    client.send('%s%s:%s'%(data[:26],self.name,data[26:]))                cli.append(self.tcpCliSock)                if not data:                    break                print data[:26]+self.name+':'+data[26:]cli = []Send = []Recv = []def main():    HOST=''    PORT=21568    ADDR=(HOST,PORT)    tcpSerSock=socket.socket(socket.AF_INET, socket.SOCK_STREAM)    tcpSerSock.bind(ADDR)    tcpSerSock.listen(5)    while(True):        print 'waiting for connected..'        tcpCliSock, addr = tcpSerSock.accept()        cli.append(tcpCliSock)        print '...connected from:', addr        Send.append(Conn('send',tcpCliSock))        Recv.append(Conn('recv',tcpCliSock))        Recv[-1].name=Recv[-1].tcpCliSock.recv(1024)[6:]        print '['+ctime()+']'+Recv[-1].name+'进入了'        Send[-1].start()        Recv[-1].start()#        Send.join()#        tcpCliSock.close() #   print 'Service Runover'    tcpSerSock.close()if __name__=='__main__':    main()

完成的服务器信息如:

waiting for connected..
...connected from: ('172.20.10.2', 55827)
[Wed Feb 15 01:53:54 2017]小蓝进入了
> waiting for connected..
[Wed Feb 15 01:53:58 2017]小蓝:大家好
[Wed Feb 15 01:54:05 2017]小蓝:再见
[Wed Feb 15 01:54:08 2017]小蓝退出了

客户端代码:

#encoding:utf-8from socket import *from time import ctimeimport selectimport threadingHOST='DESKTOP-ORQOQ33'PORT=21568BUFSIZ=1024ADDR=(HOST, PORT)tcpCliSock=socket(AF_INET, SOCK_STREAM)try:    tcpCliSock.connect(ADDR)except error:    print "connected failed"else:    print "connected success"class Conn(threading.Thread):    def __init__(self, func,socket,name=''):        threading.Thread.__init__(self)        self.name = name        self.func = func        self.tcpCliSock=socket        self.BUFSIZ=1024        self.NAME = '小蓝'    def run(self):        if self.func == 'send':            self.tcpCliSock.send('_name='+self.NAME)            while (True):                data = raw_input('> ')                if data=='exit':                    break                if data=='renamed':                    self.NAME=raw_input('请输入你想要修改的昵称')                    self.tcpCliSock.send('_name=' + self.NAME)                    continue                try:                    self.tcpCliSock.send('[%s]%s' % (ctime(), data))                except error:                    print 'error:no connection'        else:            while (True):                try:                    data = self.tcpCliSock.recv(self.BUFSIZ)                except error:                    print 'Connecetion stopped'                    break                if not data:                    break                print dataSend=Conn('send',tcpCliSock)Recv=Conn('recv',tcpCliSock)Recv.setDaemon(True)Send.start()Recv.start()Send.join()print 'Client RunOver'tcpCliSock.close()

客户端信息如:

connected success
> 大家好
> 再见
> exit
Client RunOver

明天看看怎么样从外网ip接入,实现真正的使用


C.明日计划

Python多对多聊天应用


  

0 0
原创粉丝点击