做网站国家大学科技园郑州,长春市城乡建设局网站,2008服务器做网站,建筑工程公司宣传册设计样本聊天室系统 SQLite库 服务器 客户端
聊天室系统
当设计一个聊天室系统时#xff0c;会涉及到许多方面的知识。简单的项目结构#xff0c;从不同的方面对聊天室的设计进行分析。请注意#xff0c;以下的结构只是一个示例#xff0c;实际项目可能会更加复杂#xff0c;具体…聊天室系统 SQLite库 服务器 客户端
聊天室系统
当设计一个聊天室系统时会涉及到许多方面的知识。简单的项目结构从不同的方面对聊天室的设计进行分析。请注意以下的结构只是一个示例实际项目可能会更加复杂具体要求取决于项目的规模和功能。
项目结构
1. 服务器端(Server): server.c: 聊天室服务器端的主程序。 server_functions.h / server_functions.c: 包含服务器端处理逻辑的函数的声明和定义。 user_manager.h / user_manager.c: 用户管理相关的函数如用户登录、注册、注销等。 chat_handler.h / chat_handler.c: 聊天相关的函数处理消息的发送、接收、群聊、私聊等功能。 file_handler.h / file_handler.c: 文件传输相关的函数处理文件的发送和接收。 network_handler.h / network_handler.c: 处理网络连接的函数包括创建套接字、绑定端口、监听等。 Makefile: 用于编译服务器端程序。
2. 客户端(Client): client.c: 聊天室客户端的主程序。 client_functions.h / client_functions.c: 包含客户端处理逻辑的函数的声明和定义。 user_interface.h / user_interface.c: 用户界面相关的函数处理用户输入、输出包括聊天框、输入框等。 chat_handler.h / chat_handler.c: 聊天相关的函数处理消息的发送、接收、群聊、私聊等功能。 file_handler.h / file_handler.c: 文件传输相关的函数处理文件的发送和接收。 network_handler.h / network_handler.c: 处理网络连接的函数包括创建套接字、连接服务器等。 Makefile: 用于编译客户端程序。
3. 共享的数据结构 (Common):
data_structures.h: 包含服务器端和客户端都需要使用的数据结构的定义例如表示用户信息的结构体等。
关键知识点 套接字编程(Socket Programming): 服务器和客户端之间的通信通过套接字进行。服务器通过 socket、bind、listen 等系统调用创建套接字而客户端通过 socket、connect 连接到服务器。 多线程 (Multithreading): 为了同时处理多个客户端的连接服务器通常使用多线程。每个客户端连接都在一个单独的线程中处理。 文件传输 (File Transfer): 聊天室系统可能包含文件传输的功能。这需要服务器和客户端能够将文件分块发送和接收并且能够在聊天中处理文件传输请求。 用户管理 (User Management): 用户登录、注册、注销等功能需要在服务器端进行管理。这可能涉及到用户信息的存储和验证。 聊天处理 (Chat Handling): 处理群聊、私聊等聊天功能包括消息的发送和接收。 用户界面 (User Interface): 客户端通常需要一个用户友好的界面以便用户能够方便地输入消息和查看聊天记录。 Makefile: 用于自动化编译过程将各个源文件组合成可执行文件。
项目的实现 服务器端(Server): server.c 中创建服务器套接字接受客户端连接并创建线程处理每个客户端的请求。 server_functions.c 中实现了服务器端的各种处理逻辑包括用户管理、聊天处理等。 user_manager.c 处理用户的登录、注册、注销等操作。 chat_handler.c 处理群聊、私聊消息的发送和接收。 file_handler.c 处理文件传输的相关操作。 network_handler.c 中实现了创建套接字、绑定端口、监听等网络处理相关的函数。 客户端(Client): client.c 中创建客户端套接字连接到服务器并创建线程处理接收消息。 client_functions.c 中实现了客户端的各种处理逻辑包括用户界面、聊天处理等。 user_interface.c 处理用户界面的输入和输出包括聊天框、输入框等。 chat_handler.c 处理群聊、私聊消息的发送和接收。 file_handler.c 处理文件传输的相关操作。 network_handler.c 中实现了创建套接字、连接服务器等网络处理相关的函数。 共享的数据结构 (Common): data_structures.h 定义了服务器端和客户端都需要使用的数据结构例如表示用户信息的结构体等。
如何进一步完善 数据存储: 真实的聊天室系统通常需要一个持久化的数据存储机制以保存用户信息、聊天记录等。 安全性: 在真实系统中需要考虑数据传输的安全性例如使用加密技术保护用户的隐私信息。 异常处理: 在实际项目中应该对各种异常情况进行处理例如网络连接断开、文件传输失败等。 扩展性: 考虑系统的扩展性以支持更多的功能例如群组管理、消息撤回等。 用户验证: 引入更强的用户验证机制防止恶意用户的攻击。 图形界面 (GUI): 在客户端中引入图形用户界面提供更好的用户体验。
以上只是一个简单的示例项目结构和知识点实际项目中可能需要更多的细节和优化。希望这个示例对你理解聊天室系统的设计有所帮助。如果有进一步的问题或需要详细的解释请随时提问。
SQLite库
安装 SQLite3 库通常包括以下步骤
1. 安装 SQLite3 库
【SQLite】环境安装
在 Ubuntu 或 Debian 上
sudo apt-get update
sudo apt-get install sqlite3 libsqlite3-dev在 CentOS 或 RHEL 上
sudo yum install sqlite sqlite-devel在 macOS 上
brew install sqlite2. 编译时链接 SQLite3 库
在编译你的程序时确保链接 SQLite3 库。在编译命令中添加 -lsqlite3例如
gcc -o your_executable your_source_code.c -lpthread -lsqlite33. 使用 SQLite3 命令行工具
SQLite3 还提供了一个命令行工具你可以用来管理和查询 SQLite3 数据库。你可以通过以下方式运行它
sqlite3这些步骤应该能够满足你在使用 SQLite3 库时的基本需求。请注意SQLite3 库是一个轻量级的嵌入式数据库无需独立的数据库服务器。
服务器
实现了一个聊天室的功能选择函数包含了不同的功能分支。以下是对每个功能分支的详细分析 整体结构和输入参数 函数名Function输入参数一个结构体 head该结构体包含了客户端发送的消息相关信息如发送者姓名、消息内容、接收者姓名、标志位等。 变量声明和初始化 函数一开始声明了一系列指向不同链表头结点的指针如 F_head、R_head、h、G_head、V_head 等用于管理禁言列表、管理员列表、在线用户列表、群组列表和VIP用户列表。temp 变量用于保存用户原本的权限flag 则用于检测用户是否为VIP用户。在检测用户是否为VIP用户后可能会修改用户权限因此将 temp 保存原本的权限以便后续恢复。 退出群聊功能 当 head.flag 为 -1 时表示用户选择退出群聊。调用 delete_group 函数将用户从群组中删除。 群发消息功能 当 head.flag 为 0 时表示用户选择向所有在线用户发送消息。通过循环遍历在线用户列表将消息逐一发送给每个在线用户。 私聊功能 当 head.flag 为 1 时表示用户选择进行私聊。通过 searchuser 函数查找目标用户的文件描述符并发送私聊消息给目标用户。在消息内容前添加不同颜色的标记以区分消息类型。 群聊功能 当 head.flag 为 2 时表示用户选择进行群聊。根据 temp 的值判断用户是否为管理员是否具有发送群消息的权限。向群组中的每个成员发送群消息并记录聊天记录到文件 “log.txt” 中。 文件传输功能 当 head.flag 为 3 时表示用户选择进行文件传输。通过 searchuser 函数查找目标用户的文件描述符发送文件传输相关信息和文件内容。 修改密码功能 当 head.flag 为 4 时表示用户选择修改密码。通过 read 函数接收用户发送的新密码并调用 update_pw 和 updatedb 函数更新用户密码。 禁言、解除禁言、踢人功能 当 head.flag 为 5 时表示用户选择进行禁言、解除禁言、踢人操作。根据 head.forbit 的值执行相应的操作包括禁言用户、解除禁言、踢出群聊等。在操作过程中发送通知给相关用户。
总体而言实现了多个聊天室功能包括私聊、群聊、文件传输、修改密码、禁言、解禁等。注释中使用了不同颜色和样式以便在终端中更好地展示不同类型的消息。
server.h
#ifndef SERVER_H
#define SERVER_H#include stdio.h
#include stdlib.h
#include time.h
#include string.h
#include unistd.h
#include sys/types.h
#include sys/socket.h
#include netinet/in.h
#include arpa/inet.h
#include pthread.h
#include semaphore.h
#include sqlite3.hint serverfd; // 服务器 socket描述符int clientfd[100]; // 存储客户端的 socket 描述符数组最多支持 100 个客户端,100个元素,clientfd[0]~clientfd[99]int size 50; // 控制聊天室中的最大用户数char *IP 192.168.1.145; // 主机 IP 地址short PORT 12345; // 端口号typedef struct sockaddr meng;time_t nowtime; // 用于存储时间// 创建结构体 表示客户端的信息包含标志位、权限标志位、禁言标志位、用户名、密码、消息、操作对象等信息
struct client
{int flag; // 功能标志位 -1:退出群聊 0:通知所有在线用户 1:私聊 2:群聊 3.发送文件 4.修改密码int root; // 权限标志位 -1:首次进入聊天室 0:普通用户 1:管理员 2:VIP用户int forbit; // 管理员权限 1:禁言 2:解除禁言 3:踢人char name[50]; // 账号名char password[20]; // 密码char msg[500]; // 聊天信息char to_name[50]; // 操作对象struct client *next;
};struct Forbit // 存放被禁言人员
{char name[50];struct Forbit *next;
};struct Root // 存放管理员名单
{char root[50]; // 管理员用户struct Root *next;
};struct VIP // 存放vip用户名单
{char name[50]; // 管理员用户struct VIP *next;
};struct user // 用来存放已登录用户
{int c_fd;char name[50];struct user *next;
};struct Group // 存放群内成员
{char name[50]; // 群内成员用户struct Group *next;
};struct i_fd // 线程索引和客户端文件描述符
{int i;int c_fd;
};struct Node // 存放已注册用户
{char name[50];char password[20];struct Node *next;
};// 把文件里的存到链表里,每次打开时初始化使用
void insert_fp(struct Root *root);// 创建管理员用户链表头节点
int R_init(struct Root **root);// 把文件里的存到链表里每次打开时初始化使用
void V_insert_fp(struct VIP *vip);// 创建VIP用户链表头节点
int V_init(struct VIP **vip);// 创建已登录用户链表头节点
int init_l(struct user **head);// 创建已注册用户链表头节点
int init_r(struct Node **head);// 创建被禁言人员链表头节点
int F_init(struct Forbit **head);// 创建群内成员链表头节点
int G_init(struct Group **head);// 添加被禁言人员
int insert_forbit(struct Forbit *head, char *name);// 添加群内成员
int insert_group(struct Group *head, char *name);// 移除被禁言人员
int delete_forbit(struct Forbit *head, char *name);// 移除群内成员
int delete_group(struct Group *head, char *name);// 添加已登录用户
struct user *insert_tail(struct user *head, int c_fd, char *name);// 添加注册用户
struct Node *insert(struct Node *head, char *name, char *password);// 搜索存在用户名是否存在,存在返回线程号
int searchuser(struct user *head, char *name);// 初始化服务器
void Socket();// 插入用户信息到数据库
void insertdb(struct Node *head);// 搜索密码
void search_pw(char *n, char *pw);// 搜索用户名是否存在
int search_user(char *n);// 搜索是否已经登录
int search_log(char *n);// 搜索该成员是否为群成员
int seek_Group(char *n);// 搜索该成员是否为VIP用户
int seek_VIP(char *n);// 搜索该成员是否被禁言
int seek_forbit(char *n);// 更新用户密码
void update_pw(char *name, char *pw);// 显示在线用户
void display(struct user *head);// 创建数据库表
void createtable();// 检查数据库中是否存在指定表
int searchtable(char *tablename);// 初始化数据库
void initdb();// 执行SQL命令
void carryout(char *sql);// 更新数据库中注册用户信息
void updatedb(char *name, char *pw);// 将已注册的用户信息载入链表
void downdb(struct Node *head);// 聊天室功能选择
int Function(struct client head);// 接收客户端结构体,并作出处理
void *server_thread(void *p);// 登录
int logon(int c_fd);// 注册
int user_register(int c_fd);// 登录、注册
void *register_or_log(void *tt);// 启动服务器
void server();#endif // SERVER_Hserver.c
/* 聊天室服务器端的主程序 */
#include server.hint personalflag[500] {-1};
sem_t sem[500];
sem_t sem1[500];
struct user *hl; // 已登录用户头节点
struct Node *hr; // 已注册用户头节点
pthread_t pthuser[500];
struct Forbit *F_head; // 禁言人员头节点
struct Root *R_head; // 管理员人员头节点
struct Group *G_head; // 群内成员头结点
struct VIP *V_head; // VIP用户头结点// 把文件里的存到链表里,每次打开时初始化使用
void insert_fp(struct Root *root)
{int cnt;FILE *fp;FILE *dp;dp fopen(./R_num.txt, r); // 打开文件R_num.txt该文件用于记录链表中元素的数量fscanf(dp, %d, cnt); // 读取链表中元素的数量fclose(dp);fp fopen(./root.txt, a); // 打开文件root.txt该文件用于存储链表中的元素数据while (cnt ! 0) // 遍历文件中的数据并插入链表{struct Root *newcilent (struct Root *)malloc(sizeof(struct Root)); // 申请新节点的空间if (NULL newcilent){// 处理内存分配失败的情况return;}fscanf(fp, %s \n, newcilent-root); // 从文件中读取数据到新节点newcilent-next NULL; // 设置新节点的下一个节点为NULLroot-next newcilent; // 将新节点插入到链表中root root-next; // 更新链表尾部指针cnt--;}fclose(fp); // 关闭文件
}// 创建管理员用户链表头节点
int R_init(struct Root **root)
{struct Root *newcilent (struct Root *)malloc(sizeof(struct Root)); // 为链表头节点分配内存if (NULL newcilent){return -1; // 内存分配失败返回错误码}newcilent-next NULL; // 设置链表头节点的下一个节点为NULL*root newcilent; // 将链表头指针指向新创建的节点int cnt 0; // 计数储存文件初始化FILE *dp;char ch;dp fopen(./R_num.txt, a); // 如果没有创建一个并且不会覆盖原有的数据fclose(dp);dp fopen(./R_num.txt, r); // 关闭之后再以只读模式打开ch fgetc(dp);fclose(dp);if (ch EOF) // 如果chEOF证明文件为空初始化应该存个零进去{dp fopen(./R_num.txt, w); // 写会覆盖fprintf(dp, %d, cnt); // 将计数值写入文件fclose(dp);}else if (ch ! EOF) // 如果不为空则把之前存的文件放进链表{insert_fp(*root); // 调用insert_fp函数将文件内容插入链表}return 0; // 返回成功
}// 把文件里的存到链表里每次打开时初始化使用
void V_insert_fp(struct VIP *vip)
{int cnt;FILE *fp;FILE *dp;dp fopen(./V_num.txt, r); // 打开文件V_num.txt以读取计数值fscanf(dp, %d, cnt); // 读取文件中的计数值fclose(dp);fp fopen(./VIP.txt, a); // 以附加和读取方式打开VIP.txt文件while (cnt ! 0){struct VIP *newcilent (struct VIP *)malloc(sizeof(struct VIP)); // 为VIP链表节点分配内存if (NULL newcilent){return; // 内存分配失败退出函数}fscanf(fp, %s \n, newcilent-name); // 从文件中读取VIP用户信息newcilent-next NULL; // 设置VIP链表节点的下一个节点为NULLvip-next newcilent; // 将新节点插入VIP链表vip vip-next; // 移动VIP链表指针到新节点cnt--;}fclose(fp); // 关闭文件
}// 创建VIP用户链表头节点
int V_init(struct VIP **vip)
{struct VIP *newcilent (struct VIP *)malloc(sizeof(struct VIP)); // 为VIP链表头节点分配内存if (NULL newcilent){return -1; // 内存分配失败返回错误码}newcilent-next NULL; // 设置VIP链表头节点的下一个节点为NULL*vip newcilent; // 将VIP链表头节点指针传递给调用者int cnt 0; // 计数变量用于存储文件初始化FILE *dp;char ch;dp fopen(./V_num.txt, a); // 如果文件不存在则创建一个而且不会覆盖原有的数据fclose(dp);dp fopen(./V_num.txt, r); // 关闭之后再以只读模式打开ch fgetc(dp);fclose(dp);if (ch EOF) // 如果chEOF证明文件为空初始化应该存个零进去{dp fopen(./V_num.txt, w); // 以写模式打开会覆盖原有数据fprintf(dp, %d, cnt); // 将计数值写入文件fclose(dp);}else if (ch ! EOF) // 如果文件不为空将之前存的文件放进链表{V_insert_fp(*vip); // 调用V_insert_fp函数将文件中的VIP用户信息插入VIP链表}return 0; // 返回成功
}// 创建已登录用户链表头节点
int init_l(struct user **head)
{struct user *newnode (struct user *)malloc(sizeof(struct user)); // 为已登录用户链表头节点分配内存if (NULL newnode){return -1; // 内存分配失败返回错误码}newnode-c_fd 0; // 初始化已登录用户链表头节点的客户端文件描述符为0newnode-name[0] 0; // 初始化已登录用户链表头节点的用户名为空字符串newnode-next NULL; // 设置已登录用户链表头节点的下一个节点为NULL*head newnode; // 将已登录用户链表头节点指针传递给调用者return 0; // 返回成功
}// 创建已注册用户链表头节点
int init_r(struct Node **head)
{struct Node *newnode (struct Node *)malloc(sizeof(struct Node)); // 为已注册用户链表头节点分配内存if (NULL newnode){return -1; // 内存分配失败返回错误码}newnode-name[0] 0; // 初始化已注册用户链表头节点的用户名为空字符串newnode-password[0] 0; // 初始化已注册用户链表头节点的密码为空字符串newnode-next NULL; // 设置已注册用户链表头节点的下一个节点为NULL*head newnode; // 将已注册用户链表头节点指针传递给调用者return 0; // 返回成功
}// 创建被禁言人员链表头节点
int F_init(struct Forbit **head)
{struct Forbit *newnode (struct Forbit *)malloc(sizeof(struct Forbit)); // 为被禁言人员链表头节点分配内存if (NULL newnode){return -1; // 内存分配失败返回错误码}newnode-name[0] 0; // 初始化被禁言人员链表头节点的用户名为空字符串newnode-next NULL; // 设置被禁言人员链表头节点的下一个节点为NULL*head newnode; // 将被禁言人员链表头节点指针传递给调用者return 0; // 返回成功
}// 创建群内成员链表头节点
int G_init(struct Group **head)
{struct Group *newnode (struct Group *)malloc(sizeof(struct Group)); // 为群内成员链表头节点分配内存if (NULL newnode){return -1; // 内存分配失败返回错误码}newnode-name[0] 0; // 初始化群内成员链表头节点的用户名为空字符串newnode-next NULL; // 设置群内成员链表头节点的下一个节点为NULL*head newnode; // 将群内成员链表头节点指针传递给调用者return 0; // 返回成功
}// 添加被禁言人员
int insert_forbit(struct Forbit *head, char *name)
{struct Forbit *newnode (struct Forbit *)malloc(sizeof(struct Forbit)); // 为新的被禁言节点分配内存if (NULL newnode){return -1; // 内存分配失败返回错误码}strcpy(newnode-name, name); // 将被禁言人员的用户名复制到新节点newnode-next NULL; // 设置新节点的下一个节点为NULLwhile (head-next ! NULL){if (strcmp(head-next-name, name) 0){return 1; // 如果被禁言人员已存在返回错误码}head head-next; // 移动到下一个节点}head-next newnode; // 将新节点添加到链表的末尾return 0; // 返回成功
}// 添加群内成员
int insert_group(struct Group *head, char *name)
{struct Group *newnode (struct Group *)malloc(sizeof(struct Group)); // 为新的群内成员节点分配内存if (NULL newnode){return -1; // 内存分配失败返回错误码}strcpy(newnode-name, name); // 将群内成员的用户名复制到新节点newnode-next NULL; // 设置新节点的下一个节点为NULLwhile (head-next ! NULL){if (strcmp(head-next-name, name) 0){return 1; // 如果群内成员已存在返回错误码}head head-next; // 移动到下一个节点}head-next newnode; // 将新节点添加到链表的末尾return 0; // 返回成功
}// 移除被禁言人员
int delete_forbit(struct Forbit *head, char *name)
{int count 0; // 计数器记录删除的节点数量while (head-next ! NULL){if (strcmp(head-next-name, name) 0) // 如果找到被禁言的用户{struct Forbit *ptr head-next; // 保存找到的节点的指针head-next ptr-next; // 调整链表指针跳过被禁言的用户节点free(ptr); // 释放被禁言用户的节点内存count; // 增加计数器}else{head head-next; // 移动到下一个节点}}if (count 0){return 1; // 未找到被禁言用户返回错误码}return 0; // 返回成功
}// 移除群内成员
int delete_group(struct Group *head, char *name)
{int count 0; // 计数器记录删除的节点数量while (head-next ! NULL){if (strcmp(head-next-name, name) 0) // 如果找到群成员{struct Group *ptr head-next; // 保存找到的节点的指针head-next ptr-next; // 调整链表指针跳过群成员节点free(ptr); // 释放群成员节点内存count; // 增加计数器}else{head head-next; // 移动到下一个节点}}if (count 0){return 1; // 该用户不在群内返回错误码}return 0; // 返回成功
}// 添加已登录用户
struct user *insert_tail(struct user *head, int c_fd, char *name)
{struct user *newnode (struct user *)malloc(sizeof(struct user)); // 申请新节点的内存空间if (NULL newnode){return NULL; // 内存分配失败返回空指针}newnode-c_fd c_fd; // 设置新节点的客户端文件描述符strcpy(newnode-name, name); // 复制用户名到新节点newnode-next NULL; // 新节点的下一个节点设为空while (head-next ! NULL){head head-next; // 移动到链表尾部}head-next newnode; // 将新节点添加到链表尾部return newnode; // 返回新节点的指针
}// 添加注册用户
struct Node *insert(struct Node *head, char *name, char *password)
{struct Node *newnode (struct Node *)malloc(sizeof(struct Node)); // 申请新节点的内存空间if (NULL newnode){return NULL; // 内存分配失败返回空指针}strcpy(newnode-name, name); // 复制用户名到新节点strcpy(newnode-password, password); // 复制密码到新节点newnode-next NULL; // 新节点的下一个节点设为空while (head-next ! NULL){head head-next; // 移动到链表尾部}head-next newnode; // 将新节点添加到链表尾部return newnode; // 返回新节点的指针
}// 搜索存在用户名是否存在,存在返回线程号
int searchuser(struct user *head, char *name)
{while (head-next ! NULL){if (strcmp(head-next-name, name) 0){return head-next-c_fd; // 如果找到用户名返回对应用户的线程号}else{head head-next;}}int flag search_user(name); // 如果在已登录用户链表中未找到调用search_user函数在已注册用户链表中查找return flag; // 如果用户存在返回 1否则返回 0
}// 初始化服务器
void Socket()
{// 创建 socketserverfd socket(PF_INET, SOCK_STREAM, 0);printf(\033[0;34m创建socket成功!\033[0m\n);if (serverfd -1){perror(\033[0;31m创建socket失败\033[0m);exit(-1);}// 为套接字设置ip协议 设置端口号 并自动获取本机ip转化为网络ipstruct sockaddr_in addr; // 存储套接字的信息addr.sin_family PF_INET; // 地址族addr.sin_port htons(PORT); // 设置server端端口号,你可以随便设置,当sin_port 0时,系统随机选择一个未被使用的端口号addr.sin_addr.s_addr inet_addr(IP); // 把127.0.0.1改为自己的server端的ip地址,当sin_addr INADDR_ANY时,表示从本机的任一网卡接收数据// 绑定套接字if (bind(serverfd, (meng *)addr, sizeof(addr)) -1){perror(\033[0;31m绑定失败\033[0m);exit(-1);}// 监听最大连接数if (listen(serverfd, 100) -1){perror(\033[0;31m设置监听失败!\033[0m);exit(-1);}
}// 插入用户信息到数据库
void insertdb(struct Node *head)
{char sql[500] \0;// 构造插入 SQL 语句sprintf(sql, insert into user (name,password) values(%s,%s);, head-name, head-password); // 插入,修改,删除都可以用这条语句// 执行 SQL 语句carryout(sql);
}// 搜索密码
void search_pw(char *n, char *pw)
{struct Node *head hr;while (head-next ! NULL){head head-next;if (strcmp(n, head-name) 0){strcpy(pw, head-password);}}
}// 搜索用户名是否存在
int search_user(char *n)
{int count 0;struct Node *head hr;while (head-next ! NULL){head head-next;if (strcmp(n, head-name) 0){count;// printf(count:%d\n, count);}}return count;
}// 搜索是否已经登录
int search_log(char *n)
{// printf(搜索是否已经登录\n);int flag 0;struct user *head hl;while (head-next ! NULL){head head-next;// printf(head.name:%s\n, head-name);if (strcmp(n, head-name) 0){// printf(已经登录\n);flag 1;// printf(搜索完成\n);return 1;}}if (flag 0){return 0;}// printf(搜索完成\n);
}// 搜索该成员是否为群成员
int seek_Group(char *n)
{struct Group *head G_head;while (head-next ! NULL){head head-next;if (strcmp(n, head-name) 0) // 该用户为群成员{return 1;}}return 0; // 该用户不是群成员
}// 搜索该成员是否为VIP用户
int seek_VIP(char *n)
{struct VIP *head V_head;while (head-next ! NULL){head head-next;if (strcmp(n, head-name) 0) // 该用户为VIP用户{return 1;}}return 0; // 该用户不是VIP用户
}// 搜索该成员是否被禁言
int seek_forbit(char *n)
{struct Forbit *head F_head;while (head-next ! NULL){head head-next;if (strcmp(n, head-name) 0) // 该用户被禁言{return 1;}}return 0; // 该用户未被禁言
}// 更新用户密码
void update_pw(char *name, char *pw)
{struct Node *head hr;while (head-next ! NULL){head head-next;if (strcmp(name, head-name) 0){strcpy(head-password, pw);}}
}// 显示在线用户
void display(struct user *head)
{while (head-next ! NULL){// printf(c_fd%d\nname%s\n, head-next-c_fd, head-next-name);head head-next;}
}// 创建数据库表
void createtable()
{sqlite3 *db NULL;int ret sqlite3_open(info.db, db); // 打开数据库if (SQLITE_OK ! ret){perror(sqlite3_open:);exit(1);}char *errmsg;char sql[500] \0; // 写入命令strcpy(sql, create table user(name text,password text););ret sqlite3_exec(db, sql, NULL, NULL, errmsg);if (SQLITE_OK ! ret){perror(sqlite3_exec:);printf(errmsg:%s\n, errmsg);exit(2);}ret sqlite3_close(db); // 关闭数据库if (SQLITE_OK ! ret){perror(sqlite3_close:);exit(3);}return;
}// 检查数据库中是否存在指定表
int searchtable(char *tablename)
{sqlite3 *db NULL;int ret sqlite3_open(info.db, db); // 打开数据库if (SQLITE_OK ! ret){perror(sqlite3_open:);exit(1);}char sql[200] \0;sprintf(sql, select name from sqlite_master where typetable AND name%s;, tablename);char *errmsg;char **result;int row, column;ret sqlite3_get_table(db, sql, result, row, column, errmsg); // 分别为文件描述符,数据库命令,查询结果,行,列,错误信息if (SQLITE_OK ! ret){perror(sqlite3_exec:);printf(errmsg:%s\n, errmsg);exit(2);}if (row 1){return 1;}else{return 0;}sqlite3_free_table(result); // 释放内存ret sqlite3_close(db); // 关闭数据库if (SQLITE_OK ! ret){perror(sqlite3_close:);exit(3);}
}// 初始化数据库
void initdb()
{printf(正在进行初始化!\n);if (searchtable(user) 0){createtable();}
}// 执行SQL命令
void carryout(char *sql)
{sqlite3 *db NULL;int ret sqlite3_open(info.db, db); // 打开数据库if (SQLITE_OK ! ret){perror(sqlite3_open:);exit(1);}char *errmsg;ret sqlite3_exec(db, sql, NULL, NULL, errmsg);if (SQLITE_OK ! ret){perror(sqlite3_exec1:);printf(errmsg:%s\n, errmsg);exit(2);}ret sqlite3_close(db); // 关闭数据库if (SQLITE_OK ! ret){perror(sqlite3_close:);exit(3);}
}// 更新数据库中注册用户信息
void updatedb(char *name, char *pw) // 插入,修改数据库注册用户信息
{char sql[500] \0;sprintf(sql, update user set password%s where name%s;, pw, name); // 插入,修改,删除都可以用这条语句carryout(sql);
}// 将已注册的用户信息载入链表
void downdb(struct Node *head)
{// printf(进入downdb\n);sqlite3 *db NULL;int ret sqlite3_open(info.db, db); // 打开数据库// printf(打开数据库\n);if (SQLITE_OK ! ret){perror(sqlite3_open:);exit(1);}const char *sql select *from user;; // 写入命令// printf(写入命令\n);char *errmsg;char **result;int row 0, column 0;ret sqlite3_get_table(db, sql, result, row, column, errmsg); // 分别为文件描述符,数据库命令,查询结果,行,列,错误信息if (SQLITE_OK ! ret){perror(sqlite3_exec:);printf(errmsg:%s\n, errmsg);exit(2);}printf(row%d column%d\n, row, column);if (row 0){return;}int i, j;int num;char name[20] \0;char password[20] \0;printf(开始载入\n);for (i 1; i row; i){for (j 0; j column; j){num i * column j;// printf(%s\n,result[num]); //查询到的信息按一维数组的方式储存,0到2为id,name,age这些字段,3到5为第一行数据,6到8为第二行数据if (j 0){strcpy(name, result[num]);}if (j 1){strcpy(password, result[num]);}}insert(head, name, password);}sqlite3_free_table(result); // 释放内存// printf(释放内存\n);ret sqlite3_close(db); // 关闭数据库// printf(关闭数据库\n);if (SQLITE_OK ! ret){perror(sqlite3_close:);exit(3);}return;
}// 聊天室功能选择
int Function(struct client head)
{// 变量声明和初始化// printf(msg:%s\n, head.msg);struct Forbit *p F_head;struct Root *Root R_head;struct user *all hl;struct Group *group G_head;struct VIP *vip V_head;int temp head.root; // 保存原始 root 值int flag seek_VIP(head.name); // 检查用户是否为 VIPif (flag 1){head.root 2; // 如果用户是 VIP,将 root 设置为 2}if (-1 head.flag){delete_group(group, head.name); // 退出群聊}else if (0 head.flag) // 发送给所有在线用户{// 遍历在线用户,将消息发送给所有用户while (all-next ! NULL){all all-next;// 遍历在线用户,将消息发送给所有用户if (strcmp(all-name, head.name) 0){if (all-next ! NULL){all all-next;}else{break;}}if (all-c_fd ! 0){send(all-c_fd, head.msg, sizeof(head.msg), 0);}}}else if (1 head.flag) // 私聊{char buf[1024];int toc_fd searchuser(hl, head.to_name);if (toc_fd 1){toc_fd searchuser(hl, head.name);sprintf(buf, \033[0;33m该用户未登录!\033[0m\n);send(toc_fd, buf, sizeof(head.msg), 0);memset(buf, 0, sizeof(buf));}else if (toc_fd 0){toc_fd searchuser(hl, head.name);sprintf(buf, \033[0;31m该用户不存在!\033[0m\n);send(toc_fd, buf, sizeof(head.msg), 0);memset(buf, 0, sizeof(buf));}else{// 格式化私聊消息int flag1 seek_Group(head.to_name); // 查找发送用户是否处在群聊if (flag1 1){sprintf(buf, \033[34m%s\033[35m(私密消息)\033[36m:%s\033[0m\n, head.name, head.msg);strcpy(head.msg, buf);memset(buf, 0, sizeof(buf));}else{sprintf(buf, \033[0;34m%s\033[36m:%s\033[0m\n, head.name, head.msg);strcpy(head.msg, buf);memset(buf, 0, sizeof(buf));}// 如果是 VIP,添加 VIP 标记if (head.root 2){sprintf(buf, \033[33m(VIP)%s, head.msg);strcpy(head.msg, buf);memset(buf, 0, sizeof(buf));}// 如果在群聊中,添加群聊标记if (flag1 1){sprintf(buf, \033[5m%s, head.msg);strcpy(head.msg, buf);memset(buf, 0, sizeof(buf));}// 发送私聊消息send(toc_fd, head.msg, sizeof(head.msg), 0);}}else if (head.flag 2) // 群聊{char buf[1024];int t_fd;if (temp -1) // 进入群聊{insert_group(group, head.name);return 0;}int flag1 seek_Group(head.name); // 查找该用户是否为群成员if (flag1 1){int c_fd searchuser(hl, head.name);int ret seek_forbit(head.name);if (ret 1) // 该用户处于禁言状态{sprintf(buf, \033[0;31m发送失败!你已被禁言!\033[0m\n);send(c_fd, buf, sizeof(buf), 0);memset(buf, 0, sizeof(buf));return 0;}if (head.root 2){sprintf(buf, \033[0;33m(VIP)%s, head.msg);strcpy(head.msg, buf);memset(buf, 0, sizeof(buf));}while (group-next ! NULL) // 遍历所有群成员,,发送群消息{group group-next;if (strcmp(group-name, head.name) 0){if (group-next ! NULL){group group-next;}else{break;}}t_fd searchuser(hl, group-name);send(t_fd, head.msg, sizeof(head.msg), 0);}// 记录聊天记录FILE *logs fopen(log.txt, a);if (logs NULL){printf(open file erroe: \n);}else{fputs(head.msg, logs);fclose(logs);}}else{t_fd searchuser(hl, head.name);sprintf(buf, \033[0;31m发送失败!你不是该群成员!\033[0m\n);send(t_fd, buf, strlen(buf) 1, 0);}}else if (3 head.flag) // 文件传输{char buf[1024];int toc_fd searchuser(hl, head.to_name);printf(%s向%s发送文件!\n, head.name, head.to_name);sprintf(buf, \033[0;33m%s向你发来一份文件!\033[0m\n, head.name);send(toc_fd, buf, sizeof(buf), 0);usleep(30);memset(buf, 0, sizeof(buf));strcpy(buf, flag3);send(toc_fd, buf, strlen(buf) 1, 0); // 让客户端的进行文件接收准备memset(buf, 0, sizeof(buf));printf(文件内容:\n%s\n, head.msg);send(toc_fd, head.msg, sizeof(head.msg), 0); // 发送文件内容}else if (4 head.flag) // 修改密码{int toc_fd searchuser(hl, head.name);printf(等待用户进行密码修改\n);int flag, flag1;char pw1[20] \0;char pw2[20] \0;char name[20] \0;read(toc_fd, pw1, 20);update_pw(head.name, pw1);updatedb(head.name, pw1);printf(用户密码修改成功\n);}else if (5 head.flag) // 禁言/解禁/踢人{char buf[1024];int c_fd searchuser(hl, head.name);int toc_fd searchuser(hl, head.to_name);if (head.forbit 1) // 禁言{int count 0;while (Root-next ! NULL){if (strcmp(Root-next-root, head.to_name) 0){sprintf(buf, \033[0;31m禁言失败!用户%s为管理员!\033[0m\n, head.to_name);send(c_fd, buf, sizeof(buf), 0);memset(buf, 0, sizeof(buf));count;break;}Root Root-next;}if (count 0){int flag1 seek_VIP(head.to_name);if (flag1 1){sprintf(buf, \033[0;31m禁言失败!用户%s为VIP用户!\033[0m\n, head.to_name);send(c_fd, buf, sizeof(buf), 0);memset(buf, 0, sizeof(buf));count;}}if (count 0){int ret insert_forbit(p, head.to_name);if (ret 1) // 该用户已处于禁言状态{sprintf(buf, \033[0;31m禁言失败!用户%s已处于禁言状态,不可重复禁言!\033[0m\n, head.to_name);send(c_fd, buf, sizeof(buf), 0);memset(buf, 0, sizeof(buf));}else{sprintf(buf, \033[0;33m禁言成功!你已将用户%s禁言!\033[0m\n, head.to_name);send(c_fd, buf, sizeof(buf), 0);memset(buf, 0, sizeof(buf));sprintf(buf, \033[0;31m管理员%s将你禁言!\033[0m\n, head.name);send(toc_fd, buf, sizeof(buf), 0);memset(buf, 0, sizeof(buf));}}}else if (head.forbit 2) // 解除禁言{int ret1 delete_forbit(p, head.to_name);if (ret1 1){sprintf(buf, \033[0;33m用户%s未被禁言!\033[0m\n, head.to_name);send(c_fd, buf, sizeof(buf), 0);memset(buf, 0, sizeof(buf));}else{sprintf(buf, \033[0;33m解除成功!你已将用户%s解除禁言!\033[0m\n, head.to_name);send(c_fd, buf, sizeof(buf), 0);memset(buf, 0, sizeof(buf));sprintf(buf, \033[0;33m你被管理员%s解除禁言!\033[0m\n, head.name);send(toc_fd, buf, sizeof(buf), 0);memset(buf, 0, sizeof(buf));}}else if (head.forbit 3) // 踢人{int count 0;while (Root-next ! NULL){if (strcmp(Root-next-root, head.to_name) 0){sprintf(buf, \033[0;31m踢出群聊失败!用户%s为管理员!\033[0m\n, head.to_name);send(c_fd, buf, sizeof(buf), 0);memset(buf, 0, sizeof(buf));count;break;}Root Root-next;}if (count 0){int flag1 seek_VIP(head.to_name);if (flag1 1){sprintf(buf, \033[0;31m踢出群聊失败!用户%s为VIP用户!\033[0m\n, head.to_name);send(c_fd, buf, sizeof(buf), 0);memset(buf, 0, sizeof(buf));count;}}if (count 0){int ret delete_group(group, head.to_name);if (ret 1) // 该用户不是该群成员{sprintf(buf, \033[0;31m踢出群聊失败!用户%s不是该群成员!\033[0m\n, head.to_name);send(c_fd, buf, sizeof(buf), 0);memset(buf, 0, sizeof(buf));}else{sprintf(buf, \033[0;33m用户%s已被你踢出群聊!\033[0m\n, head.to_name);send(c_fd, buf, sizeof(buf), 0);memset(buf, 0, sizeof(buf));sprintf(buf, \033[0;33m你被管理员%s踢出群聊!\033[0m\n, head.name);send(toc_fd, buf, sizeof(buf), 0);memset(buf, 0, sizeof(buf));}}}}
}// 接收客户端结构体,并作出处理
void *server_thread(void *p)
{struct client head; // 用于存储客户端信息的结构体struct user *c hl; // 指向用户链表的指针struct i_fd tt *(struct i_fd *)p; // 包含线程索引和客户端文件描述符的结构体int i tt.i; // 线程索引int fd tt.c_fd; // 客户端文件描述符sem_wait(sem1[i]); // 等待信号量,保证线程按照顺序执行if (personalflag[0] 0) // 检查是否是私聊模式{char user[50] {}; // 存储用户名称while (1){char buf[100] {};// 接收客户端发送的数据if (recv(fd, head, sizeof(struct client), 0) 0){// 客户端退出处理while (c-next ! NULL){c c-next;if (fd c-c_fd){c-c_fd 0;memset(c-name, 0, sizeof(c-name)); // 初始化break;}}printf(\033[0;31m退出:%s 退出了聊天室.\033[0m\n, user);char buf[1024];sprintf(buf, \033[0;31m%s退出了聊天室. \033[0m\n, user);head.flag 0;strcpy(head.name, user);strcpy(head.msg, buf);Function(head); // 调用处理函数FILE *logs fopen(log.txt, a);if (logs NULL){printf(open file error: \n);}else{fputs(buf, logs);fclose(logs);}pthread_exit(0); // 线程退出}// 把服务器接受到的信息发给所有的客户端strcpy(user, head.name); // 记录进入群聊的用户Function(head); // 调用处理函数}}
}// 登录
int logon(int c_fd)
{struct Root *root;root R_head;printf(等待用户进行登录\n);int flag, flag1, flag2;char pw1[20] \0;char pw2[20] \0;char name[20] \0;// 接收客户端发送的用户名read(c_fd, name, 20);printf(登录时接收到的用户名:%s\n, name);// 检查该用户名是否已经进行注册flag2 search_user(name);write(c_fd, flag2, sizeof(flag2));if (flag2 0) // 已经注册,可以登录{// 查找该用户是否已经登录flag1 search_log(name);printf(用户%s的登录状态:%d\n, name, flag1);write(c_fd, flag1, sizeof(flag1));if (flag1 0){int m_root -1;read(c_fd, m_root, sizeof(m_root));if (m_root 1) // 管理员模式登录{printf(\033[0;33m管理员模式登录中!\033[0m\n);int count 0;while (root ! NULL){if (strcmp(root-root, name) 0){write(c_fd, m_root, sizeof(m_root));count;printf(\033[0;33m管理员%s进入了聊天室!\033[0m\n, name);break;}root root-next;}if (count 0) // 该用户不是管理员{printf(\033[0;33m该用户不是管理员!\033[0m\n);m_root 0;write(c_fd, m_root, sizeof(m_root));logon(c_fd);}}// 接收客户端发送的密码read(c_fd, pw1, 20);// 查找数据库中存储的密码search_pw(name, pw2);// 验证密码是否正确if ((strcmp(pw1, pw2)) 0){flag 1;write(c_fd, flag, sizeof(flag));// 存入在线用户链表insert_tail(hl, c_fd, name);// 检查用户是否为VIPint vip seek_VIP(name);printf(vip:%d\n, vip);write(c_fd, vip, sizeof(vip));return 1; // 登录成功}else if ((strcmp(pw1, pw2)) ! 0){flag 0;write(c_fd, flag, sizeof(flag)); // 让客户端重新进行用户登录logon(c_fd);}}else if (flag1 ! 0){printf(该用户已经登录,等待客户端重新输入\n);logon(c_fd);}}else if (flag2 0){printf(该用户不存在,等待客户端重新输入\n);logon(c_fd);}
}// 注册
int user_register(int c_fd)
{char pw[20] \0;char name[20] \0;printf(等待客户端进行注册\n);// 接收客户端发送的用户名read(c_fd, name, 20);printf(读取到用户名:%s\n, name);// 检查该用户名是否已经存在int flag search_user(name);write(c_fd, flag, sizeof(flag));if (flag 0) // 不存在,可以注册,读取密码{// 接收客户端发送的密码read(c_fd, pw, 20);// 将用户信息插入链表struct Node *text insert(hr, name, pw);printf(用户注册成功\n用户名:%s\n密码:%s\n, text-name, text-password);// 将用户信息插入数据库insertdb(text);return 1; // 注册成功}else if (flag 1){printf(用户名已存在,等待客户端重新输入\n);user_register(c_fd);}
}// 登录、注册
void *register_or_log(void *tt)
{// 从传入的参数中获取客户端套接字和线程索引struct i_fd *iandfd (struct i_fd *)tt;int c_fd iandfd-c_fd;int i iandfd-i;int y 0;int flag, flag1;// 循环等待客户端选择登录或注册while (1){// 从客户端读取选择信息int ret read(c_fd, y, 4);if (y 1){// 客户端选择登录flag logon(c_fd); // 登录成功返回1if (flag){// 登录成功取消个人功能线程的阻塞personalflag[i] 0;sem_post(sem1[i]);break; // 退出循环}}else if (y 2){// 客户端选择注册user_register(c_fd);}}// 等待个人功能线程结束pthread_join(pthuser[i], NULL);
}// 启动服务器
void server()
{int c_fd;struct user *head; // 用来存放已登录用户struct Node *head1; // 存放已注册用户struct Forbit *head2; // 存放被禁言人员struct Root *head3; // 存放管理员名单struct Group *head4; // 存放群内成员struct VIP *head5; // 存放 VIP 用户名单struct i_fd iandfd; // 线程索引和客户端文件描述符// 初始化用户链表和数据库init_l(head);init_r(head1);initdb();downdb(head1);// 初始化禁言用户、管理员、群组、VIP 用户链表头节点F_init(head2);R_init(head3);G_init(head4);V_init(head5);hl head;hr head1;F_head head2;R_head head3;G_head head4;V_head head5;printf(服务器启动\n);int i 0;// 服务器主循环,等待客户端连接while (1){struct sockaddr_in fromaddr;socklen_t len sizeof(fromaddr);// 接受客户端连接请求int fd accept(serverfd, (meng *)fromaddr, len);if (fd -1){printf(客户端连接出错...\n);continue;}printf(客户端连接成功\n);iandfd.i i;iandfd.c_fd fd;// 创建线程处理客户端登录或注册int ret pthread_create(pthuser[i], NULL, register_or_log, (void *)iandfd);if (ret ! 0){perror(pthread_create1:);exit(1);}sem_init(sem[i], 0, 0);sem_init(sem1[i], 0, 0);if (clientfd[i] 0){// 记录客户端的 socketclientfd[i] fd;printf(线程号 %d\n, fd);// 有客户端连接之后,启动线程为此客户服务pthread_t tid;pthread_create(tid, 0, server_thread, iandfd);}if (size i){// 发送给客户端说聊天室满了char *str \033[0;31m对不起,聊天室已经满了!\033[0m\n;send(fd, str, strlen(str), 0);close(fd);}i;}
}int main()
{system(clear); // 清屏Socket(); // 初始化server(); // 启动服务器close(serverfd); // 关闭服务器的 socket 连接return 0;
}客户端
客户端代码主要涉及以下几个方面的知识 Socket编程使用了socket、connect等系统调用来建立客户端与服务器的网络连接。 多线程编程通过pthread库创建了一个用于接收消息的线程。这种方式可以在程序执行其他任务的同时异步接收服务器的消息。 文件操作使用read和write进行文件的读写操作实现了文件的传输功能。 字符界面交互使用了一些ANSI转义码通过控制台打印不同颜色的消息实现了简单的字符界面。 结构体和链表定义了struct client结构体表示客户端的信息通过链表组织这些结构体。 进度条使用了进度条的效果提升了用户体验。
下面是你的代码的功能分析 Socket初始化与连接Socket函数创建了客户端套接字connect建立了与服务器的连接。 用户登录/注册log_in函数提供了用户登录和注册的功能根据用户选择进行相应的操作。 接收消息线程recv_thread函数作为接收消息的线程通过recv从服务器接收消息根据消息的内容进行不同的处理。 用户聊天group_chat函数实现了群聊和私聊功能根据用户输入的命令进行不同的操作。 文件传输transfer_file函数实现了文件的传输功能根据用户选择进行文件的发送或接收。 修改密码alter函数提供了用户修改密码的功能。 管理员权限manage函数处理了管理员的权限操作如禁言、解除禁言、踢人等。 字符界面和交互使用了一系列的ANSI转义码来控制字符的颜色和显示效果提供了良好的用户交互界面。 清除消息和提示提供了清除输入框内容和系统提示信息的功能提高了用户体验。
总体来说你的客户端实现了一个简单的聊天室系统包括登录、注册、群聊、私聊、文件传输、修改密码等功能。使用了多线程来异步接收消息通过字符界面提供友好的交互体验。
client.h
#ifndef CLIENT_H
#define CLIENT_H#include stdio.h
#include stdlib.h
#include string.h
#include unistd.h
#include sys/types.h
#include sys/socket.h
#include netinet/in.h
#include arpa/inet.h
#include fcntl.h
#include semaphore.h
#include pthread.h// 表示客户端的信息包含标志位、权限标志位、禁言标志位、用户名、密码、消息、操作对象等信息
struct client
{int flag; // 功能标志位 -1:退出群聊 0:通知所有在线用户 1:私聊 2:群聊 3.发送文件 4.修改密码 5.管理员权限操作int root; // 权限标志位 -1:首次进入聊天室 0:普通用户 1:管理员 2;VIP用户int forbit; // 管理员权限 1:禁言 2:解除禁言 3:踢人char name[50]; // 账号名char password[20]; // 密码char msg[500]; // 聊天信息char to_name[50]; // 私聊对象struct client *next;
};
struct client Head;// 进度条
void progress_bar();// 清除输入框内容
void Clear_news();// 清除系统提示信息
void Clear_hint();// 打印消息
int print_msg(char *msg, int flag);// 打印聊天框
void Chat_frame(struct client *head);// 打印管理员群聊框
void Root_frame(struct client *head);// 打印功能选择界面
void Function_frame(struct client *head);// 打印系统提示消息
void print_hint(char *msg);// 读取文件
int fileread(char *massage);// 写文件
void filewrite(char *massage);// 登录
int search(struct client *head);// 用户帐号注册
int sign_in(struct client *head);// 初始化
void n_init();// 文件传输
void transfer_file(struct client *head, int temp);// 私聊、群聊
void group_chat(struct client *head);// 修改密码
void alter(struct client *head);// 管理员权限
void manage(struct client *head, int flag);// 聊天模式选择
void Chat_choose(struct client *head);// 登录注册
void log_in(struct client *head);#endif // CLIENT_Hclient.c
/* 聊天室客户端的主程序 */
#include client.hint clientfd2; // 客户端socketchar *IP 192.168.1.145; // 服务器的IPshort PORT 12345; // 服务器服务端口typedef struct sockaddr meng;// char user[50]; //设置支持的用户名长度time_t nowtime; // 用于存储时间sem_t sem1;int line 8; // 打印消息时候的行数int frame 0; // 是否在聊天框内 0:处在功能选择 1:处在聊天框// 进度条
void progress_bar()
{// ****************************** 配置 ***************************// 最后100%时的输出形式const char *LastStr [--------------------] 100%;// 进度条标志,可以改用*或其它符号const char ProgressIcon -;// 进度每加5,进度条就会加一格,注意Step值要和LastStr中的-数量相匹配,两者相乘等于100const int Step 5;// 总共需要多少进度标志,即LastStr中-的数量const int IconMaxNum 20;// 每隔100ms打印一次,这里同时每隔200ms会让进度加1const int PrintInterval 20000;// ****************************************************************for (int i 0; i 100; i){// -------------- 打印进度条 --------------printf(\033[0;34m[\033[0m);int currentIndex i / Step;for (int j 0; j IconMaxNum; j){if (j currentIndex){printf(\033[0;34m%c\033[0m, ProgressIcon); // 打印进度条标志}else{printf( ); // 未达进度则打印空格}}printf(\033[0;34m]\033[0m );// ----------------------------------------// -------- 打印数字进度 ----------printf(\033[0;34m%3d%%\033[0m, i);fflush(stdout);// -------------------------------usleep(PrintInterval);for (int j 0; j strlen(LastStr); j){printf(\b); // 回删字符,让数字和进度条原地变化}fflush(stdout);}printf(\n);
}// 清除输入框内容
void Clear_news()
{printf(\033[33;12H); // 将光标移动回到消息发送区printf(\033[K); // 清除发送框内容printf(\033[72C);printf(\033[0;34m| |\033[0m\n);printf(\033[33;12H \033[0;36m); // 将光标移动回到消息发送区printf(\033[36m);
}// 清除系统提示信息
void Clear_hint()
{printf(\033[31;4H); // 将光标系统提示信息头位置printf(\033[K); // 清除系统提示信息printf(\033[80C);printf(\033[0;34m| |\033[0m\n);printf(\033[33;12H \033[0;36m); // 将光标移动回到消息发送区
}// 打印消息
int print_msg(char *msg, int flag)
{if (frame 1){if ((*msg \0) || (*msg \n)){return 0;}char i_msg[500];printf(\033[s); // 保存光标位置fflush(stdout);if (line 8)printf( \033[8;4H); // 消息打印位置else if (line 10)printf( \033[10;4H); // 消息打印位置else if (line 12)printf( \033[12;4H); // 消息打印位置else if (line 14)printf( \033[14;4H); // 消息打印位置else if (line 16)printf( \033[16;4H); // 消息打印位置else if (line 18)printf( \033[18;4H); // 消息打印位置else if (line 20)printf( \033[20;4H); // 消息打印位置else if (line 22)printf( \033[22;4H); // 消息打印位置else if (line 24)printf( \033[24;4H); // 消息打印位置else if (line 26)printf( \033[26;4H); // 消息打印位置else if (line 28)printf( \033[28;4H); // 消息打印位置else if (line 30)printf( \033[30;4H); // 消息打印位置else if (line 32) // 行数到达最后一行{if ((Head.root 1) (Head.flag 2)){Root_frame(Head);}else{Chat_frame(Head);}printf( \033[8;4H); // 消息打印位置}if (flag 1) // 打印收到的消息{printf(%s, msg);// printf(\033[0;34m |\033[0m);}else if (flag 2) // 打印发送的消息{sprintf(i_msg, \033[0;36m%s\033[0;33m:我\033[0m\n, msg);printf(%*s, 99, i_msg);}// printf(%*s,80,msg);line line 2;printf( \033[u); // 恢复光标位置fflush(stdout);}else if (frame 0){return 0;}
}// 打印聊天框
void Chat_frame(struct client *head)
{system(clear);printf(\033[1;1H);fflush(stdout);line 8;frame 1;char name[100];char to_name[100];printf(\033[34m \033[0m\n);printf(\033[34m | |\033[0m\n);printf(\033[34m |\033[35m*********************************************\033[33m\033[1m年糕聊天室\033[0;35m********************************************\033[34m|\033[0m\n);printf(\033[34m | \033[35m版本:3.0\033[34m |\033[0m\n);printf(\033[34m |---------------------------------------------------------------------------------------------------|\033[0m\n);printf(\033[34m | | |\033[0m\n);printf(\033[34m |---------------------------------------------------------------------------------| |\033[0m\n);printf(\033[34m | |\033[33m\033[1m用户: \033[0;34m|\033[0m\n);printf(\033[34m | | |\033[0m\n);printf(\033[34m | | |\033[0m\n);printf(\033[34m | | |\033[0m\n);printf(\033[34m | | |\033[0m\n);printf(\033[34m | | |\033[0m\n);printf(\033[34m | | |\033[0m\n);printf(\033[34m | | |\033[0m\n);printf(\033[34m | | |\033[0m\n);printf(\033[34m | | |\033[0m\n);printf(\033[34m | | |\033[0m\n);printf(\033[34m | | |\033[0m\n);printf(\033[34m | | |\033[0m\n);printf(\033[34m | | |\033[0m\n);printf(\033[34m | | |\033[0m\n);printf(\033[34m | | |\033[0m\n);printf(\033[34m | | |\033[0m\n);printf(\033[34m | | |\033[0m\n);printf(\033[34m | | |\033[0m\n);printf(\033[34m | | |\033[0m\n);printf(\033[34m | | |\033[0m\n);printf(\033[34m | | \033[33m\033[1mw!:\033[0;36m文件发送 \033[34m|\033[0m\n);printf(\033[34m | | |\033[0m\n);printf(\033[34m | | |\033[0m\n);printf(\033[34m |---------------------------------------------------------------------------------| |\033[0m\n);printf(\033[34m |\033[33m消息输入:\033[0;36m \033[34m| |\033[0m\n);printf(\033[34m | |\033[35m输入\033[31mq!\033[0;35m退出聊天框\033[34m |\033[0m\n);printf(\033[34m \033[0m\n);if (head-root 1){sprintf(name, %s(管理员)\n, head-name);}else if (head-root 2){sprintf(name, %s(VIP)\n, head-name);}else{sprintf(name, %s\n, head-name);}if (head-flag 2){sprintf(to_name, 群聊\n);}else{sprintf(to_name, %s\n, head-to_name);}printf(\033[6;40H); // 将管标移到聊天对象打印位置printf(\033[33m\033[1m%s\033[0m\n, to_name); // 聊天对象printf( \033[9;85H); // 将管标移到用户名打印位置printf(\033[36m\033[1m%s\033[0m \n, name); // 用户名printf( \033[33;12H \033[0;36m); // 将光标移动回到消息发送区
}// 打印管理员群聊框
void Root_frame(struct client *head)
{// printf(\n);system(clear);printf(\033[1;1H);fflush(stdout);line 8;frame 1;char name[100];char to_name[100];printf(\033[34m \033[0m\n);printf(\033[34m | |\033[0m\n);printf(\033[34m |\033[35m*********************************************\033[33m\033[1m年糕聊天室\033[0;35m********************************************\033[34m|\033[0m\n);printf(\033[34m | \033[35m版本:3.0\033[34m |\033[0m\n);printf(\033[34m |---------------------------------------------------------------------------------------------------|\033[0m\n);printf(\033[34m | | |\033[0m\n);printf(\033[34m |---------------------------------------------------------------------------------| |\033[0m\n);printf(\033[34m | |\033[33m\033[1m用户: \033[0;34m|\033[0m\n);printf(\033[34m | | |\033[0m\n);printf(\033[34m | | |\033[0m\n);printf(\033[34m | | |\033[0m\n);printf(\033[34m | | |\033[0m\n);printf(\033[34m | | \033[33m\033[1m管理员权限: \033[0;34m|\033[0m\n);printf(\033[34m | | |\033[0m\n);printf(\033[34m | | \033[36m\033[1m1!:禁言 \033[0;34m|\033[0m\n);printf(\033[34m | | |\033[0m\n);printf(\033[34m | | \033[36m\033[1m2!:解除禁言 \033[0;34m|\033[0m\n);printf(\033[34m | | |\033[0m\n);printf(\033[34m | | \033[36m\033[1m3!:踢人 \033[0;34m |\033[0m\n);printf(\033[34m | | |\033[0m\n);printf(\033[34m | | \033[35m输入关键字 \033[0;34m|\033[0m\n);printf(\033[34m | | \033[35m进入对应权限 \033[0;34m|\033[0m\n);printf(\033[34m | | |\033[0m\n);printf(\033[34m | | |\033[0m\n);printf(\033[34m | | |\033[0m\n);printf(\033[34m | | |\033[0m\n);printf(\033[34m | | |\033[0m\n);printf(\033[34m | | |\033[0m\n);printf(\033[34m | | \033[33m\033[1mw!:\033[0;36m文件发送 \033[34m|\033[0m\n);printf(\033[34m | | |\033[0m\n);printf(\033[34m | | |\033[0m\n);printf(\033[34m |---------------------------------------------------------------------------------| |\033[0m\n);printf(\033[34m |\033[33m消息输入:\033[0;36m \033[34m| |\033[0m\n);printf(\033[34m | |\033[35m输入\033[31mq!\033[0;35m退出聊天框\033[34m |\033[0m\n);printf(\033[34m \033[0m\n);sprintf(name, %s(管理员)\n, head-name);sprintf(to_name, 群聊\n);printf(\033[6;40H); // 将管标移到聊天对象打印位置printf(\033[33m\033[1m%s\033[0m\n, to_name); // 聊天对象printf( \033[9;85H); // 将管标移到用户名打印位置printf(\033[36m\033[1m%s\033[0m \n, name); // 用户名printf( \033[33;12H \033[0;36m); // 将光标移动回到消息发送区
}// 打印功能选择界面
void Function_frame(struct client *head)
{system(clear);line 8;frame 0;char name[100];printf(\033[34m \033[0m\n);printf(\033[34m | |\033[0m\n);printf(\033[34m |\033[35m*********************************************\033[33m\033[1m年糕聊天室\033[0;35m********************************************\033[34m|\033[0m\n);printf(\033[34m | \033[35m版本:3.0\033[34m |\033[0m\n);printf(\033[34m |---------------------------------------------------------------------------------------------------|\033[0m\n);printf(\033[34m | | |\033[0m\n);printf(\033[34m | | |\033[0m\n);printf(\033[34m | |\033[33m\033[1m用户: \033[0;34m|\033[0m\n);printf(\033[34m | | |\033[0m\n);printf(\033[34m | | |\033[0m\n);printf(\033[34m | | |\033[0m\n);printf(\033[34m | \033[0;36m1:私聊功能\033[0;34m | |\033[0m\n);printf(\033[34m | | |\033[0m\n);printf(\033[34m | | |\033[0m\n);printf(\033[34m | | |\033[0m\n);printf(\033[34m | \033[0;36m2:进入群聊\033[0;34m | |\033[0m\n);printf(\033[34m | | |\033[0m\n);printf(\033[34m | | |\033[0m\n);printf(\033[34m | | |\033[0m\n);printf(\033[34m | \033[0;36m3:修改密码\033[0;34m | |\033[0m\n);printf(\033[34m | | |\033[0m\n);printf(\033[34m | | |\033[0m\n);printf(\033[34m | | |\033[0m\n);printf(\033[34m | \033[0;31m0:退出聊天室\033[0;34m | |\033[0m\n);printf(\033[34m | | |\033[0m\n);printf(\033[34m | | |\033[0m\n);printf(\033[34m | | |\033[0m\n);printf(\033[34m | | |\033[0m\n);printf(\033[34m | | |\033[0m\n);printf(\033[34m | | |\033[0m\n);printf(\033[34m | | |\033[0m\n);printf(\033[34m |---------------------------------------------------------------------------------| |\033[0m\n);printf(\033[34m |\033[33m消息输入:\033[0;36m \033[34m| |\033[0m\n);printf(\033[34m | |\033[0;35m输入\033[31mq!\033[0;35m返回本界面\033[0;34m |\n);printf(\033[34m \033[0m\n);if (head-root 1){sprintf(name, %s(管理员)\n, head-name);}else if (head-root 2){sprintf(name, %s(VIP)\n, head-name);}else{sprintf(name, %s\n, head-name);}printf( \033[9;85H); // 将管标移到用户名打印位置printf(\033[36m\033[1m%s\033[0m \n, name); // 用户名printf( \033[33;12H \033[0;36m); // 将光标移动回到消息发送区
}// 打印系统提示消息
void print_hint(char *msg)
{Clear_hint(); // 清除系统提示信息printf(\033[31;4H); // 将光标系统提示信息头位置printf(%s, msg); // 打印提示内容printf(\033[33;12H \033[0;36m); // 将光标移动回到消息发送区
}// 读取文件
int fileread(char *massage)
{int flag 0;int i;FILE *fp;char past[20];char filename[20];char buffer[1024];char buffer1[1024 * 1024];char msg[100];sprintf(msg, \033[0;33m请输入文件路径:\033[0m\n);print_hint(msg); // 打印系统提示信息memset(msg, 0, sizeof(msg));scanf(%s, past);getchar();Clear_news(); // 清除输入框内容sprintf(msg, \033[0;33m请输入文件名:\033[0m\n);print_hint(msg); // 打印系统提示信息memset(msg, 0, sizeof(msg));scanf(%s, filename);getchar();Clear_news(); // 清除输入框内容strcat(past, filename);fp fopen(filename, r);if (fp NULL){sprintf(msg, \033[0;31m文件不存在!\033[35m(任意键继续,放弃请按1)\033[0m\n);print_hint(msg); // 打印系统提示信息memset(msg, 0, sizeof(msg));scanf(%d, flag);Clear_news(); // 清除输入框内容if (flag 1){return 0;}}else{sprintf(msg, \033[0;33m文件打开成功!\033[0m\n);print_hint(msg); // 打印系统提示信息memset(msg, 0, sizeof(msg));fread(buffer, 1, sizeof(buffer), fp);sleep(1);// printf(\033[0;33m文件内容:\033[0;36m%s\033[0m\n, buffer);}if (strlen(buffer) 1){sprintf(msg, \033[0;31m文件为空!\n是否往文件里写入信息.(写入请按1,其他任意键退出)\033[0m);print_hint(msg); // 打印系统提示信息memset(msg, 0, sizeof(msg));scanf(%d, i);getchar();Clear_news(); // 清除输入框内容if (i 1){scanf(%s, buffer);getchar();fprintf(fp, %s, buffer);}else{fclose(fp);return 0;}}if (strlen(buffer) ! 0){sprintf(buffer1, %s\n%s, filename, buffer);// printf(buffer1:%s\n,buffer1);strcpy(massage, buffer1);fclose(fp);return 1;}
}// 写文件
void filewrite(char *massage)
{int i 0, len 0, flag;FILE *fp;char filename[100] ./gyf/;char filename1[100];char buffer[1024];char msg[1024];while (1){if (massage[i] \n){break;}len;i;}strncpy(filename1, massage, len);sprintf(msg, \033[0;33m文件名:\033[0;36m%s\033[0m\n, filename1);print_hint(msg); // 打印系统提示信息memset(msg, 0, sizeof(msg));sleep(1);strcat(filename, filename1);strcpy(buffer, massage[len 1]);// printf(\033[0;33m文件内容:\033[0;36m%s\033[0;36m, buffer);while (1){flag access(filename, 0);if (flag -1){break;}else if (flag 0){len strlen(filename);while (filename[len - 1] ! .){len--;}char post[10];strcpy(post, filename[len - 1]);strcpy(filename[len - 1], \0);strcat(filename, (1));strcat(filename, post);}}fp fopen(filename, w);fprintf(fp, %s, buffer);fclose(fp);memset(filename, 0, sizeof(filename));memset(filename1, 0, sizeof(filename1));memset(buffer, 0, sizeof(buffer));Clear_hint(); // 清除系统提示信息
}// 登录
int search(struct client *head)
{void log_in(); // 声明int flag -1; // 密码正确错误标志位 0:密码错误 1:密码正确int flag1 -1; // 该账号是否登录标志位 0:未登录 1:已登录int flag2 -1; // 该用户是否存在标志位 0:不存在 1:存在printf(\33[0;34m请输入用户名:\33[0m\n);scanf(%s, head-name);getchar();write(clientfd2, head-name, strlen(head-name));int readflag read(clientfd2, flag2, sizeof(flag2)); // 读取用户是否存在的标识符if (flag2 0){read(clientfd2, flag1, sizeof(flag1)); // 读取用户是否已经被登录的标识符if (flag1 0) // 0为未被登录,即可进行登录{if (head-root 1){write(clientfd2, head-root, sizeof(head-root));head-root -1;read(clientfd2, head-root, sizeof(head-root));if (head-root 0){int key 0;printf(\033[0;31m该用户不是管理员!\033[0m\n);printf(\033[0;33m是否继续以管理员身份登录?(任意键继续,按1返回初始界面!)\033[0m\n);scanf(%d, key);if (key 1){log_in(head); // 返回初始界面}else{search(head);}}}else if (head-root 0){write(clientfd2, head-root, sizeof(head-root));}printf(\33[0;34m请输入密码:\33[0m\n);scanf(%s, head-password);getchar();write(clientfd2, head-password, 20);read(clientfd2, flag, sizeof(flag));if (flag){printf(\033[0;34m正在登录中\033[0m);int vip;read(clientfd2, vip, sizeof(vip)); // 判断该用户是否为VIPif (vip 1){head-root 2;}progress_bar();return 1;}else if (flag 0){printf(\33[0;31m用户名或密码输入错误!\033[0m\n);printf(\33[0;31m请重新输入!\033[0m\n);search(head);}}else if (flag1 ! 0){printf(\33[0;31m该用户名已在其他客户端登录!\033[0m\n);printf(\33[0;31m请重新输入!\033[0m\n);search(head);}}else if (flag2 0){printf(\33[0;31m该用户不存在!\33[0m\n);printf(\33[0;31m请重新输入!\033[0m\n);search(head);}
}// 用户帐号注册
int sign_in(struct client *head)
{// 获取注册用户名和密码while (1){int flag -1;// printf(flag:%d\n, flag);printf(\33[0;34m请输入用户名:\33[0m\n);scanf(%s, head-name);getchar();write(clientfd2, head-name, strlen(head-name));int readflag read(clientfd2, flag, sizeof(flag));if (readflag 0){break;}if (flag 0){printf(\33[0;34m请输入用户密码:\33[0m\n);scanf(%s, head-password);getchar();if (head-name[0] ! \0){int ret write(clientfd2, head-password, strlen(head-password));if (ret 0){printf(\033[0;34m注册中\033[0m);progress_bar();printf(\033[0;34m注册成功!\033[0m\n);printf(\033[0;36m用户名:%s\033[0m\n, head-name);printf(\033[0;36m用户密码:%s\033[0m\n\n, head-password);break;}}}else if (flag 0){printf(\033[0;31m用户名已被占用!\n请重新输入!\n\033[0m);}}
}// 初始化
void Socket()
{clientfd2 socket(PF_INET, SOCK_STREAM, 0); // 创建套接字struct sockaddr_in addr; // 将套接字存在sockaddr_in结构体中addr.sin_family PF_INET; // 地址族addr.sin_port htons(PORT); // 端口号 可随意设置,不过不可超过规定的范围addr.sin_addr.s_addr inet_addr(IP); // inet_addr()函数将点分十进制的字符串转换为32位的网络字节顺序的ip信息// 发起连接if (connect(clientfd2, (meng *)addr, sizeof(addr)) -1){perror(\033[0;31m无法连接到服务器\033[0m);exit(-1);}printf(\033[0;33m客户端启动成功\n\033[0m);
}// 用于接收聊天信息
void *recv_thread(void *p)
{char flag[20] {};strcpy(flag, flag3);char buf[1024] {};char msg[100];while (1){char *str buf;if (recv(clientfd2, buf, sizeof(buf), 0) 0){break;}if (strcmp(buf, flag) 0) // 进行文件接收准备{recv(clientfd2, buf, 500, 0);while (*str \0){str;}printf(\033[s); // 保存光标位置fflush(stdout);// printf(\033[?25l); //隐藏光标printf(\033[31;4H); // 将光标移动到系统提示信息头位置printf(\033[0;33m文件接收中\033[0m);progress_bar();memset(msg, 0, sizeof(msg));// printf(\033[33;12H \033[0;36m); //将光标移动回到消息发送区// print_msg(buf, 3); //打印接收文件的消息sprintf(msg, \033[0;35m文件接收成功!\033[0;36m);print_msg(msg, 1); // 打印信息sleep(1);memset(msg, 0, sizeof(msg));filewrite(str); // 写文件// printf( \033[u); //恢复光标位置fflush(stdout);// Clear_news(); //清除输入框内容printf(\033[36m);// printf(1\n);// Clear_news(); //清除输入框内容printf(\033[?25h); // 显示光标// printf(\033[12C \033[1A);}else{print_msg(buf, 1); // 打印接收的消息}memset(buf, 0, sizeof(buf));}
}// 文件传输
void transfer_file(struct client *head, int temp)
{char msg[100];if (temp 1) // 该用户处在群聊{sprintf(msg, \033[0;33m请输入发送对象 :\033[0m\n);print_hint(msg); // 打印系统提示信息memset(msg, 0, sizeof(msg));scanf(%s, head-to_name);getchar();Clear_news(); // 清除输入框内容}while (1){int y;int i fileread(head-msg);if (i 0){break;}else if (i 1){send(clientfd2, head, sizeof(struct client), 0);printf(\033[?25l); // 隐藏光标printf(\033[31;4H); // 将光标系统提示信息头位置printf(\033[0;33m文件发送中\033[0m);progress_bar();memset(msg, 0, sizeof(msg));printf(\033[33;12H \033[0;36m); // 将光标移动回到消息发送区printf(\033[?25h); // 显示光标sprintf(msg, \033[0;33m文件发送成功!\033[0;35m(按任意键继续,退出请按1)\033[0;36m\n);print_hint(msg); // 打印系统提示信息memset(msg, 0, sizeof(msg));scanf(%d, y);getchar();Clear_news(); // 清除输入框内容// printf(1\n);// Clear_news(); //清除输入框内容printf(\033[36m);}else{sprintf(msg, \033[0;31m文件为空,无法传送!\033[0m\n);print_hint(msg); // 打印系统提示信息memset(msg, 0, sizeof(msg));}if (y 1){break;}}
}// 私聊、群聊
void group_chat(struct client *head)
{void Chat_choose(); // 声明if (1 head-flag) // 私聊{char msg[100];sprintf(msg, \033[0;35m请输入私聊对象:\033[0m);print_hint(msg); // 打印系统提示信息memset(msg, 0, sizeof(msg));scanf(%s, head-to_name);getchar();Clear_news(); // 清除输入框内容// printf(\033[0;35m已进入与%s的私聊界面!\033[0m\n, head-to_name);}else if (2 head-flag) // 群聊{int temp head-root; // 记录用户root值head-root -1; // 让服务器将该用户加入到群成员send(clientfd2, head, sizeof(struct client), 0);head-root temp;// printf(\033[0;35m欢迎进入群聊!\033[0m\n);}Head *head;// 打印聊天框if ((head-root 1) (head-flag 2)){Root_frame(head);}else{Chat_frame(head);}// 消息输入while (1){char buf[1024] {};// printf(\033[0;36m);scanf(%s, head-msg);getchar();// 判断关键字if (strcmp(head-msg, q!) 0) // 群出聊天{if (head-flag 2){head-flag -1;send(clientfd2, head, sizeof(struct client), 0); // 退出群聊}Chat_choose(head);}if (strcmp(head-msg, w!) 0) // 发送文件{Clear_news(); // 清除输入框内容if (head-flag 1){head-flag 3;transfer_file(head, 0); // 文件传输head-flag 1;}else if (head-flag 2){head-flag 3;transfer_file(head, 1); // 文件传输head-flag 2;}Clear_hint(); // 清除系统提示信息continue; // 结束本次循环,进入下次循环}if (strcmp(head-msg, 1!) 0) // 禁言{Clear_news(); // 清除输入框内容if ((head-root 1) (head-flag 2)){head-flag 5;manage(head, 1);head-flag 2;continue;}}if (strcmp(head-msg, 2!) 0) // 解除禁言{Clear_news(); // 清除输入框内容if ((head-root 1) (head-flag 2)){head-flag 5;manage(head, 2);head-flag 2;continue;}}if (strcmp(head-msg, 3!) 0) // 踢出群聊{Clear_news(); // 清除输入框内容if ((head-root 1) (head-flag 2)){head-flag 5;manage(head, 3);head-flag 2;continue;}}Clear_news(); // 清除聊天框内容// 靠左边打印发送的消息/*sprintf(buf, \033[0;33m\033[1m我:\033[0;36m%s\033[0m\n, head-msg);print_msg(buf, 1); //靠左边打印发送的消息*/// 靠右边打印发送的消息print_msg(head-msg, 2); // 靠右边打印发送的消息if (head-flag 2){if (head-root 1){sprintf(buf, \033[0;33m(管理员)\033[0;34m%s\033[0;36m:%s\033[0m\n, head-name, head-msg);}else{sprintf(buf, \033[0;34m%s:\033[0;36m%s\033[0m\n, head-name, head-msg);}strcpy(head-msg, buf);memset(buf, 0, sizeof(buf));}send(clientfd2, head, sizeof(struct client), 0);}
}// 修改密码
void alter(struct client *head)
{void Chat_choose();int flag -1; // 用来判断密码输入是否正确send(clientfd2, head, sizeof(struct client), 0); // 让服务器进入修改密码等待char pw[20];char pw1[20];char msg[100];while (1){sprintf(msg, \033[0;33m请输入旧密码:\033[0m\n);print_hint(msg); // 打印系统提示信息memset(msg, 0, sizeof(msg));scanf(%s, pw);Clear_news(); // 清除输入框内容if (strcmp(head-password, pw) 0){sprintf(msg, \033[0;33m请输入新密码:\033[0m\n);print_hint(msg); // 打印系统提示信息memset(msg, 0, sizeof(msg));memset(pw, 0, 20);scanf(%s, pw);getchar();Clear_news(); // 清除输入框内容sprintf(msg, \033[0;33m请确认密码:\033[0m\n);print_hint(msg); // 打印系统提示信息memset(msg, 0, sizeof(msg));memset(pw1, 0, 20);scanf(%s, pw1);getchar();Clear_news(); // 清除输入框内容if (strcmp(pw, pw1) 0){strcmp(head-password, pw);int ret write(clientfd2, head-password, strlen(head-password));if (ret 0){printf(\033[31;4H); // 将光标移动到系统提示信息头位置printf(\033[0;33m服务器修改中\033[0m);progress_bar();memset(msg, 0, sizeof(msg));sprintf(msg, \033[0;35m密码修改成功!\033[0m\n);print_hint(msg); // 打印系统提示信息memset(msg, 0, sizeof(msg));sleep(1);Clear_hint();break;}else{sprintf(msg, \033[0;31m密码修改失败,请重新输入!\033[0m\n);print_hint(msg); // 打印系统提示信息memset(msg, 0, sizeof(msg));sleep(1);}}else{sprintf(msg, \033[0;31m;两次密码输入有误!请重新输入!\033[0m\n);print_hint(msg); // 打印系统提示信息memset(msg, 0, sizeof(msg));sleep(1);}}else{int temp 0;sprintf(msg, \033[0;31m密码输入错误!是否继续输入\033[0;35m(任意键继续,放弃请按1)\033[0m\n);print_hint(msg); // 打印系统提示信息memset(msg, 0, sizeof(msg));scanf(%d, temp);Clear_news(); // 清除输入框内容if (temp 1){Chat_choose(head);}}}
}// 管理员权限
void manage(struct client *head, int flag)
{Clear_news(); // 清除输入框内容char msg[100];int temp 0;// sprintf(msg,\033[0;33m请输入禁言用户:\033[0m\n);// print_hint(msg);//打印系统提示信息while (1){if (1 flag){head-forbit 1;sprintf(msg, \033[0;33m请输入禁言用户:\033[0m\n);print_hint(msg); // 打印系统提示信息memset(msg, 0, sizeof(msg));scanf(%s, head-to_name);getchar();Clear_news(); // 清除输入框内容send(clientfd2, head, sizeof(struct client), 0);usleep(100);}else if (2 flag){head-forbit 2;sprintf(msg, \033[0;33m请输入解禁用户:\033[0m\n);print_hint(msg); // 打印系统提示信息memset(msg, 0, sizeof(msg));scanf(%s, head-to_name);getchar();Clear_news(); // 清除输入框内容send(clientfd2, head, sizeof(struct client), 0);usleep(100);}else if (3 flag){head-forbit 3;sprintf(msg, \033[0;33m请输入被踢用户:\033[0m\n);print_hint(msg); // 打印系统提示信息memset(msg, 0, sizeof(msg));scanf(%s, head-to_name);getchar();Clear_news(); // 清除输入框内容send(clientfd2, head, sizeof(struct client), 0);}memset(msg, 0, sizeof(msg));sprintf(msg, \033[0;33m\033[1m是否继续:\033[0;33m(任意键继续,结束请按1)\033[0m \n);print_hint(msg); // 打印系统提示信息memset(msg, 0, sizeof(msg));scanf(%d, temp);getchar();Clear_news(); // 清除输入框内容if (1 temp){Clear_hint(); // 清除系统提示信息break;}}
}// 聊天模式选择
void Chat_choose(struct client *head)
{int count 0;char msg[100];frame 0;Function_frame(head); // 打印功能选择框sprintf(msg, \033[0;34m请输入功能选择项:\033[0m \n);print_hint(msg); // 打印系统提示信息memset(msg, 0, sizeof(msg));while (1){scanf(%d, count);getchar();Clear_news(); // 清除输入框内容printf(\n);switch (count){case 1:Clear_hint(); // 清除系统提示信息head-flag 1;group_chat(head); // 私聊功能case 2:Clear_hint(); // 清除系统提示信息head-flag 2;group_chat(head); // 群聊功能break;case 3:Clear_hint(); // 清除系统提示信息head-flag 4;alter(head); // 修改密码break;case 0:printf(msg, \033[0;33m退出成功!欢迎下次使用!\033[0m\n);print_hint(msg); // 打印系统提示信息memset(msg, 0, sizeof(msg));close(clientfd2);printf(\033[35,0H);exit(-1);break;default:printf(\033[0;31m选择有误!请重新输入!\033[0m\n);print_hint(msg); // 打印系统提示信息memset(msg, 0, sizeof(msg));break;}}
}// 登录注册
void log_in(struct client *head)
{int select;system(clear);printf(\033[0;34m\033[0m\n);printf(\033[0;34m| |\033[0m\n);printf(\033[0;34m|***********************\033[0;33m欢迎来到年糕聊天室\033[0;34m***********************|\033[0m\n);printf(\033[0;34m| 版本:3.0 |\033[0m\n);printf(\033[0;34m|----------------------------------------------------------------|\033[0m\n);printf(\033[0;34m| |\033[0m\n);printf(\033[0;34m| \033[0;36m1.用户登录 \033[0;34m|\033[0m\n);printf(\033[0;34m| |\033[0m\n);printf(\033[0;34m| \033[0;36m2.帐号注册 \033[0;34m|\033[0m\n);printf(\033[0;34m| |\033[0m\n);printf(\033[0;34m| \033[0;33m3.管理员登录 \033[0;34m|\033[0m\n);printf(\033[0;34m| |\033[0m\n);printf(\033[0;34m| \033[0;31m 0:退出聊天室 \033[0;34m|\033[0m\n);printf(\033[0;34m| |\033[0m\n);printf(\033[0;34m\033[0m\n);printf(\n);while (1){printf(\033[0;34m请选择:\033[0m\n);scanf(%d, select);getchar();if (1 select){write(clientfd2, select, sizeof(select));head-root 0; // 普通用户登录search(head);break;}else if (2 select){write(clientfd2, select, sizeof(select));sign_in(head);}else if (3 select){select 1;write(clientfd2, select, sizeof(select));head-root 1; // 管理员登录search(head);break;}else if (0 select){printf(\033[0;33m退出成功!\033[0m\n);printf(\033[0;33m欢迎下次使用!\033[0m\n);exit(-1);break;}else{printf(\033[0;31m选择错误!请重新输入!\033[0m\n);}}
}int main()
{struct client head;Socket();log_in(head); // 进入登录注册界面// 创建一个线程用于数据的接收pthread_t id;void *recv_thread(void *);int ret1 pthread_create(id, 0, recv_thread, 0);if (ret1 ! 0){perror(pthread_create1:);exit(1);}// 让服务器告诉所有在线用户,该用户进入聊天室if (head.root 1){sprintf(head.msg, \033[0;33m%s\033[0;35m(管理员)\033[0;33m进入了聊天室!\033[0m\n, head.name);}else{sprintf(head.msg, \033[0;33m%s进入了聊天室!\033[0m\n, head.name);}head.flag 0;send(clientfd2, head, sizeof(struct client), 0);memset(head.msg, 0, sizeof(head.msg));Chat_choose(head); // 进入聊天室功能界面close(clientfd2);return 0;
}