python----------基于UDP的聊天室程序

基于UDP的聊天室程序

功能

基于UDP的聊天室程序,实现多人聊天功能,能够实现:
1.用户注册
2.用户登录
3.公聊
4.私聊

知识点

1、服务器和客户端之间发送的消息均为字节流消息,因此在发送和接受消息时需分别执行加密 encode() 和解密 decode() 操作;
2、实现服务器和客户端之间的连接,首先应 sock = socket.socket(socket.AF_INET ,socket.SOCK_DGRAM) 创建一个空套接(其中socket.AF_INET指机器IP采用的是IPv4,socket.SOCK_DGRAM指机器采用的是TCP流式的传输协议类型),再通过 sock.bind((address,port)) 绑定并监听一个ip地址和端口号
3、sock.recvfrom(MAX_BYTES) 中的MAX_BYTES指的是一次性能接收数据消息的大小,而返回的是目标对象的数据流(即加密过后的消息)以及对方的地址

思路

client.py 中,发送给服务器端的消息(text)内容为以双空格为分隔的内容,消息内容在 server.py 中被切片后,存放在列表(text_list)中:
text_list[0]标识的是用户所期待执行的操作(注册、登陆、公聊、私聊、退出等);
注册和登陆 操作时:text_list[1]、text_list[2]分别用于存放用户名和密码;
公聊和私聊 操作时:text_list[1]、text_list[2]、text_list[3]分别用于存放用户名、IP地址和发送的消息内容;
退出 时:text_list[1]、text_list[2]分别用于保存用户名和IP地址信息。

保存的IP地址,用于在 退出 时服务器需找到对应的客户端,从而断开彼此之间的连接,以及在 公聊私聊 操作时,用于服务器遍历所有或者某一个IP地址发送消息。

源码

client.py

import socket
import sys
from multiprocessing import Process
import os

MAX_BYTES = 65535
ADDRESS = '127.0.0.1'
PORT = 1600


#将注册或者登陆信息打包发送给服务器端
def Person_Message(sock ,choice):
    name = input('please input name:')
    password = input('please input password:')
    text = str(choice) + '  ' + name + '  ' + password
    data = text.encode('ascii')
    sock.sendto(data ,(ADDRESS ,PORT))
    data ,address= sock.recvfrom(MAX_BYTES)
    return data.decode('ascii') ,name ,address


#将用户聊天信息传送给公共频道
def Chat_Message(sock ,name ,address):
    print('Please enter the chat content:\n\n(input \033[1;44mExit\033[0m to quit the room,\n'
          'input \033[1;44ms/name/message\033[0m for Private chat)\t\t\tHistory:')
    #创建进程,父进程发送消息,子进程接受消息
    p = Process(target = rcvmsg ,args = (sock ,name ,address))
    p.start()
    sendmsg(sock ,name ,address)


#发送消息
def sendmsg(sock ,name ,address):
    while True:
        message = input()
        Words = message.split('/')
        if Words[0] == 's':
            Destination = Words[1]
            true_message = Words[2]
            text = '4' + '  ' + name + '  ' + str(address) + '  ' + true_message + '  ' + Destination
            data = text.encode('ascii')
            sock.sendto(data ,(ADDRESS , PORT))
            print('OK!')
        elif message == 'Exit':
            text = '5' + '  ' + name + '  ' + str(address)
            data = text.encode('ascii')
            sock.sendto(data ,(ADDRESS ,PORT))
            sys.exit('You have exited the chat room\n')
        else:
            text = '3' + '  ' + name + '  ' + str(address) + '  ' + message
            data = text.encode('ascii')
            sock.sendto(data ,(ADDRESS ,PORT))


#接收消息
def rcvmsg(sock ,name ,address):
    while True:
        data ,address= sock.recvfrom(MAX_BYTES)
        message = data.decode('ascii')
        if message == 'exit':
            os._exit(0)
        else:
            print('\t\t\t\t\t\t' + message)

    
#套接字连接
def main():
    sock = socket.socket(socket.AF_INET ,socket.SOCK_DGRAM)
    while True:
        while True:
            choice = int(input('please select:\n 1、register 2、login\n'))
            if choice == 1 or choice == 2:
                break
            print('Unknown command')
        #signal标识注册或者登陆时返回的值
        signal ,name ,address = Person_Message(sock ,choice)
        if signal == 'OK':
            os.system("cls")
            print('\t\t\t\tYou have successfully entered the room\t\t')
            break
        elif signal == 'Error_UserExist':
            print('User already exists!')
        elif signal == 'Error_PasswordError':
            print('Password error!')
        elif signal == 'Error_UserNotExist':
            print('user does not exist!')
    Chat_Message(sock ,name ,address)


