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

项目名称:多人聊天室
项目结构:
  client.py
  server.py
  settings.py
项目思路:服务端接收客户端连接,客户端发送信息给服务端,服务端将信息发送给所有客户端。
项目实现:主进程负责接收键盘输入(sys.stdin.readline),使用multiprocessing.Process函数创造一个进程,在这个进程中,使用select监听两个套接字,一个套接字负责服务端与客户端之间的消息接收与发送,另一个负责与主进程保持联系。

 

# settings.py

import os
from socket import *
from random import randint
import shelve

HOST = "127.0.0.1"

# 服务端和客户端的连接地址
SOCK_PORT = 4444
SOCK_ADDR = HOST, SOCK_PORT

# 服务端server.py文件中供pipe_server和pipe_client使用的套接字地址
SER_PIPE_PORT = 4321
SER_PIPE_ADDR = HOST, SER_PIPE_PORT

# 客户端client.py文件中供pipe_server和pipe_client使用的套接字地址
# 因为每个客户端都必须有不同的套接字来作起到连接键盘输入和网络套接字之间的管道的作用
# 使用一个文件记录下每一次运行出现的端口号,以保证不重复
if not os.path.exists("ports.dat"):
    f = shelve.open("ports")
    f["ports"] = []
f = shelve.open("ports")
while True:
    n = randint(4500, 10000)
    if n not in f["ports"]:
        f['ports'].append(n)
        break
f.close()
CLI_PIPE_PORT = n


CLI_PIPE_ADDR = HOST, CLI_PIPE_PORT

# 缓冲区大小
BUFFERSIZE = 1024

# 返回一个TCP服务端套接字
def server(addr):
    sock = socket(AF_INET, SOCK_STREAM, 0)
    sock.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
    sock.bind(addr)
    sock.listen(10)
    return sock

# 返回一个TCP客户端套接字
def client(addr):
    sock = socket(AF_INET, SOCK_STREAM, 0)
    sock.connect(addr)
    return sock
settings.py
# server.py

import sys
import shelve
from socket import *
from select import select
from multiprocessing import Process
from settings import *

def listen(sock_server, pipe_server):
    # IO多路复用:循环监听套接字
    rlist = [sock_server, pipe_server]
    wlist = []
    xlist = []

    print("等待连接...")
    while True:
        rs, ws, xs = select(rlist, wlist, xlist)

        for r in rs:
            if r is sock_server:
                # 接受客户端连接
                conn, addr = sock_server.accept()
                rlist.append(conn)
            elif r is pipe_server:
                # 接收键盘输入并发送到所有客户端去
                conn, addr = pipe_server.accept()
                data = conn.recv(BUFFERSIZE)
                data = bytes("管理员:", "UTF-8") + data
                for c in rlist[2:]:
                    c.send(data)
                conn.close()
            else:
                # 接收客户端信息
                # 将客户端信息发送到所有的客户端中去
                try:
                    data = r.recv(BUFFERSIZE)
                except:
                    r.close()
                    rlist.remove(r)
                else:
                    print(data.decode(), end="")
                    for c in rlist[2:]:
                        c.send(data)

def clear_all():
    f = shelve.open("ports")
    f['ports'].clear()
    f.close()

if __name__ == '__main__':
    # 首先将ports内容都删除
    clear_all()

    # 创建两个套接字
    # 套接字sock_server是一个TCP服务端,负责服务端与客户端的交流
    # 套接字pipe_server也是一个TCP服务端,不过起到管道的作用,负责接收键盘输入
    sock_server = server(SOCK_ADDR)
    pipe_server = server(SER_PIPE_ADDR)

    # 开始一个子进程,执行listen函数
    p = Process(target=listen, args=(sock_server, pipe_server))
    p.daemon = True
    p.start()
    
    # 循环接收键盘输入
    while True:
        try:
            # 从标准输入流(键盘)读取一行
            data = sys.stdin.readline()  
        except KeyboardInterrupt:
            # 如果遇到退出/中止信号,关闭套接字,结束子进程,退出程序
            sock_server.close()
            pipe_server.close()
            p.terminate()
            clear_all()
            break

        if not data:
            # 如果从键盘获取数据为空,继续循环
            continue
        else:
            # 获得键盘数据,创建客户端套接字pipe_client,将键盘输入传输给pipe_server
            pipe_client = client(SER_PIPE_ADDR)
            pipe_client.send(bytes(data, "UTF-8"))
            pipe_client.close()
    
    
server.py
# client.py

import sys
from socket import *
from select import select
from multiprocessing import Process
from settings import *

def connect(sock_client, pipe_server, name):
    # IO多路复用:循环监听套接字
    rlist = [sock_client, pipe_server]
    wlist = []
    xlist = []

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

        for r in rs:
            if r is sock_client:
                # 接受服务端的信息
                data = sock_client.recv(BUFFERSIZE).decode()
                print(data, end="")
            elif r is pipe_server:
                # 接受键盘输入并发送给服务端
                conn, addr = pipe_server.accept()
                data = conn.recv(BUFFERSIZE)
                data = bytes(name + "", "UTF-8") + data
                sock_client.send(data)
                conn.close()

def get_name():
    return input("User name: ")

if __name__ == '__main__':
    # 使用get_name函数获得用户名 
    name = get_name()

    # 创建两个套接字
    # 套接字sock_client是一个TCP客户端,负责服务端与客户端的交流
    # 套接字pipe_server也是一个TCP服务端,不过起到管道的作用,负责接收键盘输入
    sock_client = client(SOCK_ADDR)
    sock_client.send(bytes(name + "加入了聊天室。\n", "UTF-8"))
    pipe_server = server(CLI_PIPE_ADDR)

    # 开始一个子进程,执行connect函数
    p = Process(target=connect, args=(sock_client, pipe_server, name))
    p.daemon = True
    p.start()

    # 循环接收键盘输入
    while True:
        try:
            # 从标准输入流(键盘)读取一行
            data = sys.stdin.readline()
        except KeyboardInterrupt:
            # 如果遇到退出/中止信号,发送退出信息,关闭套接字,结束子进程,退出程序
            sock_client.send(bytes(name + "退出了聊天室。\n", "UTF-8"))
            sock_client.close()
            pipe_server.close()
            p.terminate()
            break

        if not data:
            # 如果从键盘获取数据为空,继续循环
            continue
        else:
            # 获得键盘数据,创建客户端套接字pipe_client,将键盘输入传输给pipe_server
            pipe_client = client(CLI_PIPE_ADDR)
            pipe_client.send(bytes(data, "UTF-8"))
            pipe_client.close()
client.py

 

posted @ 2019-12-18 21:44  no樂on  阅读(3506)  评论(0编辑  收藏  举报