Python实现网络多人聊天室 - Linux

Python实现网络多人聊天室 - Linux

相关连接:

  Python实现网络多人聊天室 - Windows

  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()

运行截图:

总结:

  • 在打代码之前,一定要先进行规划,大致写出项目的大概路线。
  • 项目的实现要从最基本的地基开始,像这样一个网络间的多人聊天室的实现必须先从建立服务端和客户端开始,不能反而从表面入手。比如如果要做一个网络多人聊天室的图形化界面应用,绝对不可以先去写图形界面的实现,就算最后实现的图形界面多么好看,如果不能实现网络通信也白干。
  • 对于项目中出错的点,应该多加注释,方便以后阅读,在网上查找到有益的知识,可以把网址复制下来,写进项目文档,方便以后不时之需。
posted @ 2019-08-03 17:02  no樂on  阅读(17554)  评论(0编辑  收藏  举报