聊天室系统
SQLite库
服务器
客户端
当设计一个聊天室系统时,会涉及到许多方面的知识。简单的项目结构,从不同的方面对聊天室的设计进行分析。请注意,以下的结构只是一个示例,实际项目可能会更加复杂,具体要求取决于项目的规模和功能。
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: 用于编译服务器端程序。
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: 用于编译客户端程序。
套接字编程(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):
以上只是一个简单的示例项目结构和知识点,实际项目中可能需要更多的细节和优化。希望这个示例对你理解聊天室系统的设计有所帮助。如果有进一步的问题或需要详细的解释,请随时提问。
安装 SQLite3 库通常包括以下步骤:
【SQLite】环境安装
sudo apt-get update
sudo apt-get install sqlite3 libsqlite3-dev
sudo yum install sqlite sqlite-devel
brew install sqlite
在编译你的程序时,确保链接 SQLite3 库。在编译命令中添加 -lsqlite3
,例如:
gcc -o your_executable your_source_code.c -lpthread -lsqlite3
SQLite3 还提供了一个命令行工具,你可以用来管理和查询 SQLite3 数据库。你可以通过以下方式运行它:
sqlite3
这些步骤应该能够满足你在使用 SQLite3 库时的基本需求。请注意,SQLite3 库是一个轻量级的嵌入式数据库,无需独立的数据库服务器。
实现了一个聊天室的功能选择函数,包含了不同的功能分支。以下是对每个功能分支的详细分析:
整体结构和输入参数:
Function
head
,该结构体包含了客户端发送的消息相关信息,如发送者姓名、消息内容、接收者姓名、标志位等。变量声明和初始化:
F_head
、R_head
、h
、G_head
、V_head
等,用于管理禁言列表、管理员列表、在线用户列表、群组列表和VIP用户列表。temp
变量用于保存用户原本的权限,flag
则用于检测用户是否为VIP用户。temp
保存原本的权限,以便后续恢复。退出群聊功能:
head.flag
为 -1
时,表示用户选择退出群聊。delete_group
函数将用户从群组中删除。群发消息功能:
head.flag
为 0
时,表示用户选择向所有在线用户发送消息。私聊功能:
head.flag
为 1
时,表示用户选择进行私聊。searchuser
函数查找目标用户的文件描述符,并发送私聊消息给目标用户。群聊功能:
head.flag
为 2
时,表示用户选择进行群聊。temp
的值判断用户是否为管理员,是否具有发送群消息的权限。文件传输功能:
head.flag
为 3
时,表示用户选择进行文件传输。searchuser
函数查找目标用户的文件描述符,发送文件传输相关信息和文件内容。修改密码功能:
head.flag
为 4
时,表示用户选择修改密码。read
函数接收用户发送的新密码,并调用 update_pw
和 updatedb
函数更新用户密码。禁言、解除禁言、踢人功能:
head.flag
为 5
时,表示用户选择进行禁言、解除禁言、踢人操作。head.forbit
的值执行相应的操作,包括禁言用户、解除禁言、踢出群聊等。在操作过程中发送通知给相关用户。总体而言,实现了多个聊天室功能,包括私聊、群聊、文件传输、修改密码、禁言、解禁等。注释中使用了不同颜色和样式,以便在终端中更好地展示不同类型的消息。
#ifndef SERVER_H
#define SERVER_H
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
int 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_H
/* 聊天室服务器端的主程序 */
#include "server.h"
int 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; // 设置新节点的下一个节点为NULL
root->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) // 如果ch=EOF,证明文件为空,初始化应该存个零进去
{
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链表节点的下一个节点为NULL
vip->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) // 如果ch=EOF,证明文件为空,初始化应该存个零进去
{
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; // 初始化已登录用户链表头节点的客户端文件描述符为0
newnode->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; // 设置新节点的下一个节点为NULL
while (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; // 设置新节点的下一个节点为NULL
while (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()
{
// 创建 socket
serverfd = 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转化为网络ip
struct 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 type='table' 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); // 检查用户是否为 VIP
if (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, "flag==3");
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);
// 检查用户是否为VIP
int 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); // 登录成功返回1
if (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)
{
// 记录客户端的 socket
clientfd[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转义码来控制字符的颜色和显示效果,提供了良好的用户交互界面。
清除消息和提示:提供了清除输入框内容和系统提示信息的功能,提高了用户体验。
总体来说,你的客户端实现了一个简单的聊天室系统,包括登录、注册、群聊、私聊、文件传输、修改密码等功能。使用了多线程来异步接收消息,通过字符界面提供友好的交互体验。
#ifndef CLIENT_H
#define CLIENT_H
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
// 表示客户端的信息,包含标志位、权限标志位、禁言标志位、用户名、密码、消息、操作对象等信息
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_H
/* 聊天室客户端的主程序 */
#include "client.h"
int clientfd2; // 客户端socket
char *IP = "192.168.1.145"; // 服务器的IP
short 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中的"-"数量相匹配,两者相乘等于100
const int Step = 5;
// 总共需要多少进度标志,即LastStr中"-"的数量
const int IconMaxNum = 20;
// 每隔100ms打印一次,这里同时每隔200ms会让进度加1
const 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)); // 判断该用户是否为VIP
if (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, "flag==3");
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;
}