第24章 项目5:虚拟茶话会

wxPython

编写客户端需要用到wxPython,它是一个GUI工具包,

下载地址:https://www.wxpython.org/download.php

运行安装即可。

当使用命令:import wx时,未报错,安装成功。

聊天室客户端代码不能在 windows 下运行,因为代码使用 select 同时监听 socket 和输入流,在 Windows select 函数是由 WinSock 库提供,不能处理不是由 WinSock 定义的文件描述符。

客户端代码还有个缺陷是,当某个客户端在输入消息但还未发送出去时,服务器也发送消息过来,这样会冲刷掉客户端正在输入的消息。这目前来看没办法解决的,唯一的解决方法是使用像 ncurses 终端库使用户输入和输出独立开,或者写一个GUI的程序。

http://www.cnblogs.com/hazir/p/python_chat_room.html

 

24-1.py 迷你服务器程序    

from asyncore import dispatcher
import asyncore

class ChatServer(dispatcher): pass

s = ChatServer()
asyncore.loop()

运行之后什么都没发生。

 

24-2.py 可以接收连接的服务器

# coding=utf-8
from asyncore import dispatcher
import socket, asyncore

class ChatServer(dispatcher):

def handle_accept(self):# 调用允许客户端连接的self.accpet函数,并返回一个连接和一个地址
conn, addr = self.accept()
print 'Connection attempt from', addr[0] # addr[0]是客户端的IP地址

s = ChatServer()
s.create_socket(socket.AF_INET, socket.SOCK_STREAM) # 服务器初始化,使用两个参数指定所需套接字的类型
s.bind(('', 5005)) # 把服务器绑定到具体的地址上,,主机名为空(即本地主机),端口号为5005
s.listen(5) # 调用服务器以告诉服务器要监听连接,并指定5个连接的代办事务
asyncore.loop() # 启动服务器,循环监听

客户端测试:telnet命令——通过如下步骤设置:

1. 打开控制面板

2. 程序和功能

3. 打开或关闭WINDOWS功能

4. CHECK TELNET 客户端。

5. TELNET输入如果出现乱码:按下 CTRL+] 即可正常输入

以上配置完毕后,运行24-2.py,并在cmd窗口输入测试命令:

telnet 127.0.0.1 5005或者telnet localhost 5005

客户端立即被断开,即cmd恢复到初始状态

然后服务端出现如下:Connection attempt from 127.0.0.1

24-3.py具有一些清理功能的基本服务器

from asyncore import dispatcher
import socket, asyncore

PROT = 5005

class ChatServer(dispatcher):

def __init__(self, port):
dispatcher.__init__(self)
self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
self.set_reuse_addr()
self.bind(('',port))
self.listen(5)

def handle_accept(self):
conn, addr = self.accept()
print 'Connection attempt from', addr[0]

if __name__ == '__main__':
s = ChatServer(PROT)
try: asyncore.loop()
except KeyboardInterrupt: pass
运行
24-3.py,并在cmd窗口输入测试命令:telnet localhost 5005

服务端出现如下:Connection attempt from 127.0.0.1

 

ChatSession

基本的ChatSession类用处不大,应在代码实现中为每个连接创建一个dispatcher对象。主要任务是收集来自客户端的数据进行响应,可以使用asynchat模块。

为了让asynchat起作用,只要覆盖两个方法即可。

(1) collect_incoming_data:在从套接字中读取一些bit文本时调用。

(2) found_terminator:在读取一个结束符时调用。结束符通过set_terminiator方法设置,一般设置为"\r\n".

24-4.py 带有ChatSession类的服务器程序

# coding=utf-8
from asyncore import dispatcher
from asynchat import async_chat
import socket, asyncore

PORT = 5005

class ChatSession(async_chat):
"""
负责和单用户通信
"""

def __init__(self, sock):
async_chat.__init__(self, sock)
self.set_terminator("\r\n")
self.data = []

def collect_incoming_data(self, data):
self.data.append(data)

def found_terminator(self):
line = ''.join(self.data)
self.data = []
# 处理这行数据.....
print line

class ChatServer(dispatcher):

def __init__(self, port):
dispatcher.__init__(self)
self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
self.set_reuse_addr()
self.bind(('',port))
self.listen(5)
self.sessions = []

def handle_accept(self):
conn, addr = self.accept()
self.sessions.append(ChatSession(conn))

if __name__ == '__main__':
s = ChatServer(PORT)
try: asyncore.loop()
except KeyboardInterrupt: print

运行24-4.py,并在cmd窗口输入测试命令:telnet localhost 5005

运行之后出现客户端界面,而连接没有马上断掉

输入1111并回车,在该界面无任何输出,而服务端输出如下:

输入Hello, world!并回车:

实现了同时使用两个或者更多客户端进行连接,在客户端输入的每一行都会在服务器终端打印出来。

整合

还需要将用户的发言广播给其他的用户,可以通过在服务器端遍历回话的列表,将发言行写到每一个客户端里面。此外,必须保证在客户单断开连接后,将它从会话列表中移除。通过重写事件处理方法handle_close 来实现这个功能。

24-5 simple_chat.py ——简单的聊天服务器
# coding=utf-8
from asyncore import dispatcher
from asynchat import async_chat
import socket, asyncore

PORT = 5005
NAME= 'TestChat'

class
ChatSession(async_chat):
"""
处理服务器和一个用户之间连接的类
"""
def __init__(self, server, sock):
# 标准设置任务:
async_chat.__init__(self, sock)
self.server = server
self.set_terminator("\r\n")
self.data = []
# 问候用户:
self.push('Welcome to %s\r\n' % self.server.name)

def collect_incoming_data(self, data):
self.data.append(data)

def found_terminator(self):
"""
如果发现了一个终止对象,也就意味着读入了一个完整的行,将其广播给每个人。
"""
line = ''.join(self.data)
self.data = []
self.server.broadcast(line)

def handle_close(self):
async_chat.handle_close(self)
self.server.disconnect(self)

class ChatServer(dispatcher):
"""
接受连接并且产生单个会话的类。它还会处理到其他会话的广播。
"""
def __init__(self, port, name):
# Standrad setup tasks
dispatcher.__init__(self)
self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
self.set_reuse_addr()
self.bind(('',port))
self.listen(5)
self.name = name
self.sessions = []

def disconnect(self, session):
self.sessions.remove(session)

def broadcast(self, line):
for session in self.sessions:
session.push(line+ '\r\n')

def handle_accept(self):
conn, addr = self.accept()
self.sessions.append(ChatSession(self, conn))

if __name__ == '__main__':
s = ChatServer(PORT, NAME)
try: asyncore.loop()
except KeyboardInterrupt: print

运行simple_chat.py,并在cmd窗口输入测试命令:telnet localhost 5005

运行之后出现客户端界面:

输入Hello, world!并回车,客户端输出该字符串:

而服务端无输出。

聊天服务器的最终版本

24-6 稍复杂的聊天服务器 chatserver.py

运行chatserver.py之后,打开两个cmd窗口,均输入测试命令:telnet localhost 5005,回车。

login命令:第一个窗口输入login maguns,第二个输入login dilbert

look命令:

say 命令:

重复登陆:

退出:

posted @ 2016-12-04 20:13  Sumomo  阅读(685)  评论(0编辑  收藏  举报