聊天服务器

一.实验介绍

1.1 项目目标

本项目使用wxpython、asynchat、_thread等模块开发一个图形界面的聊天室程序。

1.2知识点

  • asyncore 、asynchat模块使用
  • wxPython 图形开发

1.3项目环境

  • Python3.5

二.服务器端

首先创建一个聊天服务器类,通过继承asyncore.dispatcher类来实现,创建server.py文件,写入如下代码:

import asynchat
import asyncore

# 定义端口
PORT = 2222

# 定义结束异常类
class EndSession(Exception):
    pass

class ChatServer(asyncore.dispatcher):
    """
    聊天服务器
    """
    def __init__(self, port):
        asyncore.dispatcher.__init__(self)
        # 创建socket
        self.create_socket()
        # 设置 socket 为可重用
        self.set_reuse_addr()
        # 监听端口
        self.bind((‘0.0.0.0‘, port))
        self.listen(5)
        self.users = {}
        self.main_room = ChatRoom(self)

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

上述代码完成后,在服务器中还需要能维护每个用户的链接会话,这个功能使用asynchat.async_chat类来实现,在server.py中继续写下如下代码:

class ChatSession(asynchat.async_chat):
    """
   负责和客户端通信
    """

    def __init__(self, server, sock):
        asynchat.async_chat.__init__(self, sock)
        self.server = server
        self.set_terminator(b‘\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.decode("utf-8"))

    def found_terminator(self):
        # 当客户端的一条数据结束时的处理
        line = ‘‘.join(self.data)
        self.data = []
        try:
            self.room.handle(self, line.encode("utf-8"))
        # 退出聊天室的处理
        except EndSession:
            self.handle_close()

    def handle_close(self):
        # 当 session 关闭时,将进入 LogoutRoom
        asynchat.async_chat.handle_close(self)
        self.enter(LogoutRoom(self.server))

接下来实现命令处理类,实现用户的登陆,退出。发消息、查询在线用户的功能,并且使得在房间中的用户类可以继承该命令处理类。在server.py中继续写入如下代码:

class CommandHandler:
    """
    命令处理类
    """
    def unknown(self, session, cmd):
        # 响应未知命令
        # 通过 aynchat.async_chat.push 方法发送消息
        session.push((‘Unknown command {} \n‘.format(cmd)).encode("utf-8"))

    def handle(self, session, line):
        line = line.decode()
        # 命令处理
        if not line.strip():
            return
        parts = line.split(‘ ‘, 1)
        cmd = parts[0]
        try:
            line = parts[1].strip()
        except IndexError:
            line = ‘‘
        # 通过协议代码执行相应的方法
        method = getattr(self, ‘do_‘ + cmd, None)
        try:
            method(session, line)
        except TypeError:
            self.unknown(session, cmd)

class Room(CommandHandler):
    """
    包含多个用户的环境,负责基本的命令处理和广播
    """
    def __init__(self, server):
        self.server = server
        self.sessions = []

    def add(self, session):
        # 一个用户进入房间
        self.sessions.append(session)

    def remove(self, session):
        # 一个用户离开房间
        self.sessions.remove(session)

    def broadcast(self, line):
        # 向所有的用户发送指定消息
        # 使用 asynchat.asyn_chat.push 方法发送数据
        for session in self.sessions:
            session.push(line)

    def do_logout(self, session, line):
        # 退出房间
        raise EndSession

class LoginRoom(Room):
    """
    处理登录用户
    """
    def add(self, session):
        # 用户连接成功的回应
        Room.add(self, session)
        # 使用 asynchat.asyn_chat.push 方法发送数据
        session.push(b‘Connect Success‘)

    def do_login(self, session, line):
        # 用户登录逻辑
        name = line.strip()
        # 获取用户名称
        if not name:
            session.push(b‘UserName Empty‘)
        # 检查是否有同名用户
        elif name in self.server.users:
            session.push(b‘UserName Exist‘)
        # 用户名检查成功后,进入主聊天室
        else:
            session.name = name
            session.enter(self.server.main_room)

