Linux 期末大作业(C语言实现网络编程)
设计并实现一个简单的聊天室程序,实现如下功能:
- 终端字符界面,支持用户管理,用户名/密码注册和登录
- 客户端登陆到服务器聊天室后,可以在聊天室内与其他多个用户交流,聊天室中的任何一个用户输入一段字符后,室内的其他用户都可以看到这句话;
- 客户端对应每一个参加聊天的用户,完成从终端上输入采集并传递到服务器端和从服务器端接收信息输出显示的功能。
采用状态机编程实现,根据用户的选择切换不同的功能模块。
// 枚举表示客户端的不同状态, 如初始化、菜单、发送消息等
typedef enum
{
STATUS_MENU,
STATUS_LOGIN,
STATUS_REGIST,
STATUS_QUIT,
STATUS_SEND_MSG
} ClientState;
// 枚举表示客户端和服务器之间的消息类型,如初始化消息、菜单消息、发送消息等。
typedef enum
{
MSG_LOGIN,
MSG_REGIST,
MSG_QUIT,
MSG_LOGIN_FAILED,
MSG_REGIST_FAILED,
MSG_SEND,
MSG_BROADCAST // 新增的消息类型,用于广播消息给其他用户
} MessageType;
- 用户管理:使用文件系统来保存用户信息,包括用户名和密码。
- 客户端和服务器通信:使用TCP协议实现客户端和服务器之间的通信。
- 消息广播:当一个用户发送消息时,服务器接收并将消息广播到所有在线用户。
- 显示菜单选项:首先打印出聊天室服务系统的主菜单,包括选项“1 登录”、“2 注册”和“3 退出系统”。
- 读取用户输入:函数使用
getchar()
函数从标准输入(通常是键盘)读取一个字符。 - 处理额外的输入:为了防止之后的输入受到之前输入的影响,使用一个循环来清除输入缓冲区中的剩余字符(包括回车符和换行符)。
- 根据输入切换状态:
- 如果用户输入'1',函数返回
STATUS_LOGIN
状态,表示用户选择了登录。 - 如果输入'2',则返回
STATUS_REGIST
状态,表示用户选择了注册。 - 如果输入'3',则打印退出提示并返回
STATUS_QUIT
状态,表示用户选择了退出程序。
- 如果用户输入'1',函数返回
- 处理无效输入:如果用户输入了除'1'、'2'或'3'以外的字符,函数会认为这是一个无效输入。此时,它会提示用户输入不合法,并重新展示菜单,要求用户重新输入。这个过程是在一个无限循环(
while(1)
)中进行的,只有在用户做出有效选择时才会退出循环。
- 初始化 JSON 对象:使用 cJSON 库创建一个新的 JSON 对象来存储登录信息。
- 获取用户名和密码:
- 显示登录提示信息。
- 调用
getUsernameAndPassword
函数获取用户输入的用户名和密码。 - 这个函数会检查输入的有效性,确保用户名和密码不为空且仅包含合法字符(字母、数字和下划线)。
- 封装登录信息:
- 将用户名和密码添加到 JSON 对象中。
- 添加消息类型(
MSG_LOGIN
),用于告知服务器这是一个登录请求。
- 发送 JSON 消息到服务器:
- 调用封装好的
sendJsonMessage
函数,将 JSON 对象序列化成字符串并发送给服务器。 - 释放 JSON 对象占用的内存。
- 调用封装好的
- 接收和处理服务器响应:
- 从服务器接收响应并解析为 JSON 对象。
- 检查响应中的消息类型,判断登录操作是否成功。
- 如果登录成功,从响应中提取用户ID,并更新全局变量(
user_id
和user_name
)。 - 显示登录成功的消息,并返回
STATUS_SEND_MSG
状态,表示用户可以进入聊天室。 - 如果登录失败,显示失败消息,并返回
STATUS_MENU
状态,允许用户重新尝试或执行其他操作。
客户端:
- 获取并验证用户输入的用户名和密码。
- 发送注册信息到服务端并等待响应。
- 接收服务端的响应,并根据响应类型给用户相应的反馈。
服务器:
- 服务端接收并解析客户端发送的JSON请求。
- 检查用户名是否已被使用。如果是,则返回“用户名已存在”的响应;如果不是,则继续进行注册流程。
- 生成新用户的唯一ID,并创建包含用户名、密码和用户ID的新用户JSON对象,加入到用户列表中。
- 创建一个包含用户ID的成功响应,并返回给客户端。
- 将更新后的用户列表保存到文件中。
客户端:
- 接收服务器消息:在无限循环中,持续从服务器接收消息。
- 解析和显示消息:解析接收到的 JSON 消息,提取消息类型和内容。
- 显示广播消息:如果消息类型是广播(
MSG_BROADCAST
),则显示来自其他用户的消息。 - 线程安全:使用互斥锁来确保在多线程环境中正确地处理共享资源。
服务器:
- 消息发送处理 (
handleSendMessage
函数):- 接收并解析客户端发送的消息(包含用户ID和消息内容)。
- 通过用户ID,获取发送者的用户名,用于日志记录和消息广播。
- 调用
broadcastMessageToAllExceptSender
函数广播消息到所有除发送者之外的在线用户。 - 创建并返回一个确认消息,告知发送者消息已成功发送。
- 消息广播 (
broadcastMessageToAllExceptSender
函数):- 遍历在线用户列表,寻找除发送者以外的所有用户。
- 为每个接收用户创建一个包含发送者ID、消息内容和消息类型(广播)的JSON对象。
- 将JSON对象转换为字符串,并发送到每个接收用户的套接字。
- 确保广播消息后及时释放分配的内存资源。