Python實現網絡多人聊天室

網絡多人聊天室

文件結構:

  chatroom
  ├── client.py  # 客戶端代碼
  ├── language.py  # 語言文件
  ├── server.py  # 服務端代碼
  └── settings.py  # 設置文件

  0 directories, 4 files

使用模塊:

  • os
  • sys
  • socket
  • select

思路:

  • settings.py,定義HOST、PORT、ADDR、buffersize、language、curuser等變量。
  • server.py,服務器代碼,使用select模塊select方法實現IO多路復用監聽sys.stdin輸入以及客戶端連接,實現與客戶端通信,將從客戶端接收到的信息群發給每個客戶端。
  • client.py,客戶端代碼,同樣使用IO多路復用同時監聽客戶端接收信息以及sys.stdin輸入信息,實現與服務端的通信,間接實現與其他客戶端的群聊。
  • language.py,語言文件,支持中文以及英語。

代碼:

settings.py

# settings.py

HOST = '0.0.0.0'      # 主機名
PORT = 5555            # 端口號
buffersize = 1024    # 緩衝大小
ADDR = HOST, PORT    # 地址

languages = ['cn', 'en']      # 'cn' -> 中文
language = 'cn'                # 'en' -> 英文

curuser = ''  # 當前用戶

language.py

# language.py

from settings import language

if language == 'en':
    administrator = 'Administrator'
    txt_administrator_close_chatroom = 'Chatroom closed by Administrator.'
    txt_uesr_enter_chatroom = 'entered the chatroom.'
    txt_user_quit_chatroom = 'quited the chatroom.'
    txt_username = 'username> '
    txt_user_already_exists = 'Username already exists!'
    txt_connect_to = 'Connected to'
    txt_connect_from = 'Connected from'
elif language == 'cn':
    administrator = '管理員'
    txt_administrator_close_chatroom = '管理員關閉了聊天室。'
    txt_uesr_enter_chatroom = '進入了聊天室。'
    txt_user_quit_chatroom = '退出了聊天室。'
    txt_username = '用戶名> '
    txt_user_already_exists = '用戶名已存在。'
    txt_connect_to = '連接到'
    txt_connect_from = '連接從'

server.py

# server.py

# 導入系統模塊
import os, sys
# 導入網絡編程(傳輸層)模塊
from socket import *
# IO多路復用模塊
from select import select
# 設置模塊
from settings import *
# 語言模塊
from language import *

def main():
    'main 主函數'
    server = socket(AF_INET, SOCK_STREAM)  # 建立TCP套接字
    server.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)  # 設置端口可立即重用
    server.bind(ADDR)  # 綁定地址
    server.listen()  # 監聽

    # 接收函數
    accept(server)

def accept(server):
    'accept 服務器接受函數'

    # 使用select模塊的select方法實現IO多路復用監聽傳輸
    rlist = [server, sys.stdin]
    wlist = []
    xlist = []

    while True:
        rs, ws, xs = select(rlist, wlist, xlist)

        for r in rs:
            if r is server:
                # 服務器接受客戶端連接
                conn, addr = server.accept()
                # 調用validate函數檢查用戶名
                if validate(conn):
                    # 將客戶端套接字添加到rlist中以監聽
                    rlist.append(conn)
                    # 如果用戶名註冊成功
                    print(txt_connect_from, addr)
                else:
                    conn.close()
            elif r is sys.stdin:
                # 服務器向所有客戶端發送系統(管理員)消息
                data = sys.stdin.readline()
                if data == '\n':
                    # 如果服務器輸入回車,則退出
                    for c in rlist[2:]:
                        c.send(b'\n')
                        c.close()
                    server.close()
                    print(txt_administrator_close_chatroom)
                    os._exit(0)
                else:
                    # 如果服務器輸入正常語句,通知所有客戶端
                    data = administrator + ': ' + data
                    for c in rlist[2:]:
                        c.send(data.encode())
            else:
                # 服務器接受客戶端的消息並轉發給所有客戶端
                data = r.recv(buffersize)
                if not data:
                    # 關閉客戶端
                    r.close()
                    rlist.remove(r)
                else:
                    # 轉發信息給其他客戶端
                    print(data.decode(), end='')
                    for c in rlist[2:]:
                        if c is not r:
                            c.send(data)

