物联网通信技术-基于Socket的聊天程序

欢迎光临

古之立大事者,不惟有超世之才,亦必有坚忍不拔之志。——苏轼
---------------🍎------------🍉--------------
🐼学编程的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地址是否变了,如何在客户端代码里面进行修改

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值