if __name__ == "__main__":
    main()

server.py

import socket
import datetime 

MAX_BYTES = 65535
ADDRESS = '127.0.0.1'
PORT = 1600

    
#选项菜单
def menu(sock ,Users_message):
    while True:
        # data ,address = sock.recvfrom(MAX_BYTES)
        # text = data.decode('ascii')
        # print('message from {} is {}'.format(address ,text))
        # text = 'hello ,too'
        # data = text.encode('ascii')
        # sock.sendto(data ,address)
        data ,address = sock.recvfrom(MAX_BYTES)
        text_list = data.decode('ascii').split('  ')

        if int(text_list[0]) == 1:
            #注册
            Register(sock ,Users_message ,text_list ,address)
        if int(text_list[0]) == 2:
            #登陆
            Login(sock ,Users_message ,text_list ,address)
        if int(text_list[0]) == 3:
            #公聊
            Public_chat(sock ,Users_message ,text_list)
        if int(text_list[0]) == 4:
            #私聊
            Private_chat(sock ,Users_message ,text_list)
        if int(text_list[0]) == 5:
            #退出
            Exit(sock ,Users_message ,text_list)

#注册
def Register(sock ,Users_message ,text_list ,address):
    name = text_list[1]
    password = text_list[2]
    if name in Users_message.keys():
        sock.sendto('Error_UserExist'.encode('ascii') ,address)
        print(Users_message)
    else:
        Users_message[name] = [password ,address]
        print(name + ' is enter the room')
        sock.sendto('OK'.encode('ascii') ,address)

#登陆
def Login(sock ,Users_message ,text_list ,address):
    name = text_list[1]
    password = text_list[2]
    if name in Users_message.keys():
        if Users_message[name][0] == password:
            sock.sendto('OK'.encode('ascii') ,address)
            print(name + ' is enter the room\n')
        else:
            sock.sendto('Error_PasswordError'.encode('ascii') ,address)
    else:
        sock.sendto('Error_UserNotExist'.encode('ascii') ,address)

#公聊
def Public_chat(sock ,Users_message ,text_list):
    name = text_list[1]
    #address = text_list[2]
    message = text_list[3]
    data = ('[' + name + ']:' + message)
    for user in Users_message.keys():
        if user != name:
            sock.sendto(data.encode('ascii') ,Users_message[user][1])
    print('[' + str(datetime.datetime.now()) + ']' + '[' + name + ']:' + message)


#私聊
def Private_chat(sock ,Users_message ,text_list):
    name = text_list[1]
    #address = text_list[2]
    message = text_list[3]
    Destination = text_list[4]
    data = ('[' + name + ']:' + message)
    for user in Users_message.keys():
        if user == Destination:
            sock.sendto(data.encode('ascii') ,Users_message[user][1])
            print('[' + str(datetime.datetime.now()) + ']' + '[' + name + ']' + ' to [' + Destination + ']: ' + message)


#退出程序
def Exit(sock ,Users_message ,text_list):
    name = text_list[1]
    address = text_list[2]
    print(address)
    data = 'exit'
    for user in Users_message.keys():
        if name == user:
            sock.sendto(data.encode('ascii') ,Users_message[user][1])
            print(name + ' is quit the room\n')
    

#套接字连接
def main():
    #用户信息存在字典中,实现可持久化存储可将用户信息写入txt等文本内
    Users_message={}
    sock = socket.socket(socket.AF_INET ,socket.SOCK_DGRAM)
    sock.bind((ADDRESS ,PORT))
    print('listen to {}'.format(sock.getsockname()))
    menu(sock ,Users_message)


if __name__ == "__main__":
    main()

测试结果

首先进行用户的登陆注册(Bob和Tom是提前注册好的)
python----------基于UDP的聊天室程序
之后分别执行:左下角在公聊频道打了声招呼,右上角对July私聊,右小角退出聊天室,均成功并且可以同时运行。
python----------基于UDP的聊天室程序

上一篇:服务识别


下一篇:TCP/IP协议栈在Linux内核中的运行时序分析