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('\n[System]: %s\n' % data)

    # 向隊列中廣播消息
    def broadcast(self, user, data):
        for sock in self.queue:
            sock.sendall('\n[%s]: %s\n' % (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('\n\tServer:use reged\t切換到注冊頁\n\tServer:exit\t\t退出系統')
            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('\n\tServer:use login\t切換到注冊頁\n\tServer:exit\t\t退出系統')
            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('\n\tServer:logout\t退出聊天室')
            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('\nByeBye!')

        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)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM