zoukankan      html  css  js  c++  java
  • Python 多人聊天工具 ( 多线程 )

    程序实现:
    1、单或多客户端使用 telnet 登陆服务端 ( 可远程 ) 进行会话
    2、服务端实现登陆、注册、退出功能
    3、客户端发送的消息会被广播到已经登陆的其他用户界面
    4、连接到服务端后,可以执行相应的程序指令
    
    程序代码:https://coding.net/u/wangxiaoqiangs/p/pycode/git/tree/master/socket/GServer
    
    GServer.py
    
    #!/usr/bin/env python
    # coding: utf-8
    # author: Xiao Guaishou
    
    import socket
    from db import DB
    from threading import currentThread, Thread
    
    
    class HandlerThread(object):
        queue = [] # sockect 队列
        db = DB()
    
        def __init__(self, sock):
            self.sock = sock
    
        def recv(self):
            data = self.sock.recv(1024).strip() # 如果使用 while 接收数据时,会导致用户必须多敲一次回车键
            return data
    
        def send(self, data):
            self.sock.sendall('
    [System]: %s
    ' % data)
    
        # 向队列中广播消息
        def broadcast(self, user, data):
            for sock in self.queue:
                sock.sendall('
    [%s]: %s
    ' % (user, data))
    
        # 关闭客户端连接
        def stop(self):
            self.send('ByeBye!')
            self.sock.close()
            self.queue.remove(self.sock) # 关闭连接后,记得从队列中删除
    
        # 程序入口
        def handler(self):
            funcdict = {
                        'login': self.login,
                        'register': self.register
            }
    
            try:
                thname = currentThread().getName()
                print('[%s] Got connection from %s' % (thname, self.sock.getpeername())) # 该程序中所有 print 的数据,将全部使用 loging 模块代替
    
                self.send('请选择功能:login/register/exit')
    
                data = self.recv()
                if data == 'exit':
                    self.stop() # 其实这里应该单独使用 self.sock.close() 来关闭连接,因为这时队列中并没有该连接,不过有了下面的捕获就没有问题了 ^_^
                elif data in funcdict:
                    return funcdict.get(data)()
                else:
                    self.handler()
            except: # 如果这里不捕获一下,就无法正常断开客户端连接
                pass
    
        # 处理用户登陆
        def login(self):
            self.send('Login... 请输入用户名密码,格式:User Password,输入 Server: 执行程序指令!')
            user_data = self.recv()
    
            # 程序内部指令
            if user_data == 'Server:':
                self.send('
    	Server:use reged	切换到注册页
    	Server:exit		退出系统')
                user_data = self.recv()
                if user_data == 'Server:use reged':
                    self.register()
                elif user_data == 'Server:exit':
                    self.stop()
                else:
                    self.send('输入错误...')
    
            datalist = user_data.split()
    
            # 判断用户输入,格式是否正确
            if len(datalist) == 2:
                user = datalist[0]
                password = datalist[1]
    
                db_data = self.db.get_data() or {}
    
                if user in db_data and password == db_data.get(user):
                    self.queue.append(self.sock) # 有权限登陆系统者,连接被加入到队列中
                    self.send('欢迎加入聊天室,输入 Server: 获取功能方法!')
                    self.broadcast('System', '[%s] 加入聊天室!' % user)
                    self.chat_room(user)
                else:
                    self.send('用户名、密码错误!')
                    self.login()
            self.login()
    
        def register(self):
            self.send('Register... 请输入用户名密码,格式:User Password,输入 Server: 执行程序指令!')
            user_data = self.recv()
    
            if user_data == 'Server:':
                self.send('
    	Server:use login	切换到注册页
    	Server:exit		退出系统')
                user_data = self.recv()
                if user_data == 'Server:login':
                    self.login()
                elif user_data == 'Server:exit':
                    self.stop()
                else:
                    self.send('输入错误...')
    
            datalist = user_data.split()
    
            if len(datalist) == 2:
                user = datalist[0]
                password = datalist[1]
    
                db_data = self.db.get_data() or {}
    
                if user in db_data:
                    self.send('该用户名已被注册!')
                    self.register()
                else:
                    db_data[user] = password
                    self.db.put_data(db_data)
                    self.queue.append(self.sock)
                    self.broadcast('System', '新用户 [%s] 加入聊天室!' % user)
                    self.chat_room(user)
            self.register()
    
        def chat_room(self, user):
            user_data = self.recv()
            if user_data == 'Server:':
                self.send('
    	Server:logout	退出聊天室')
                user_data = self.recv()
                if user_data == 'Server:logout':
                    self.stop()
                    return # 这里如果不加 return ,会将客户端执行的 Server: 指令也广播出去
                else:
                    self.send('输入错误...')
                    self.chat_room(user)
            else:
                self.broadcast(user, user_data)
                self.chat_room(user)
    
    # 为每连接创建线程
    def Startthread(sock, addr):
        print('Received new client connection. %s:%s' % (addr[0], addr[1]))
    
        th = HandlerThread(sock)
        t = Thread(target=th.handler)
        t.setDaemon(True)
        t.start()
    
    # 启动服务
    def Server():
        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        s.bind(('0.0.0.0', 17170))
        s.listen(1)
    
        while True:
            try:
                sock, addr = s.accept()
            except KeyboardInterrupt:
                exit('
    ByeBye!')
    
            Startthread(sock, addr)
    
        s.close()
    
    if __name__ == '__main__':
        Server()
        
    db.py
    
    # coding: utf-8
    
    import json
    
    # 创建一个类,代替数据库
    class DB(object):
        def __init__(self, path='Storage.db'):
            self.path = path
    
        def get_data(self, data=None):
            try:
                with open(self.path) as f:
                    data = json.load(f)
            except IOError as e:
                return data # 首次取数据时,由于文件不存在或没数据,将返回默认值 None
            finally:
                return data
    
        def put_data(self, data):
            with open(self.path, 'w') as f:
                json.dump(data, f)
  • 相关阅读:
    实现USB即插即用 教您怎么取消安全删除硬件功能
    如何用EFS对脱机文件加密
    快速美化封面用word就可以
    给iPhone联系人设置小头像的两种方法
    文件夹加密巧用“类标识符”
    保存网页FLASH有妙招
    使用EFS对你电脑上的脱机文件加密
    虚拟xp系统如何在win7系统中安装
    医药圈 www.eyaoq.com
    《eyaoq.com医药圈医药人自己的社区邀请函》
  • 原文地址:https://www.cnblogs.com/wangxiaoqiangs/p/5630418.html
Copyright © 2011-2022 走看看