前言

笔者最近也捡了捡n多年前学过的一点python基础,接触了一些socket编程,也参考了一些其他大佬的博客。

看见一个比较有趣的题目:用socket制作一个聊天室,于是自己也试着做了一下。

参考博客:

在我的代码中我参考了上述博客的思路(客户端的脚本基本没什么改动),然后我在服务端的代码中加入了聊天记录的功能(通过写入文件),另外还有显示在线用户的功能。

程序

服务端

import socket
import time

# 创建UDP套接字
svrsocket = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
# 绑定
svrsocket.bind(('',8080))
# 聊天室内用户,用字典存放,{(ip:port):用户名}
users = {}

def send_record(msg):
    '''
    接收一个字符串
    打印并传递给write2file和broadcast
    通过该方法可在服务器和用户上看见用户进入/离开聊天室以及用户聊天信息
    同时将上述信息写入文件(通过write2file)
    '''
    print(msg)
    write2file(msg)
    broadcast(msg)

def write2file(string):
    '''
    接收一个字符串
    可将字符串作为单独的一行追加到文件末尾
    用于将聊天信息记录到文件中
    '''
    file = open('/root/python/chat_history.txt','a')
    file.write(string+"\n")
    file.close()

def broadcast(msg):
    '''
    在套接字中广播(给每个用户都发送msg)
    把每个用户发送的信息都发送给其他用户
    '''
    # 遍历用户字典users
    for address in users:
        svrsocket.sendto(msg.encode(), address)

while True:
    try:
        # 从UDP套接字接收到信息
        user_data, user_addr = svrsocket.recvfrom(1024)
        
        # 新用户加入聊天室
        if not user_addr in users:
            # 该用户进入聊天室的msg
            enter_msg = time.asctime() + "\n" + user_data.decode() + "进入聊天室..."
            # 该用户进入聊天室,发送提示并记录到文件
            send_record(enter_msg)
            # 将该用户添加到用户字典users
            users[user_addr] = user_data.decode('utf-8')
            # 新用户进入聊天室,无需再判断user_data
            continue


        # 用户输入"--EXIT"退出聊天
        if '--EXIT' in user_data.decode('utf-8'):
            # 用户离开聊天室的msg
            leave_msg = time.asctime() + "\n" + users[user_addr] + "离开了聊天室..."
            # 用户字典删除该用户
            users.pop(user_addr)
            # 用户退出聊天室,发送提示并记录到文件
            send_record(leave_msg)

        # 用户输入"--USERLIST"请求聊天室内在线用户列表
        elif '--USERLIST' in user_data.decode('utf-8'):
            # 遍历存放用户的字典users
            for user in users.keys():
                # list_msg = username+ip+port
                list_msg = (users[user]+': '+user[0]+":"+str(user[1])).encode()
                # 单独发送给请求userlist的用户
                svrsocket.sendto(list_msg,user_addr)

        else:
            # 聊天室内用户正常聊天
            msg = time.asctime() + '\n' +user_data.decode('utf-8')
            # 发送消息并记录
            send_record(msg)

    except ConnectionResetError:
        print("ConnectionResetError")

客户端

import socket
import threading

def send(sock,addr):
   '''
   该函数接受一个套接字和一个元组(地址,端口)
   可通过input()获取用户输入,将信息发送给套接字
   '''
   while True:
      # 获取用户输入
      string = input()
      # 发送输入信息
      message = name + ": " + string
      sock.sendto(message.encode('utf-8'),addr)
      # 用户输入exit退出聊天
      if string == '--EXIT':
         break

def recv(sock,addr):
   '''
   该函数接受一个套接字和一个元组(地址,端口)
   可通过套接字接收服务端发过来的信息
   '''
   sock.sendto(name.encode('utf-8'),addr)
   while True:
      data = sock.recv(1024)
      print(data.decode('utf-8'))
    
    
print('____WELCOME____\n'+'EXIT -> exits\nUSERLIST -> get users')
# 获取用户名
name = input('please input your name:')
# 建立套接字
socket = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
# 服务器IP地址为192.168.218.17,端口设置为8080
server = ('192.168.218.17',8080)
# 多线程接收和发送
tr = threading.Thread(target=recv,args=(socket,server),daemon=True)
ts = threading.Thread(target=send,args=(socket,server))
tr.start()
ts.start()
ts.join()
socket.close()

效果图

使用 Python 开发一个在线聊天室 python制作聊天软件_socket

总结

可以从效果图看出来,用户输入的信息和用户们的聊天记录混在一起,确实很影响用户体验,而且在聊天的时候没有任何提示符、消息的时间刷得太多同样也很影响使用。