zoukankan      html  css  js  c++  java
  • 套接字实现基于网络的简易聊天室

    套接字实现基于网络的简易聊天室

    客户端还有bug,代码还能继续优化,日后有时间再修改。

    服务端代码

    import  socketserver
    import hmac
    import os
    
    secret_key=b'wenli bang bang bang'
    
    
    # 验证客户端合法性
    def conn_auth(conn):
    
        print('开始验证客户端合法性')
        #生成32位的随机数
        msg=os.urandom(32)
        #发送信息
        conn.sendall(msg)
        #进行加盐
        h=hmac.new(secret_key,msg)
        #转为数字型
        digest=h.digest()
        response = conn.recv(len(digest))
        return hmac.compare_digest(response,digest)
    
    
    
    
    
    # 继承一个类,每次来一个连接就实例化一个连接,实例与实例之间互相没有影响
    class Myserver(socketserver.BaseRequestHandler):
        def handle(self):
            global renshu
            print('conn is:', self.request)
            print('Client addr is:', self.client_address)
            conn = self.request
    
            #验证客户端合法性
            if not conn_auth(conn):
                print('该链接不合法,关闭连接')
                conn.close()
    
            Client_list.append(conn)
    
    
             # 在线人数
            renshu = len(Client_list)
    
            print('链接合法,开始通信')
    
    
            #进行信息通信
            while True:
                try:
                    # 收信息
                    data = conn.recv(1024)
                    if not data: break
                    print(data.decode('utf-8'))
    
                    # 发信息
                    for i in range(renshu):
                        print(Client_list,renshu)
                        print(1)
                        if conn is Client_list[i]: continue
                        Client_list[i].sendall(data)
                        print(conn,Client_list[i])
                except Exception as e:
                    print(e)
                    break
            conn.close()
    
    
    
    if __name__ == '__main__':
        ip=input('输入服务端的ip地址:')
        port=4444
        Client_list=[]
        renshu=0
        #ThreadingTCPServer相当于外层循环,Myserver相当于里层循环
        s=socketserver.ThreadingTCPServer((ip,port),Myserver)
        #启动服务
        print('服务端已启动')
        s.serve_forever()
    

    客户端

    from socket import *
    import socket
    import threading
    import hmac
    
    
    secret_key=b'wenli bang bang bang'
    
    
    #验证客户端合法性
    def conn_auth(conn):
        print('开始进行通信认证')
        msg=conn.recv(32)
        h=hmac.new(secret_key,msg)
        digest=h.digest()
        conn.sendall(digest)
    
    
    
    #接收信息
    def recevie(s2,buffer_size):
        while True:
            reponse = s2.recv(buffer_size)
            if not reponse:continue
            print('
    -->',reponse.decode('utf-8'))
    
    #发送信息
    def send(s2,name):
        while True:
            data = input('-->')
            if not data: continue  # 如果客户端输入信息为空,则继续下一次循环
            if data == 'quite': break
            data = name + ':' + data
            s2.send(data.encode('utf-8'))
        s2.close()
    
    
    #通信
    def commucation(s2,buffer_size,name):
        t1 = threading.Thread(target=recevie,args=(s2,buffer_size))
        t2 = threading.Thread(target=send,args=(s2,name))
        t1.start()
        t2.start()
        t1.join()
        t2.join()
    
    
    def main(ip,port):
        ip_port = (ip,port)
        buffer_size = 1024
        s2 = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        s2.connect(ip_port)  # 连接服务端#
        #进行基于连接的认证
        conn_auth(s2)
        print('验证通过')
    
        #输入姓名
        name = ''
        while name == '':
            name = input('请输入您的名字:')
    
        #通讯
        commucation(s2,buffer_size,name)
    
    
    
    if __name__ == '__main__':
        ip=input('输入你的机器的ip地址:')
        port=4444
        main(ip,port)
    

      

    服务端

    import asynchat
    import asyncore
    
    
    # 定义端口
    PORT = 6666
    
    
    class CommandHandler:
        """
        命令处理类
        """
    
        def unknown(self, session, cmd):
            # 响应未知命令
            # 通过 asynchat.async_chat.push 方法发送消息
            session.push(('Unknown command {} 
    '.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)
    
    
    #这里我们首先需要一个聊天服务器类,通过继承 asyncore 的 dispatcher 类来实现
    # 定义结束异常类
    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(('127.0.0.1', port))
            self.listen(5)
            self.users = {}
            self.main_room = ChatRoom(self)
    
        def handle_accept(self):
            conn, addr = self.accept()
            ChatSession(self, conn)
    
    
    
    
    
    
    class ChatSession(asynchat.async_chat):
        """
        负责和客户端通信
        """
    
        def __init__(self, server, sock):
            asynchat.async_chat.__init__(self, sock)
            self.server = server
            self.set_terminator(b'
    ')
            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))
    
    
    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()
            # 获取用户名称
            print(name)
            if not name:
                print(1)
                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.
    ').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.
    ').encode("utf-8"))
    
        def do_say(self, session, line):
            # 客户端发送消息
            self.broadcast((session.name + ': ' + line + '
    ').encode("utf-8"))
    
        def do_look(self, session, line):
            # 查看在线用户
            session.push(b'Online Users:
    ')
            for other in self.sessions:
                session.push((other.name + '
    ').encode("utf-8"))
    
    if __name__ == '__main__':
    
        s = ChatServer(PORT)
        try:
            print("chat server run at '127.0.0.1:{0}'".format(PORT))
            asyncore.loop()
        except KeyboardInterrupt:
            print("chat server exit")
    
    # 最后就可以运行程序进行聊天了,注意需要先启动服务器再启动客户端。这个项目中使用了 asyncore 的 dispatcher 来实现服务器,asynchat 的 asyn_chat 来维护用户的连接会话,用 wxPython 来实现图形界面,用 telnetlib 来连接服务器,在子线程中接收服务器发来的消息,由此一个简单的聊天室程序就完成了。
    View Code

    客户端

    import wx
    import telnetlib
    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(':')
                print(serverAddress)
                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)) + '
    ').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):
            # 显示错误信息对话框
            print(1)
            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 + '
    ').encode("utf-8"))
                self.message.Clear()
    
        def lookUsers(self, event):
            # 查看当前在线用户
            con.write(b'look
    ')
    
        def close(self, event):
            # 关闭窗口
            con.write(b'logout
    ')
            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()
    View Code
  • 相关阅读:
    hdu 4521 小明系列问题——小明序列(线段树 or DP)
    hdu 1115 Lifting the Stone
    hdu 5476 Explore Track of Point(2015上海网络赛)
    Codeforces 527C Glass Carving
    hdu 4414 Finding crosses
    LA 5135 Mining Your Own Business
    uva 11324 The Largest Clique
    hdu 4288 Coder
    PowerShell随笔3 ---别名
    PowerShell随笔2---初始命令
  • 原文地址:https://www.cnblogs.com/-wenli/p/10264270.html
Copyright © 2011-2022 走看看