一起来做一个基于python的tcp聊天室吧

基于python的tcp聊天室的实现

1.目标和思路

2.客户端代码

3.服务器代码

4.运行结果

1.目标和思路

1.功能和目标: 类似群聊天

    [1]有人进入聊天室需要输入姓名,姓名不能重复

    [2] 有人进入聊天室时,其他人会收到通知:

        *** 进入聊天室

    [3] 一个人发消息,其他人会收到:

        ××× :××××××××

    [4] 有人退出聊天室,则其他人会收到通知:

        ××× 退出聊天室

2.确定技术模型

    [1] 使用字典保存用户信息 {姓名:客户端套接字}

    [2] 套接字选择: tcp套接字

    [3] 转换模型 : 客户端-->>服务端-->>所有客户端

    [4] 收发关系处理: 一个客户端发送消息给服务端,服务端再转发给其他客户端

                    客户端使用多进程来分别处理收发消息

                    服务端使用多线程来管理客户端

3.具体实现流程

    [1] 搭建网络连接

    [2] 实现登录

            客户端: × 输入姓名

                    × 将姓名发送给服务端

                    × 接收服务端反馈

                    × 如果不允许进入则重新输入,允许则进入聊天室

                    × 创建新的进程用于收发消息

            服务端:  * 创建新的线程用于管理客户端的连接

                    × 接收姓名

                    ×  判断姓名是否存在

                    ×  将结果反馈给客户端

                    × 不允许 登录结束,允许登录则将用户信息插入数据机构保存

                    × 将登录信息通知其他人

    [3] 聊天

            客户端: × 循环发送消息

                                × 循环接收消息

            服务器: × 接收消息,判断请求类型

                    × 将消息转发给其他用户

    [4] 退出

            客户端: ×输入quit进行退出

                    × 发送进程关闭,接收进程关闭

            服务器: × 退出信息发送给其他人

                    × 将其信息从服务器清除

2.客户端代码

import os,sys,signal

from socket import *

server_addr = (('127.0.0.1',9696))

def msg_recv(sockfd):      ##接收消息

    while True:

        data = sockfd.recv(1024)

        if data.decode() == 'q':

            break

        print(data.decode())

def msg_send(sockfd,name):      ##发送消息

    while True:

        try:

            data = input("发言>>")

        except KeyboardInterrupt:

            data = 'quit'

        if data == 'quit':  ##退出

            msg = 'Q %s'%name

            sockfd.send(msg.encode())

            break

        else:

            msg = 'C %s %s'%(data,name)

            sockfd.send(msg.encode())

def main():

    sockfd = socket()##创建套接字

    try:

        sockfd.connect(server_addr)

    except Exception:

        print("连接失败")

        sockfd.close()

        return

    while True:            ####输入姓名登录

        name = input("请输入昵称:")

        sockfd.send(('L '+name).encode())  ## L 区分消息类型

        data = sockfd.recv(128)

        if data.decode() == 'OK':

            print("你已经进入9696聊天室")

            break

        else:

            print(data.decode())

    signal.signal(signal.SIGCHLD,signal.SIG_IGN)

    pid = os.fork()

    if pid < 0:

        sys.exit("err") 

    elif pid == 0:        ##子进程

        msg_recv(sockfd)

    else:

        msg_send(sockfd,name)

    sys.exit("退出")

if __name__ == "__main__":

    main()

3.服务器代码

from threading import Thread

from socket import *

import os,sys

Host = '0.0.0.0'

Post = 9696

address = (Host,Post)

chat_name = {}  ## name:conn

jobs = {}  ###conn:t  存储线程对象t   

#conn_stat = {}  ###  conn:'true'  存储套接字的状态 true为运行  false 为结束线程

def do_login(conn,name):        ##登录

    if name not in chat_name:

        conn.send(b'OK')

    else:

        conn.send("昵称已经存在".encode())

        return

    msg = "%s 进入9696聊天室" %name

    for c in chat_name:

        chat_name[c].send(msg.encode())

    chat_name[name] = conn

def do_chat(conn,msg,name):

    text = "%s :%s"%(name,msg)

    for n in chat_name:

        if n != name:

            chat_name[n].send(text.encode())

def do_quit(conn,name):

    msg = "%s 退出9696聊天室了"%name

    for c in chat_name:

        if c == name:

            conn.send(b'q')

        else:

            chat_name[c].send(msg.encode())

    del chat_name[name]

    #conn_stat[conn] = 'false'

    conn.close()

def do_request(conn):  ##用来处理客户端的请求

    while True:

        data = conn.recv(1024).decode().strip().split(' ')

        if data[0] == 'L':  ##登录

            name = data[-1]

            do_login(conn,name)

        elif data[0] == 'C':    ##聊天

            name = data[-1]

            msg = ''.join(data[1:-1])

            do_chat(conn,msg,name)

        elif data[0] == 'Q':  ##退出

            name = data[-1]

            do_quit(conn,name)

            break


def main():

    sockfd = socket()  ##tcp套接字

    sockfd.setsockopt(SOL_SOCKET,SO_REUSEADDR,1)    ##端口重用

    sockfd.bind(address)

    sockfd.listen(5)

    while True:

        try:

            conn,addr = sockfd.accept()

        except KeyboardInterrupt:

            sockfd.close()

            sys.exit("退出")

        except Exception as e:

            print(e)

            continue

        print("Connect from:",addr)

        t = Thread(target=do_request,args=(conn,))

        t.start()

        jobs[conn] = t

        #conn_stat[conn] = 'true'

        ###回收线程  当有线程结束,只有当新的客户端连接进来才会回收结束的线程

        for c in jobs:

            if c._closed is True:

                jobs[c].join()

if __name__ == '__main__':

    main()

4.运行结果

1.运行客户端和服务器

2.输入姓名

3.发送消息

4.退出客户端

运行中收到的消息会显示在“发言>>”之后,可以在其后加上“\r”让光标回到行首,效果会好一点。。。。。

你可能感兴趣的:(一起来做一个基于python的tcp聊天室吧)