Python实现网络聊天室(支持多人聊天与私聊)

  • 实验名称:
    • 功能:
    • 概述
    • 代码解释

实验名称:

网络聊天室

功能:

i. 掌握利用Socket进行编程的技术
ii. 掌握多线程技术,保证双方可以同时发送
iii. 建立聊天工具
iv. 可以和单人聊天
v. 可以和多个人同时进行聊天
vi. 使用图形界面,显示双方的语录
vii. 程序可以在一定程度上进行错误识别

概述

实验通过聊天室可以完成单人或多人之间的聊天通信,功能的实现主要是通过Socket通信来实现。本次实验采用客户端/服务器(C/S)架构模式,通过Python语言来编写服务器端与客户端的程序。运用多线程可完成多点对多点的聊天。
服务器端程序主要用于接收用户信息,消息接收与转发。
客户端程序实现用户注册登录,聊天信息显示与信息输入。

代码解释

统计当前在线人数,并且将新用户加到用户列表中。
Serve.py。

import socket
import threading
import queue
import json  # json.dumps(some)打包 json.loads(some)解包
import os
import os.path
import sys
IP = '127.0.0.1'
PORT = 9999     # 端口
messages = queue.Queue()
users = []   # 0:userName 1:connection
lock = threading.Lock()
def onlines():    # 统计当前在线人员
online = []
for i in range(len(users)):
online.append(users[i][0])
return online
class ChatServer(threading.Thread):
global users, que, lock
def __init__(self):         # 构造函数
threading.Thread.__init__(self)
self.s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
os.chdir(sys.path[0])
# 接受来自客户端的用户名,如果用户名为空,使用用户的IP与端口作为用户名。如果用户名出现重复,则在出现的用户名依此加上后缀“2”、“3”、“4”……
def receive(self, conn, addr):             # 接收消息
user = conn.recv(1024)        # 用户名称
user = user.decode()
if user == '用户名不存在':
user = addr[0] + ':' + str(addr[1])
tag = 1
temp = user
for i in range(len(users)):     # 检验重名,则在重名用户后加数字
if users[i][0] == user:
tag = tag + 1
user = temp + str(tag)
users.append((user, conn))
USERS = onlines()
self.Load(USERS,addr)
# 在获取用户名后便会不断地接受用户端发来的消息(即聊天内容),结束后关闭连接。
try:
while True:
message = conn.recv(1024)            # 发送消息
message = message.decode()
message = user + ':' + message
self.Load(message,addr)
conn.close()
# 如果用户断开连接,将该用户从用户列表中删除,然后更新用户列表。
except:   
j = 0            # 用户断开连接
for man in users:
if man[0] == user:
users.pop(j)       # 服务器段删除退出的用户
break
j = j+1
USERS = onlines()
self.Load(USERS,addr)
conn.close()
# 将地址与数据(需发送给客户端)存入messages队列。
def Load(self, data, addr):
lock.acquire()
try:
messages.put((addr, data))
finally:
lock.release()        
# 服务端在接受到数据后,会对其进行一些处理然后发送给客户端,如下图,对于聊天内容,服务端直接发送给客户端,而对于用户列表,便由json.dumps处理后发送。
def sendData(self): # 发送数据
while  True:
if not messages.empty():
message = messages.get()
if isinstance(message[1], str):
for i in range(len(users)):
data = ' ' + message[1]
users[i][1].send(data.encode())
print(data)
print('\n')
if isinstance(message[1], list):
data = json.dumps(message[1])
for i in range(len(users)):
try:
users[i][1].send(data.encode())
except:
pass
def run(self):
self.s.bind((IP,PORT))
self.s.listen(5)
q = threading.Thread(target=self.sendData)
q.start()
while True:
conn, addr = self.s.accept()
t = threading.Thread(target=self.receive, args=(conn, addr))
t.start()
self.s.close()
if __name__ == '__main__':
cserver = ChatServer()
cserver.start()

Client.py