class LogoutRoom(Room):
    """
    处理退出用户
    """
    def add(self, session):
        # 从服务器中移除
        try:
            del self.server.users[session.name]
        except KeyError:
            pass

class ChatRoom(Room):
    """
    聊天用的房间
    """
    def add(self, session):
        # 广播新用户进入
        session.push(b‘Login Success‘)
        self.broadcast((session.name + ‘ has entered the room.\n‘).encode("utf-8"))
        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‘).encode("utf-8"))

    def do_say(self, session, line):
        # 客户端发送消息
        self.broadcast((session.name + ‘: ‘ + line + ‘\n‘).encode("utf-8"))

    def do_look(self, session, line):
        # 查看在线用户
        session.push(b‘Online Users:\n‘)
        for other in self.sessions:
            session.push((other.name + ‘\n‘).encode("utf-8"))

在server.py的最下民输入如下代码,启动server端。

if __name__ == ‘__main__‘:
    s = ChatServer(PORT)
    try:
        print("chat serve run at ‘127.0.0.1:{0}‘".format(PORT))
        asyncore.loop()
    except KeyboardInterrupt:
        print("chat server exit")

三.客户端

完成了服务器端后,就需要实现客户端了。客户端将基于 wxPython 模块实现。
wxPython 模块是 wxWidgets GUI 工具的
Python 绑定。所以通过
wxPython 模块我们就可以实现
GUI 编程了。同时我们的聊天协议基于文本,所以我们和服务器之间的通信将基于 telnetlib 模块实现。代码如下:

import telnetlib,wx
from time import sleep
import _thread as thread

class LoginFrame(wx.Frame):
    """
    登录窗口
    """
    def __init__(self, parent, id, title, size):
        # 初始化,添加控件并绑定事件
        wx.Frame.__init__(self, parent, id, title)
        self.SetSize(size)
        self.Center()
        self.serverAddressLabel = wx.StaticText(self, label="Server Address", pos=(10, 50), size=(120, 25))
        self.userNameLabel = wx.StaticText(self, label="UserName", pos=(40, 100), size=(120, 25))
        self.serverAddress = wx.TextCtrl(self, pos=(120, 47), size=(150, 25))
        self.userName = wx.TextCtrl(self, pos=(120, 97), size=(150, 25))
        self.loginButton = wx.Button(self, label=‘Login‘, pos=(80, 145), size=(130, 30))
        # 绑定登录方法
        self.loginButton.Bind(wx.EVT_BUTTON, self.login)
        self.Show()

    def login(self, event):
        # 登录处理
        try:
            serverAddress = self.serverAddress.GetLineText(0).split(‘:‘)
            con.open(serverAddress[0], port=int(serverAddress[1]), timeout=10)
            response = con.read_some()
            if response != b‘Connect Success‘:
                self.showDialog(‘Error‘, ‘Connect Fail!‘, (200, 100))
                return
            con.write((‘login ‘ + str(self.userName.GetLineText(0)) + ‘\n‘).encode("utf-8"))
            response = con.read_some()
            if response == b‘UserName Empty‘:
                self.showDialog(‘Error‘, ‘UserName Empty!‘, (200, 100))
            elif response == b‘UserName Exist‘:
                self.showDialog(‘Error‘, ‘UserName Exist!‘, (200, 100))
            else:
                self.Close()
                ChatFrame(None, 2, title=‘ShiYanLou Chat Client‘, size=(500, 400))
        except Exception:
            self.showDialog(‘Error‘, ‘Connect Fail!‘, (95, 20))

    def showDialog(self, title, content, size):
        # 显示错误信息对话框
        dialog = wx.Dialog(self, title=title, size=size)
        dialog.Center()
        wx.StaticText(dialog, label=content)
        dialog.ShowModal()