def validate(client):
    '檢驗用戶名 validate username'
    name = client.recv(buffersize).decode()
    # print(name.decode())
    # print(users)
    if name in users:
        client.send(b'Username already exists!')
        return False
    else:
        users.append(name)
        client.send(b'Welcome!')
        return True


if __name__ == '__main__':
    # 全局變量,管理用戶信息
    users = []

    # 主函數
    main()

client.py

# client.py

# 導入系統模塊
import os, sys
# 導入網絡編程(傳輸層)模塊
from socket import *
# IO多路復用模塊
from select import select
# 設置模塊
from settings import *
# 語言模塊
from language import *

def main():
    'main 主函數'
    client = socket(AF_INET, SOCK_STREAM)  # 建立TCP套接字

    # 登錄函數
    if login(client):
        # 連接函數
        connect(client)

def connect(client):
    'connect 客戶端連接函數'

    # 使用select模塊的select方法實現IO多路復用監聽傳輸
    rlist = [client, sys.stdin]
    wlist = []
    xlist = []

    while True:
        rs, ws, xs = select(rlist, wlist, xlist)

        for r in rs:
            if r is client:
                # 接受服務器發來的消息
                data = client.recv(buffersize)
                if data.decode() == '\n':
                    # 如果消息為回車,聊天室關閉
                    client.close()
                    print(txt_administrator_close_chatroom)
                    os._exit(0)
                else:
                    # 打印接收到的信息
                    print(data.decode(), end='')
            elif r is sys.stdin:
                # 發送消息給服務器
                data = sys.stdin.readline()
                if data == '\n':
                    # 如果回車,發送退出消息,關閉客戶端,退出聊天室
                    data = curuser + ': ' + txt_user_quit_chatroom + '\n'
                    client.send(data.encode())
                    client.close()
                    os._exit(0)
                else:
                    # 發信息給服務器
                    data = curuser + ': ' + data
                    client.send(data.encode())

def login(client):
    '登錄函數 login'
    # 使用全局變量管理用戶
    # 先讓客戶端輸入姓名
    global curuser
    curuser = input(txt_username)
    # 再連接到服務器,傳送用戶名以檢驗
    client.connect(ADDR)  # 連接到服務器地址
    print(txt_connect_to, ADDR)
    client.send(curuser.encode())
    data = client.recv(buffersize)
    if data.decode() == 'Username already exists!':
        # 如果用戶名已經存在,要求重新輸入
        print(txt_user_already_exists)
        return False
    else:
        # 發送信息給服務器,告知服務器用戶進入聊天室
        # -*- 因為監聽傳輸的是sys.stdin.readline(),所以必須在最後添加換行符,以便清除阻塞 -*-
        data = curuser + ': ' + txt_uesr_enter_chatroom + '\n'
        client.send(data.encode())
        return True


if __name__ == '__main__':
    main()

運行截圖:

《Python實現網絡多人聊天室》

總結:

  • 在打代碼之前,一定要先進行規劃,大致寫出項目的大概路線。
  • 項目的實現要從最基本的地基開始,像這樣一個網絡間的多人聊天室的實現必須先從建立服務端和客戶端開始,不能反而從表面入手。比如如果要做一個網絡多人聊天室的圖形化界面應用,絕對不可以先去寫圖形界面的實現,就算最後實現的圖形界面多麼好看,如果不能實現網絡通信也白乾。
  • 對於項目中出錯的點,應該多加註釋,方便以後閱讀,在網上查找到有益的知識,可以把網址複製下來,寫進項目文檔,方便以後不時之需。
点赞

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *