欢迎光临
古之立大事者,不惟有超世之才,亦必有坚忍不拔之志。——苏轼
---------------🍎------------🍉--------------
🐼学编程的bird的博客,邀您一起学习🦌
----------------🥕------------🥭-------------😊很高兴你打开了这篇博客。
★如有疑问不解或者说有想问的地方,都可以在下方评论留言,博主看到会尽快回复的。
★当然,期待你的点赞+关注哦!★本人微信:wxid_v28nw5cckzz622,若需要帮助,请备注来源+意图
————————————————
版权声明:本文为CSDN博主「学编程的bird」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
1.目的
- 实现一个简单的基于Socket的聊天程序,支持多用户注册、登录、私聊和公聊功能。
- 通过实验了解Socket编程和多线程编程的基本原理。
2.环境
- 操作系统:Windows 10
- 编程语言:Python 3.8
- 开发工具:PyCharm Community Edition 2022.3、VMware Workstation Pro
3.内容
3.1服务器端
服务器端主要功能包括:
- 用户注册和登录验证
- 处理客户端消息,支持公聊和私聊
- 允许服务器发送消息
3.2客户端
客户端主要功能包括:
- 用户连接服务器并登录或注册
- 支持发送公聊和私聊消息
- 显示接收到的消息
4.预期结果
4.1服务器
4.1.1启动服务器
4.1.2客户端接入
4.1.3服务器在公聊频道发送消息
4.1.4客户端接收到信息
4.2客户端
4.2.1启动客户端1
4.2.2根据提示完成注册
4.2.3注册完成后进行登录
4.2.4同理,完成客户端2的登录
4.2.5客户端1和客户端2在公聊频道发送消息
Client1:发送消息
Client2:接收到消息并发送消息
4.2.6客户端client1和client2私聊
Client1:私聊发送消息
Client2:接收到私聊消息
5.源码分享
5.1服务器端
import socket
import threading
import json
# 存储用户信息
users = {}
# 加载用户数据
def load_users():
try:
with open('users.json', 'r') as file:
return json.load(file)
except FileNotFoundError:
return {}
# 保存用户数据
def save_users():
with open('users.json', 'w') as file:
json.dump(users, file)
# 广播消息
def broadcast_message(message, sender_socket, recipient=None):
if recipient:
if recipient in clients:
try:
clients[recipient].send(message)
except Exception as e:
print(f"发送消息给客户端时出错: {e}")
clients[recipient].close()
del clients[recipient]
else:
for client_name, client_socket in clients.items():
try:
client_socket.send(message)
except Exception as e:
print(f"发送消息给客户端时出错: {e}")
client_socket.close()
del clients[client_name]
# 处理客户端消息
def handle_client(client_socket, client_name):
current_channel = "all"
while True:
try:
message = client_socket.recv(1024)
if not message or message.decode('utf-8').strip() == '':
continue
decoded_message = message.decode('utf-8').strip()
if decoded_message.lower() == 'exit':
break
if decoded_message.startswith("/private"):
parts = decoded_message.split(" ", 2)
if len(parts) >= 3:
_, recipient, private_message = parts
if recipient in clients:
private_msg = f"[私聊]{client_name} 对你说: {private_message}"
broadcast_message(private_msg.encode('utf-8'), sender_socket=client_socket, recipient=recipient)
client_socket.send(f"[私聊]你对 {recipient} 说: {private_message}".encode('utf-8'))
else:
error_msg = f"用户 {recipient} 不在线。"
client_socket.send(error_msg.encode('utf-8'))
else:
error_msg = "私聊命令格式错误。使用 /private <用户名> <消息>。"
client_socket.send(error_msg.encode('utf-8'))
elif decoded_message.lower() == "/all":
current_channel = "all"
client_socket.send("你已进入公聊频道。\n".encode('utf-8'))
else:
public_msg = f"[公聊]{client_name}: {decoded_message}"
broadcast_message(public_msg.encode('utf-8'), sender_socket=client_socket)
except Exception as e:
print(f"处理客户端消息时出错: {e}")
break
client_socket.close()
del clients[client_name]
broadcast_message(f"{client_name} 已下线。".encode('utf-8'), sender_socket=client_socket)
# 处理登录和注册
def authenticate(client_socket):
while True:
try:
client_socket.send("请选择:1. 注册 2. 登录\n".encode('utf-8'))
option = client_socket.recv(1024).decode('utf-8').strip()
if option == '1':
client_socket.send("请输入用户名: ".encode('utf-8'))
username = client_socket.recv(1024).decode('utf-8').strip()
if username in users:
client_socket.send("用户名已存在,请重新输入。\n".encode('utf-8'))
continue
client_socket.send("请输入密码: ".encode('utf-8'))
password = client_socket.recv(1024).decode('utf-8').strip()
client_socket.send("请再次输入密码: ".encode('utf-8'))
password_confirm = client_socket.recv(1024).decode('utf-8').strip()
if password != password_confirm:
client_socket.send("两次输入的密码不一致,请重新注册。\n".encode('utf-8'))
continue
users[username] = password
save_users()
client_socket.send("注册成功,请重新登录。".encode('utf-8'))
continue
elif option == '2':
client_socket.send("请输入用户名: ".encode('utf-8'))
username = client_socket.recv(1024).decode('utf-8').strip()
client_socket.send("请输入密码: ".encode('utf-8'))
password = client_socket.recv(1024).decode('utf-8').strip()
if username in users and users[username] == password:
client_socket.send("登录成功。\n\n开始聊天吧~\n提示:使用 /private <用户名> <消息> 可进行私聊,使用 /all 返回公聊频道。\n".encode('utf-8'))
return username
else:
client_socket.send("用户名或密码错误,请重新登录。\n".encode('utf-8'))
else:
client_socket.send("无效选项,请重新选择。\n".encode('utf-8'))
except Exception as e:
print(f"认证时出错: {e}")
client_socket.close()
return None
# 服务器端发送消息
def server_send_message():
while True:
message = input()
if message.startswith("/private"):
parts = message.split(" ", 2)
if len(parts) >= 3:
_, recipient, private_message = parts
if recipient in clients:
private_msg = f"[服务器私聊] 对 {recipient} 说: {private_message}"
broadcast_message(private_msg.encode('utf-8'), sender_socket=None, recipient=recipient)
else:
print(f"用户 {recipient} 不在线。")
else:
print("私聊命令格式错误。使用 /private <用户名> <消息>。")
else:
public_msg = f"服务器: {message}"
broadcast_message(public_msg.encode('utf-8'), sender_socket=None)
# 启动服务器
def start_server(host='0.0.0.0', port=9999):
global users
users = load_users()
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.bind((host, port))
server.listen(5)
print(f"服务器正在监听 {host}:{port}")
threading.Thread(target=server_send_message).start()
while True:
client_socket, addr = server.accept()
client_handler = threading.Thread(target=client_auth_handler, args=(client_socket,))
client_handler.start()
def client_auth_handler(client_socket):
username = authenticate(client_socket)
if username:
if username in clients:
client_socket.send("该用户已登录。\n".encode('utf-8'))
client_socket.close()
return
clients[username] = client_socket
print(f"{username} 已连接。")
broadcast_message(f"{username} 已上线。".encode('utf-8'), sender_socket=client_socket)
handle_client(client_socket, username)
if __name__ == '__main__':
clients = {}
start_server()
5.2客户端
import socket
import threading
def receive_message(client_socket):
while True:
try:
message = client_socket.recv(1024).decode('utf-8')
if message:
print(message.strip())
else:
break
except Exception as e:
print(f"接收消息时出错: {e}")
break
def send_message(client_socket):
try:
while True:
message = input()
if message.strip() == '':
continue
if message.lower() == 'exit':
client_socket.send("exit".encode('utf-8'))
break
client_socket.send(message.encode('utf-8'))
except KeyboardInterrupt:
print("用户中断")
finally:
client_socket.close()
def main():
server_ip = '10.223.96.98' # 替换为你的主机IP地址
server_port = 9999 # 替换为你的服务器端口
client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client_socket.connect((server_ip, server_port))
receive_thread = threading.Thread(target=receive_message, args=(client_socket,))
receive_thread.start()
send_thread = threading.Thread(target=send_message, args=(client_socket,))
send_thread.start()
receive_thread.join()
send_thread.join()
if __name__ == '__main__':
main()
6.手把手教程
如果当你看完上面的项目相关内容并没有思路的话,那么我猜你和我应该都是一个编程小白,身为同水平的小小菜鸟,我必然知道大家头疼的点在于有源码但是不清楚怎么搭建。接下来,就进入我的手把手教程吧!
6.1VMware Workstation Pro
6.1.1软件介绍
首先解释一下,这个软件是干什么用的。这个一款用于搭建虚拟机的软件。
那么虚拟机又是干什么的呢?
虚拟机本质是在你的本地电脑上分配了一块空间,然后模拟出了一个可以不同于你的电脑系统的虚拟电脑,你可以在上面安装上不同的系统。例如:
我的电脑系统是Window 10 x64位,如果我想要使用Ubuntu 64位的话,通过这个软件就能模拟出来一个对应的系统。这意味着我就没必要在本机上装第二个系统,或者说再买一台电脑专门安装系统。
本次的项目里,把服务器安置在Window 10的虚拟机里面是为了模拟云服务器,大家有钱的话也可以直接购买一个云服务器,然后配置完好也是可以实现相同的功能的。
6.1.2软件安装
首先强调一点,咱们有能力的话,还是支持购买正版软件。博主确实兜里没多少钱,就整个破解版啦,啊哈哈,大家见谅。咱也不商用,就是自己玩玩。建议是安装【VMware Workstation 16 Pro中文破解版】这个版本相对来说比较经典和稳定。
这个软件的相关安装教程的话,其实一搜就一大把,我也就不再重复的赘述了。
稍微提示一下小伙伴们吧,安装完之后最难找的莫过于镜像了,我这里分享一个网站,便于大家找到自己想找的镜像
https://next.itellyou.cn/Original/Index
打开之后是这样:大家可以找到对应的系统下载镜像。
大家要下载的话点击’详情‘可以进入
大概这么个页面。然后点击’BT‘下面的'复制'。打开’迅雷‘或者其他的BT下载工具,就能下载了。
暂不支持网页直接下载哦
6.1.3虚拟机python环境配置
大家配置完虚拟机,安装好对应的系统,运行虚拟机之后,就可以看见桌面了
正常大家按照完之后,里面是不会有python的,需要大家自己按照。建议是在你自己的本机上下载完之后,直接拖拽到虚拟机里面就行。
python的官方下载地址:
https://python.p2hp.com/downloads/
找到虚拟机对应的系统的python版本,下载好,然后直接拖拽到虚拟机按照就行
6.1.4虚拟机入站规则配置
这里是重点!!!!不然大家到时候跑代码也跑不动的,需要把网关配置好
打开虚拟机里面的控制面板>系统和安全>防火墙>高级设置>入站规则>新建规则
然后选择’端口‘
'TCP' '9999'
'允许连接'
最后随便起个名字,点击确定就行
这一步配置完之后,将客户端代码中的IP地址换为虚拟机的IP地址之后,你在虚拟机运行服务器端的代码,本机运行客户端代码,就能够完成本地到虚拟机的一个通信了。不理解的可以看下面的示意图:
提示:查看虚拟机IP地址的方法,打开虚拟机的CMD窗口,然后出入’ipconfig‘即可
提示:代码修改位置
6.1.4虚拟机网络配置
如果你想要你的虚拟机里面的服务器,能够被你朋友、同事、老师(其他人的计算机)在同一个WIFI下可以连接的话,就需要配置这一步。只想在本机和虚拟机通信的话就不用看这一步。
点击虚拟机软件的’编辑‘里面的’虚拟网络编辑器‘
进入这个界面,点击’更改设置‘
选中‘VMnet8’ 然后点击‘NAT设置’
点击‘添加’
参考示意图填写数据
点击确定之后,就可以查看到你添加好的。我因为之前已经添加了,所有里面会有显示
之后就是一路都是确定,最后点击‘虚拟机’>'电源'>'重启虚拟机'。这样就完成了虚拟机的网关配置了。
6.1.5本机入站规则配置
如果你想要你的虚拟机里面的服务器,能够被你朋友、同事、老师(其他人的计算机)在同一个WIFI下可以连接的话,就需要配置这一步。只想在本机和虚拟机通信的话就不用看这一步。
接下来是要在本机的系统里面配置入口规则,可以参考‘6.1.3虚拟机环境配置’,和那个基本一样
打开本机里面的控制面板>系统和安全>防火墙>高级设置>入站规则>新建规则
此文章的6.1.4和6.1.5是为了完成在不同电脑之间的通信,示意图如下:
如果想要在别人电脑也可以聊天,把客户端代码里的端口和IP地址换成你的主机的即可。主机和虚拟机通信,只需要把端口和IP地址转换位虚拟机的即可。
提示:不同电脑需要在同一个网络下才能通信
6.2 运行代码
6.2.1服务器启动
在虚拟机按照好python环境之后,不需要下载pycharm,可以之间用cmd运行代码
新建一个文本文件
把服务器源代码复制到文本文件里面
保存之后,把后缀改为'py'即可
点击‘确定’
生成一个1.py文件
提示:如果看不到后缀,就看这一个提示
打开’此电脑‘
勾选'文件扩展名'
之后在cmd进入对应存放文件的路径,输入python 1.py即可
6.2.2客户端启动
运行客户端代码
出现登录注册选项的时候就代表了解成功了
如果想要多个账号聊天,就多启动几个客户端就行了
提示:你在不同网络下的IP地址是不一样的,连接不通的时候注意查看本机的IP地址是否变了,如何在客户端代码里面进行修改