class ChatFrame(wx.Frame):
    """
    聊天窗口
    """
    def __init__(self, parent, id, title, size):
        # 初始化,添加控件并绑定事件
        wx.Frame.__init__(self, parent, id, title)
        self.SetSize(size)
        self.Center()
        self.chatFrame = wx.TextCtrl(self, pos=(5, 5), size=(490, 310), style=wx.TE_MULTILINE | wx.TE_READONLY)
        self.message = wx.TextCtrl(self, pos=(5, 320), size=(300, 25))
        self.sendButton = wx.Button(self, label="Send", pos=(310, 320), size=(58, 25))
        self.usersButton = wx.Button(self, label="Users", pos=(373, 320), size=(58, 25))
        self.closeButton = wx.Button(self, label="Close", pos=(436, 320), size=(58, 25))
        # 发送按钮绑定发送消息方法
        self.sendButton.Bind(wx.EVT_BUTTON, self.send)
        # Users按钮绑定获取在线用户数量方法
        self.usersButton.Bind(wx.EVT_BUTTON, self.lookUsers)
        # 关闭按钮绑定关闭方法
        self.closeButton.Bind(wx.EVT_BUTTON, self.close)
        thread.start_new_thread(self.receive, ())
        self.Show()

    def send(self, event):
        # 发送消息
        message = str(self.message.GetLineText(0)).strip()
        if message != ‘‘:
            con.write((‘say ‘ + message + ‘\n‘).encode("utf-8"))
            self.message.Clear()

    def lookUsers(self, event):
        # 查看当前在线用户
        con.write(b‘look\n‘)

    def close(self, event):
        # 关闭窗口
        con.write(b‘logout\n‘)
        con.close()
        self.Close()

    def receive(self):
        # 接受服务器的消息
        while True:
            sleep(0.6)
            result = con.read_very_eager()
            if result != ‘‘:
                self.chatFrame.AppendText(result)

if __name__ == ‘__main__‘:
    app = wx.App()
    con = telnetlib.Telnet()
    LoginFrame(None, -1, title="Login", size=(320, 250))
    app.MainLoop()

原文地址:https://www.cnblogs.com/wl443587/p/10462136.html

时间: 2024-07-28 23:31:34

聊天服务器的相关文章

使用GoWorld游戏服务器引擎轻松实现分布式聊天服务器

GoWorld游戏服务器引擎简介 GoWorld是一款开源的分布式可扩展的游戏服务器引擎,使用Go语言(Golang)编写.它采用类似BigWorld的结构,使用了简化的场景-对象框架.以一个典型的MMORPG为例,每个服务器上会有多个场景,每个场景里可以包含多个对象,这些对象包括玩家.NPC.怪物等.GoWorld服务器可以将场景分配到在不同的进程甚至不同的机器上,从而使得游戏服务器的负载是可扩展的. 开源分布式游戏服务器引擎:https://github.com/xiaonanln/gowo

通过python 构建一个简单的聊天服务器

构建一个 Python 聊天服务器 一个简单的聊天服务器 现在您已经了解了 Python 中基本的网络 API:接下来可以在一个简单的应用程序中应用这些知识了.在本节中,将构建一个简单的聊天服务器.使用 Telnet,客户机可以连接到 Python 聊天服务器上,并在全球范围内相互进行通信.提交到聊天服务器的消息可以由其他人进行查看(以及一些管理信息,例如客户机加入或离开聊天服务器).这个模型如图 1 所示. 图 1. 聊天服务器使用 select 方法来支持任意多个客户机 聊天服务器的一个重要

使用ServerSocket建立聊天服务器(二)