import socket
import tkinter
import tkinter.messagebox
import threading
import json
import tkinter.filedialog
from tkinter.scrolledtext import ScrolledText
IP = ''
PORT = ''
user = ''
listbox1 = ''  # 用于显示在线用户的列表框
show = 1  # 用于判断是开还是关闭列表框
users = []  # 在线用户列表
chat = '------Group chat-------'  # 聊天对象
#登陆窗口
root0 = tkinter.Tk()
root0.geometry("300x150")
root0.title('用户登陆窗口')
root0.resizable(0,0)
one = tkinter.Label(root0,width=300,height=150,bg="LightBlue")
one.pack()
IP0 = tkinter.StringVar()
IP0.set('')
USER = tkinter.StringVar()
USER.set('')
labelIP = tkinter.Label(root0,text='IP地址',bg="LightBlue")
labelIP.place(x=20,y=20,width=100,height=40)
entryIP = tkinter.Entry(root0, width=60, textvariable=IP0)
entryIP.place(x=120,y=25,width=100,height=30)
labelUSER = tkinter.Label(root0,text='用户名',bg="LightBlue")
labelUSER.place(x=20,y=70,width=100,height=40)
entryUSER = tkinter.Entry(root0, width=60, textvariable=USER)
entryUSER.place(x=120,y=75,width=100,height=30)
def Login(*args):
global IP, PORT, user
IP, PORT = entryIP.get().split(':')
user = entryUSER.get()
if not user:
tkinter.messagebox.showwarning('warning', message='用户名为空!')
else:
root0.destroy()
loginButton = tkinter.Button(root0, text ="登录", command = Login,bg="Yellow")
loginButton.place(x=135,y=110,width=40,height=25)
root0.bind('<Return>', Login)
root0.mainloop()
# 建立连接
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((IP, int(PORT)))
if user:
s.send(user.encode())  # 发送用户名
else:
s.send('用户名不存在'.encode())
user = IP + ':' + PORT
# 聊天窗口
root1 = tkinter.Tk()
root1.geometry("640x480")
root1.title('群聊')
root1.resizable(0,0)
# 消息界面
listbox = ScrolledText(root1)
listbox.place(x=5, y=0, width=640, height=320)
listbox.tag_config('tag1', foreground='red',backgroun="yellow")
listbox.insert(tkinter.END, '欢迎进入群聊,大家开始聊天吧!', 'tag1')
INPUT = tkinter.StringVar()
INPUT.set('')
entryIuput = tkinter.Entry(root1, width=120, textvariable=INPUT)
entryIuput.place(x=5,y=320,width=580,height=170)
# 在线用户列表
listbox1 = tkinter.Listbox(root1)
listbox1.place(x=510, y=0, width=130, height=320)
def send(*args):
message = entryIuput.get() + '~' + user + '~' + chat
s.send(message.encode())
INPUT.set('')
sendButton = tkinter.Button(root1, text ="\n发\n\n\n送",anchor = 'n',command = send,font=('Helvetica', 18),bg = 'white')
sendButton.place(x=585,y=320,width=55,height=300)
root1.bind('<Return>', send)
def receive():
global uses
while True:
data = s.recv(1024)
data = data.decode()
print(data)
try:
uses = json.loads(data)
listbox1.delete(0, tkinter.END)
listbox1.insert(tkinter.END, "当前在线用户")
listbox1.insert(tkinter.END, "------Group chat-------")
for x in range(len(uses)):
listbox1.insert(tkinter.END, uses[x])
users.append('------Group chat-------')
except:
data = data.split('~')
message = data[0]
userName = data[1]
chatwith = data[2]
message = '\n' + message
if chatwith == '------Group chat-------':   # 群聊
if userName == user:
listbox.insert(tkinter.END, message)
else:
listbox.insert(tkinter.END, message)
elif userName == user or chatwith == user:  # 私聊
if userName == user:
listbox.tag_config('tag2', foreground='red')
listbox.insert(tkinter.END, message, 'tag2')
else:
listbox.tag_config('tag3', foreground='green')
listbox.insert(tkinter.END, message,'tag3')
listbox.see(tkinter.END)
r = threading.Thread(target=receive)
r.start()  # 开始线程接收信息
root1.mainloop()
s.close()
## 如何改变文本的样式
*强调文本* _强调文本_
**加粗文本** __加粗文本__
==标记文本==
~~删除文本~~
> 引用文本
H~2~O is是液体。
2^10^ 运算结果是 1024.
## 插入链接与图片
链接: [link](https://www.csdn.net/).
图片: ![Alt](https://imgconvert.csdnimg.cn/aHR0cHM6Ly9hdmF0YXIuY3Nkbi5uZXQvNy83L0IvMV9yYWxmX2h4MTYzY29tLmpwZw)
带尺寸的图片: ![Alt](https://imgconvert.csdnimg.cn/aHR0cHM6Ly9hdmF0YXIuY3Nkbi5uZXQvNy83L0IvMV9yYWxmX2h4MTYzY29tLmpwZw =30x30)
居中的图片: ![Alt](https://imgconvert.csdnimg.cn/aHR0cHM6Ly9hdmF0YXIuY3Nkbi5uZXQvNy83L0IvMV9yYWxmX2h4MTYzY29tLmpwZw#pic_center)
居中并且带尺寸的图片: ![Alt](https://imgconvert.csdnimg.cn/aHR0cHM6Ly9hdmF0YXIuY3Nkbi5uZXQvNy83L0IvMV9yYWxmX2h4MTYzY29tLmpwZw#pic_center =30x30)
当然,我们为了让用户更加便捷,我们增加了图片拖拽功能。

本文地址:https://blog.csdn.net/qq_41395729/article/details/110928877