-------------siwuxie095 工程名:TestMyServerSocket 包名:com.siwuxie095.socket 类名:MyServerSocket.java(主类).ServerListener.java.ChatSocket.java.ChatManager.java 工程结构目录如下: MyServerSocket.java(主类): package com.siwuxie095.socket; /** * 聊天服务器,不仅能向客户端发送数据,也能从客户端读取

定制的Server-Sent Events 聊天服务器

1 //匿名聊天服务器 2 //将新的消息POST到/chat地址,或者以GET形式从通一个URL获取文本或事件流 3 //创建一个GET请求到"/"来返回一个简单的HTML文件,这个文件包括客户端聊天UI 4 5 var http = require('http'); 6 7 //聊天客户端使用的HTML文件 8 var clientUI = require('fs').readFileSync("chatClient.html"); 9 var emulatio

7.持久聊天服务器

服务器准备部分: https://technet.microsoft.com/zh-cn/library/dn951388 https://technet.microsoft.com/zh-cn/library/gg398495 1.服务器基础配置和软件同标准版前端 2.安装 消息队列 3.标准版前端可以并置持久聊天服务器,企业版不可以并置 4.持久聊天服务器的数据存储和合规数据存储,需要SQL数据库支持,测试可以使用后端数据库(RTC实例), 持久聊天服务器角色安装部分: 1.使用拓扑生成器添

27.app后端搭建聊天服务器的经历

现在,聊天功能已经成了社交app的标配了.但是,众多web开发出生的程序员对聊天相关的服务的不了解,带来了很多开发上的困扰.在这篇文章中,根据下面3个方面,谈谈聊天服务. 1.      聊天服务的技术选型 2.      开发社交app中,实现聊天服务踩过的坑 3.      那些著名app的聊天服务 1. 聊天服务的技术选型 需要开发聊天服务,首先要选择用到的协议,现在,常用的聊天协议有: (1)      xmpp,一个基于xml的消息协议,被广泛应用于Gtalk,Facebook,但缺点

手把手教你搭建LyncServer2013之安装持久聊天服务器(十三)

这一节中,不得不说的就是持久聊天服务器,为Lync  Server 2013新建的一个角色,在企业版中,需要单独部署,不能和其他服务器并置,WAC服务器也是如此,因在前面的拓扑中未定义持久聊天服务器,下面我们开始新建拓扑并进行发布了,在前端服务器上打开拓扑生成器,并下载当前拓扑信息 右键持久聊天池,新建持久聊天池 输入FQDN并选择"单计算机池" 我后续想测试下合规性,所以这里选了启用合规性,可以根据自己组织内部需求进行选择,输入显示名称 定义SQL Server存储,我这里仍然使用镜

使用ServerSocket建立聊天服务器(一)

-------------siwuxie095 工程名:TestMyServerSocket 包名:com.siwuxie095.socket 类名:MyServerSocket.java(主类).ServerListener.java.ChatSocket.java 工程结构目录如下: MyServerSocket.java(主类): package com.siwuxie095.socket; /** * 聊天服务器,仅能向客户端发送数据 * * @author siwux * */ pub

Lync2013Chat持久聊天服务器部署(六)

LYNC2013持久聊天服务器部署 作者:牛志会 准备工作 计算机名称 操作系统 角色 IP地址 AD01.BJN99.CN Windows Server 2008R2 DC / DNS /CAS 192.168.3.10 LYNCFE01.BJN99.CN Windows Server 2008R2 LYNC前端服务器 192.168.3.11 LYNCSQL01.BJN99.CN Windows Server 2008R2 LYNC后端服务器 192.168.3.12 LYNCCHAT.BJ

Skype For Business 2015实战系列15:安装持久聊天服务器

这篇博文中我们将要开始安装持久聊天服务器,具体安装步骤如下: 一. 准备拓扑 登陆到前端服务器,打开SFB拓扑生成器.鼠标右键"持久聊天池"-"新建持久聊天池": 输入持久聊天池FQDN,点击下一步: 输入持久聊天池显示名称, 并且勾选"启用合规性"."使用此池作为魔人池": 定义持久聊天池是用的SQL Server存储: 指定"合规性SQL Server存储": 定义文件存储,如下图所示: 选择下一个跃点