1.设计需求
用户注册、登录、悄悄话、群聊、私聊、查看在线用户、超级用户权限、踢人、禁言、 解禁、文件传输、查看聊天记录、忘记密码、注销用户
2.使用事项
注册用户即为普通用户,在界面需要主动申请为VIP(管理员),但是必须在后台服务器进行操作(同意或者拒绝);
若被禁言,则为永久禁言,知道管理员解除禁言,或者重启服务器;
若被管理员强制下线,退出聊天框重新进入即可;
进行聊天时可以选择常用语或者手动输入。
3.代码效果演示
4.客户端代码
main.c
#include "client.h" int main(int argc, char const *argv[]) { int ret; //注册信号函数 //进行信号捕捉,将SIGINT信号的处理方式改成自己的处理方式 //去执行我自己的功能 if(signal(SIGINT,Close) == SIG_ERR) { perror("signal"); return -1; } pthread_t tid_read; pthread_t tid_write; //创建套接字 int sockfd = socket(AF_INET,SOCK_STREAM,0); if(-1 == sockfd) { perror("socket"); return -1; } //向服务器发起连接 struct sockaddr_in server_addr; //保存服务器信息 memset(&server_addr,0,sizeof(server_addr)); server_addr.sin_family = AF_INET; //地址族 ipv4 server_addr.sin_port = 8883; //网络字节序端口 server_addr.sin_addr.s_addr = inet_addr("127.0.0.1"); //ip地址 //server_addr.sin_addr.s_addr = inet_addr("192.168.124.131"); ret = connect(sockfd,(struct sockaddr *)&server_addr,sizeof(server_addr)); if(-1 == ret) { perror("connect"); return -1; }
client.h
#ifndef _CLIENT_H_ #define _CLIENT_H_ #include <sqlite3.h> #include <ctype.h> #include <stdio.h> #include <sys/socket.h> #include <netinet/in.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <pthread.h> #include <fcntl.h> #include <sys/types.h> #include <sys/stat.h> #include <errno.h> #include <arpa/inet.h> #include <time.h> #include<signal.h> int sockfd; //客户端套接字socket enum { REG, //注册 LOGIN, //登录 FORGET, //忘记密码 LOOKUSERS, //查看在线用户 PRIVATE, //私聊 GROUP, //群聊 ANONPRIVATE,//私聊(匿名聊天) ANONGROUP, //群聊(匿名聊天) REPLY, //回复 EXCUSE, //禁言 ADMINISTRATOR, //申请管理员 OUTADMINISTRATOR, //取消管理员 WORLD, //解除禁言 KICK, //踢人 KILLUSER, //注销账户 //FILE, //传输文件 }; //保存信息结构体 typedef struct Message { char id[32]; //账号 char myid[32]; //用于保存自己的id char name[32]; //昵称 char passwd[32]; //密码 char secret[32]; //密保 char cmd[32]; //聊天方式 int cfd; //聊天对象 char msg[128]; //聊天内容 int root; //管理员标志 char chat[1024]; //聊天记录 char buffer[1024]; }Message; //聊天室功能能选择界面 void menu(); //常用语功能界面 void menu1(); //聊天室界面 void menu2(); //常用语 char *PhrasalVerbs(int *select); //写线程 void *write_thread(void * arg); //读线程 void *read_thread(void * arg); //修改退出聊天室的方式 void Close(int signum); void file_from(int sockfd); void file_recv(char buffer[]); #endif
client.c
#include "client.h"
//管理员身份标志 等于1的时候是管理员身份 等于0的时候不是管理员身份
int root = 0;
//禁言标志 等于1是被禁言 等于0的时候没有被禁言
int forbid_flag = 0;
//修改退出聊天室的方式
void Close(int signum)
{
printf("请正确退出聊天室\n");
}
void menu()
{
界面设计
}
//读线程
void *read_thread(void * arg)
{
char receive[128];
int length;
struct Message node;
node = *((struct Message *)arg);
sockfd = node.cfd;
while(1)
{
memset(receive, 0, sizeof(receive));
length = recv(sockfd,receive,100,0);
if(length == 0)
{
pthread_exit(NULL);
}
receive[length] = '\0';
if(strcmp(receive,"管理员身份申请成功") == 0)
{
printf("%s\n",receive);
root = 1;
}
else if(strcmp(receive,"撤销管理员身份成功") == 0)
{
printf("%s\n",receive);
root = 0;
}
else if(strcmp(receive,"你已经被管理员禁言") == 0)
{
printf("%s\n",receive);
forbid_flag = 1;
}
else if(strcmp(receive,"你已经被管理员解禁") == 0)
{
printf("%s\n",receive);
forbid_flag = 0;
}
else if(strcmp("AAAAA",receive) == 0)
{
printf("接收文件中....\n");
char buffer[1024];
memset(buffer,0,sizeof(buffer));
int file_len = recv(sockfd,buffer,1024,0);
if(-1 == file_len)
{
perror("recv");
exit(-1);
}
printf("file_len = %d\n",file_len);
buffer[file_len] = '\0';
//printf("buffer = %s\n",buffer);
file_recv(buffer);
printf("接收文件成功\n");
}
else
{
printf("%s\n" , receive);
}
}
pthread_exit(NULL);
}
//写线程
void *write_thread(void * arg)
{
char sendline[128];
Message message;
int a; //判断是否注销账户
int a1; //判断是否匿名聊天方式
int a2; //判断是否使用常用语
int select;
sockfd = *((int *)arg);
while(1)
{
system("clear");
menu();
memset(&select,0,sizeof(int));
scanf("%d",&select);
getchar();
switch (select)
{ //登录
case 1: //登录
printf("账号:\n");
scanf("%s",message.id);
getchar();
strcpy(message.myid,message.id);
printf("密码:\n");
scanf("%s",message.passwd);
getchar();
strcpy(message.cmd,"LOGIN");
printf("正在登录,请稍后......\n");
sleep(1);
send(sockfd,&message,sizeof(message),0);
sleep(1);
system("clear");
break;
//注册
case 2:
printf("id:\n");
scanf("%s",message.id);
getchar();
printf("昵称:\n");
scanf("%s",message.name);
getchar();
printf("密码:\n");
scanf("%s",message.passwd);
getchar();
printf("请输入密保:\n");
scanf("%s",message.secret);
getchar();
strcpy(message.cmd,"REG");
printf("正在注册,请稍后......\n");
sleep(1);
send(sockfd,&message,sizeof(message),0);
sleep(1);
system("clear");
break;
//匿名聊天
case 3:
if(forbid_flag == 0)
{
printf("请选择匿名聊天的聊天方式\n");
printf("1 : 匿名私聊 2 : 匿名群聊 \n");
scanf("%d", &a1);
getchar();
//匿名私聊
if(a1 == 1)
{
printf("是否需要使用聊天常用语快捷输入\n");
printf("1 : 是 2 : 否 \n");
scanf("%d", &a2);
getchar();
if(a2 == 1)
{
int select1;
system("clear");
sleep(1);
menu1();
printf("请输入编号\n");
scanf("%d",&select1);
getchar();
char *world;
world = PhrasalVerbs(&select1);
if(strcmp(world,"NO") != 0)
{
strcpy(message.cmd,"ANONPRIVATE");
printf("请输入对方的昵称:\n");
scanf("%s",message.id);
strcpy(message.msg,world);
send(sockfd,&message,sizeof(message),0);
free(world); //记住一定要用free释放,否则会造成内存泄露
break;
}
a2 =2;
}
if (a2 == 2)
{
printf("请输入消息:\n");
memset(sendline,0,sizeof(sendline));
fgets(sendline,128,stdin);
strcpy(message.cmd,"ANONPRIVATE");
printf("请输入对方的昵称:\n");
scanf("%s",message.id);
strcpy(message.msg,sendline);
send(sockfd,&message,sizeof(message),0);
break;
}
else
{
printf("输入错误,正在跳转功能页面\n");
sleep(1);
break;
}
}
//匿名群聊
if(a1 == 2)
{
printf("是否需要使用聊天常用语快捷输入\n");
printf("1 : 是 2 : 否 \n");
scanf("%d", &a2);
getchar();
if(a2 == 1)
{
int select1;
system("clear");
sleep(1);
menu1();
printf("请输入编号\n");
scanf("%d",&select1);
getchar();
char *world;
world = PhrasalVerbs(&select1);
if(strcmp(world,"NO") != 0)
{
strcpy(message.cmd,"ANONGROUP");
strcpy(message.msg,world);
send(sockfd,&message,sizeof(message),0);
free(world); //记住一定要用free释放,否则会造成内存泄露
break;
}
a2 =2;
}
if(a2 == 2)
{
printf("请输入消息:\n");
memset(sendline, 0, sizeof(sendline));
fgets(sendline,100,stdin);
strcpy(message.cmd,"ANONGROUP");
strcpy(message.msg,sendline);
send(sockfd,&message,sizeof(message),0);
break;
}
else
{
printf("输入错误,正在跳转功能页面\n");
sleep(1);
break;
}
}
else
{
printf("输入错误,正在跳转功能页面\n");
sleep(1);
break;
}
}
else
{
printf("你已经被管理员禁言\n");
}
break;
//私聊
case 4:
if(forbid_flag == 0)
{
printf("是否需要使用聊天常用语快捷输入\n");
printf("1 : 是 2 : 否 \n");
scanf("%d", &a2);
getchar();
if(a2 == 1)
{
while(1)
{
int select1;
system("clear");
sleep(1);
menu1();
printf("请输入编号:");
scanf("%d",&select1);
if(select1 == 0)
{
break;
}
getchar();
char *world;
world = PhrasalVerbs(&select1);
strcpy(message.cmd,"PRIVATE");
printf("请输入对方的昵称:\n");
scanf("%s",message.id);
strcpy(message.name,message.myid);
strcpy(message.msg,world);
send(sockfd,&message,sizeof(message),0);
free(world); //记住一定要用free释放,否则会造成内存泄露
}
}
a2 =2;
if(a2 == 2)
{
printf("输入对方的昵称:\n");
scanf("%s",message.id);
printf("请输入消息:\n");
while(1)
{
memset(sendline,0,sizeof(sendline));
fgets(sendline,128,stdin);
if(strcmp(sendline,"q\n") == 0)
{
break;
}
strcpy(message.name,message.myid);
strcpy(message.cmd,"PRIVATE");
strcpy(message.msg,sendline);
send(sockfd,&message,sizeof(message),0);
}
break;
}
else
{
printf("输入错误,正在跳转功能页面\n");
sleep(1);
}
}
else
{
printf("你已经被管理员禁言\n");
}
break;
//群聊
case 5:
if(forbid_flag == 0)
{
printf("是否需要使用聊天常用语快捷输入\n");
printf("1 : 是 2 : 否 \n");
scanf("%d", &a2);
getchar();
while (1)
{
if(a2 == 1)
{
int select1;
system("clear");
sleep(1);
menu1();
printf("请输入编号\n");
scanf("%d",&select1);
if(select1 == 0)
{
break;
}
getchar();
char *world;
world = PhrasalVerbs(&select1);
if(strcmp(world,"NO") != 0)
{
strcpy(message.cmd,"GROUP");
strcpy(message.name,message.myid);
strcpy(message.msg,world);
send(sockfd,&message,sizeof(message),0);
free(world); //记住一定要用free释放,否则会造成内存泄露
break;
}
a2 =2;
}
if(a2 == 2)
{
printf("请输入消息:\n");
memset(sendline, 0, sizeof(sendline));
fgets(sendline,100,stdin);
if(strcmp(sendline,"q\n") == 0)
{
break;
}
strcpy(message.name,message.myid);
strcpy(message.cmd,"GROUP");
strcpy(message.msg,sendline);
send(sockfd,&message,sizeof(message),0);
}
}
}
else
{
printf("你已经被管理员禁言\n");
}
break;
//踢人
case 6:
if(root == 1)
{
printf("请输入你要强制下线用户的id\n");
scanf("%s",message.id);
strcpy(message.cmd,"KICK");
send(sockfd,&message,sizeof(message),0);
}
else
{
printf("你还不是管理员身份,不能使用该功能\n");
}
break;
//禁言
case 7:
if(root == 1)
{
//msg_text.admin_flag = admin_flag;
printf("需要禁言用户的id\n");
scanf("%s",message.id);
strcpy(message.cmd,"EXCUSE");
send(sockfd,&message,sizeof(message),0);
}
else
{
printf("你还不是管理员身份,不能使用该功能\n");
}
break;
//解除禁言
case 8:
if(root == 1)
{
printf("需要解禁言用户的id\n");
scanf("%s",message.id);
strcpy(message.cmd,"WORLD");
send(sockfd,&message,sizeof(message),0);
}
else
{
printf("你还不是管理员身份,不能使用该功能\n");
}
break;
//忘记密码
case 9:
printf("请输入id:\n");
scanf("%s",message.id);
getchar();
printf("请输入密保:\n");
scanf("%s",message.secret);
getchar();
printf("请输入新密码:\n");
scanf("%s",message.passwd);
getchar();
strcpy(message.cmd,"FORGET");
printf("正在更改密码,请稍后......\n");
sleep(1);
send(sockfd,&message,sizeof(message),0);
sleep(1);
system("clear");
break;
//查看在线人数
case 10:
strcpy(message.cmd,"LOOKUSERS");
send(sockfd,&message,sizeof(message),0);
sleep(1);
break;
//申请管理员身份
case 11:
if(root == 1)
{
printf("你已经是管理员\n");
}
else
{
strcpy(message.cmd,"ADMINISTRATOR");
send(sockfd,&message,sizeof(message),0);
printf("正在等待服务器响应...........\n");
}
sleep(2);
system("clear");
break;
//取消管理员身份
case 12:
if(root == 0)
{
printf("你还不是管理员\n");
}
else
{
strcpy(message.cmd,"OUTADMINISTRATOR");
send(sockfd,&message,sizeof(message),0);
printf("正在等待服务器响应...........\n");
}
sleep(1);
system("clear");
break;
//注销账户
case 15:
printf("你确定要注销账户吗?\n");
printf("1 : 确定 2 : 取消 \n");
scanf("%d", &a);
if(a == 1)
{
printf("请输入id:\n");
scanf("%s",message.id);
getchar();
printf("请输入密保:\n");
scanf("%s",message.secret);
getchar();
strcpy(message.cmd,"KILLUSER");
printf("正在注销账户,请稍后......\n");
sleep(1);
send(sockfd,&message,sizeof(message),0);
sleep(1);
}
else
{
break;
}
break;
//查看聊天记录
case 14:
strcpy(message.cmd,"LOOKCHATRECORD");
send(sockfd,&message,sizeof(message),0);
break;
//退出聊天室
case 16:
system("clear");
//sleep(1);
printf("退出成功!\n");
printf("欢迎下次使用!\n");
exit(-1);
break;
//文件传输
case 13:
file_from(sockfd);
break;
default:
printf("选择有误!请重新输入!\n");
break;
}
}
}
oid file_from(int sockfd)
{
char sendline[100];
struct Message msg_text;
int from_fd;
int bytes_read;
char *from_ptr;
char filename_path[1024];
strcpy(msg_text.cmd,"FILE");
printf("请输入传输对象的id:\n");
scanf("%s",msg_text.id);
printf("请输入文件路径\n");
scanf("%s",filename_path);
if((from_fd = open(filename_path,O_RDONLY)) == -1)
{
perror("open error!\n");
printf("没发现此文件\n");
exit(1);
}
whlie(1)
{
memset(msg_text.buffer,0,sizeof(msg_text.buffer));
bytes_read = read(from_fd,msg_text.buffer,1024);
if((bytes_read == -1))
{
perror("read error!\n");
exit(1);
}
if(bytes_read == 0)
{
break;
}
send(sockfd,&msg_text,sizeof(msg_text),0);
sleep(3);
}
close(from_fd);
}
void file_recv(char buffer[])
{
int to_fd;
char filename_path[1024];
char * to_ptr;
int bytes_write;
if((to_fd = open("1.txt",O_APPEND | O_CREAT | O_WRONLY , 0655)) == -1)
{
perror("open error!\n");
exit(1);
}
to_ptr = buffer;
bytes_write = write(to_fd,to_ptr,strlen(buffer));
if((bytes_write == -1))
{
perror("write error!\n");
exit(1);
}
close(to_fd);
}
5.服务器代码
main.c
#include "server.h"
int main(int argc, char **argv)
{
printf("稍等服务器正在启动中...\n");
sleep(1);
system("clear");//它通过执行 clear 命令来清空终端
int sockfd;//设置网络字节序
int ret;
//打开数据库若不存在数据库则创建一个数据库
sqlite3 *ppdb;
ret = sqlite3_open("stu.db",&ppdb);
if(ret != SQLITE_OK)
{
printf("数据库打开失败: %s\n",sqlite3_errmsg(ppdb));//如果 sqlite3_open 函数执行失败,即返回值不等于 SQLITE_OK,则调用 sqlite3_errmsg(ppdb) 获取错误消息,并使用 printf 函数将其打印出来。
exit(-1);
}
printf("数据库打开成功!\n");
//创建表1保存注册同户的信息
CreatTable(ppdb);
//创建表2保存聊天记录
CreatTable2(ppdb);
//遍历表1里的注册用户信息
Id(ppdb);
//指向OnlineLinkList结构体里 id cfd pdb
OnlineLinkList *head;
OnlineLinkList *new_node;
thread_node node; //cfd head id pdb
CreateLink(&head);
//结构体指向链表
node.head = head;
//结构体指向数据库
node.ppdb = ppdb;
sockfd = socket(AF_INET,SOCK_STREAM,0);//ipv4 流失套接字
if(-1 == sockfd)
{
perror("socket error!\n");
exit(-1);
}
printf("socket successfully!\n");
int opt = 1;
setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));//设置地址可以被重复绑定
struct sockaddr_in server_addr;//struct sockaddr_in 是一个用于表示 IPv4 地址和端口的结构体。它是在网络编程中常用的结构体之一。
//在您提供的代码中,您声明了一个名为 server_addr 的变量
memset(&server_addr,0,sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = htons(INADDR_ANY);//ip地址
server_addr.sin_port = 8883;//网络端口号
//绑定
ret = bind(sockfd, (struct sockaddr*)&server_addr,sizeof(server_addr));
//判断
if(-1 == ret)
{
perror("绑定失败!\n");
exit(-1);
}
//绑定成功则进行进监听
ret = listen(sockfd,10);
//它的第一个参数是套接字描述符,第二个参数是将客户端连接请求排队的最大数量(也称为队列的长度)。
//在这个代码中,将队列的长度设置为 10。
if( -1 == ret)
{
perror("监听失败!\n");
exit(-1);//return -1;
}
printf("等待客户端连接......\n");
struct sockaddr_in Message_addr; //用于保存客户端信息
int length = sizeof(Message_addr);
//线程池初始化
struct threadpool *pool = threadpool_init(10, 100);
while(1)
{
//阻塞等待客服端连接
int ret = accept(sockfd,(struct sockaddr *)&Message_addr, &length);
if(-1 == ret)
{
perror("accept");
exit(-1);
}
printf("接收到客户端的连接 %d\n",ret);
node.cfd = ret; //cfd head id
//往线程池 任务队列 放任务
threadpool_add_job(pool, (void *)MyFun,(void *)&node);
}
close(sockfd);
thread_destroy(pool);
return 0;
}
server.h
#ifndef _SERVER_H_
#define _SERVER_H_
#include<sqlite3.h>
#include <ctype.h>
#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include<fcntl.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<errno.h>
#include<time.h>
#include<signal.h>
#include<arpa/inet.h>
enum
{
REG, //注册
LOGIN, //登录
FORGET, //忘记密码
LOOKUSERS, //查看在线用户
PRIVATE, //私聊
GROUP, //群聊
ANONPRIVATE,//私聊(匿名聊天)
ANONGROUP, //群聊(匿名聊天)
EXCUSE, //禁言
ADMINISTRATOR, //申请管理员
OUTADMINISTRATOR, //取消管理员
WORLD, //解除禁言
KICK, //踢人
KILLUSER, //注销账户
LOOKCHATRECORD, //聊天记录
//FILE //传输文件
};
//在线用户链表
typedef struct OnlineLinkList
{
char id[100];
char name[32];
int cfd;
int root;
int forbid_flag;
struct OnlineLinkList *next; //指针域,为了能够操作后面结点
//所以指针的类型为当前结构体的类型
}OnlineLinkList;
typedef struct thread_node
{
int cfd;
OnlineLinkList *head;
sqlite3 *ppdb;
}thread_node;
//保存信息结构体
typedef struct Message
{
char id[32]; //账号
char myid[32]; //用于保存自己的id
char name[32]; //昵称
char passwd[32]; //密码
char secret[32]; //密保
char cmd[32]; //聊天方式
int cfd; //聊天对象
char msg[128]; //聊天内容
int root; //管理员标志
char chat[1024]; //聊天记录
char buffer[1024];
}Message;
//创建一个空链表
void CreateLink(OnlineLinkList **head);
//创建新的结点
void CreateNode(OnlineLinkList ** new_node);
//头插法插入数据
void InsertNodeHead(OnlineLinkList *head, OnlineLinkList * new_node);
//遍历用户注册信息
void Id(sqlite3 * ppdb ) ;
//线程函数
void *MyFun(void *arg);
//查找id是否存在
int FindId(sqlite3 * ppdb , Message *data);
//注册
void Register(thread_node * node , Message *data);
//向数据库中插入数据
void InsertData(sqlite3 *ppdb , Message *data);
//创建表
void CreatTable(sqlite3 *ppdb);
//收发消息
void MsgSendRecv(thread_node * cfd_node);
//查找账号和密保是否存在
int FindSecret(sqlite3 * ppdb , Message *data);
//更新数据库(更改密码)
void UpdateData(sqlite3 *ppdb , Message *data);
//登录
void Login(thread_node *cfd_node , Message *data);
//登录验证
int VerifyIdPassword(sqlite3 *ppdb , Message *data);
//群聊
void GroupChat(sqlite3 *ppdb , OnlineLinkList *head , Message *data);
//私聊
void PrivateChat(thread_node *node , Message *data);
//群聊(匿名聊天)
void AnonGroupChat(OnlineLinkList *head , Message *data , sqlite3 *ppdb);
//私聊(匿名聊天)
void AnonPrivateChat(thread_node *node , Message *data);
//查看在线用户
int LookOnlineUsers(thread_node * node);
//检查自己是否在线
int InspectOwnOnline(thread_node *node);
//检查账号是否重复登录
int RepeatLogin(thread_node *node , Message *data);
//申请管理员身份
void DaministerUser(thread_node *node);
//撤销管理员身份
void OutDaministerUser(thread_node *node);
//踢人
void KickUser(thread_node *node , char *ID);
//禁言
int ForbidWorld(thread_node *cfd_node,char *ID);
//解禁
int CancelForbidWorld(thread_node *cfd_node,char *ID);
//注销用户
void CancelUser(thread_node * node,Message *data);
//删除注册信息
void DeleteData(sqlite3 *ppdb,Message *data);
//删除注销用户在链表内的信息
void KillLinkListUser(thread_node *node,char * ID);
//创建第二张表用于保存聊天记录
void CreatTable2(sqlite3 *ppdb);
//向第二张表中插入聊天记录
void InsertChatData(sqlite3 *ppdb , char *chat);
//遍历聊天记录
void PrintChatRecord(sqlite3 *ppdb , thread_node *node);
void FileRecv(thread_node *node,Message *data);
// 定长线程池实现
struct job //存放线程函数,和传参
{
void *(*func)(void *arg); //函数指针
void *arg;
struct job *next;
};
struct threadpool
{
int thread_num; //已开启线程池已工作线程
pthread_t *pthread_ids; // 薄脆线程池中线程id
struct job *head;
struct job *tail; // 任务队列的尾
int queue_max_num; //任务队列的最多放多少个
int queue_cur_num; //任务队列已有多少个任务
pthread_mutex_t mutex;
pthread_cond_t queue_empty; //任务队列为空
pthread_cond_t queue_not_emtpy; //任务队列不为空
pthread_cond_t queue_not_full; //任务队列不为满
int pool_close; //线程退出标志
};
void * threadpool_function(void *arg);//任务队列取数据 执行线程函数
struct threadpool * threadpool_init(int thread_num, int queue_max_num);
void threadpool_add_job(struct threadpool *pool, void *(*func)(void *), void *arg);//增加任务
void thread_destroy(struct threadpool *pool);
#endif
server.c
#include "server.h"
//设置超级用户标志, 置为1时候设置为超级用户, 等于0的时候,则不是超级用户
int superroot;
//创建一个空链表(保存在线用户)
void CreateLink(OnlineLinkList **head)
{
CreateNode(head);
(*head)->next = NULL; //初始化头指针指向空
}
//创建新的结点
void CreateNode(OnlineLinkList ** new_node)
{
//定义新的结点,在堆区开辟空间
*new_node = (OnlineLinkList *)malloc(sizeof(OnlineLinkList));
//判断新结点开辟新空间是否成功
if(NULL == new_node)
{
printf("malloc error!\n"); //打印错误信息
exit(-1); //异常退出
}
}
//头插法插入数据
void InsertNodeHead(OnlineLinkList *head, OnlineLinkList * new_node)
{
//新的结点指向第一个结点的地址
new_node->next = head->next;
//头结点指向第一个结点
head->next = new_node;
}
/*函数首先获取要删除节点的信息,包括链表头指针 head 和客户端套接字描述符 cfd。
然后,使用两个辅助指针 p1 和 p2 来遍历链表。p1 指向链表中的当前节点,而 p2 则指向 p1 前面的节点。
如果链表为空(p1 为空),则不执行任何操作。
如果链表不为空,则通过遍历链表查找与给定 cfd 匹配的节点。当找到匹配的节点时,将 p2 的 next 指针指向 p1 的下一个节点,然后释放 p1 节点的内存。
如果在遍历链表时没有找到匹配的节点,则打印消息 “no such Message!”。
这段代码假设链表是一个名为 OnlineLinkList 的自定义结构体,其中包含指向下一个节点的指针 next,以及存储客户端套接字描述符的变量 cfd。
请注意,根据代码的上下文,可能需要进一步处理其他与节点和链表相关的细节,例如释放相关资源或更新链表中其他字段的值。*/
void DeleteNode(thread_node *node)
{
OnlineLinkList *p1 = NULL;
OnlineLinkList *p2 = NULL;
OnlineLinkList *head = NULL;
int cfd;
head = node->head;
cfd = node->cfd;
p1 = head->next;
p2 = head;
// 空链表
if (p1 == NULL) {
// 处理空链表情况
}
else {
// 遍历链表查找要删除的节点
while (p1 != NULL && p1->cfd != cfd) {
p2 = p1;
p1 = p1->next;
}
// 未找到节点
if (p1 == NULL) {
printf("no such Message!\n");
}
else {
// 删除节点
p2->next = p1->next;
free(p1);
}
}
}
//线程函数
void *MyFun(void *arg)
{
thread_node node;
node = *((struct thread_node *)arg);
while(1)
{
//收发消息
MsgSendRecv(&node);
}
//关闭当前通信接口
close(node.cfd);
return NULL;
}
int FindId(sqlite3 *ppdb, Message *data)
{
char sq1[128] = {0};
sprintf(sq1, "select *from mytable;");
char **result;
int row, column;
int flag = 0;
// 执行 SQL 查询,获取结果保存在 result 中
int ret = sqlite3_get_table(ppdb, sq1, &result, &row, &column, NULL);
if (ret != SQLITE_OK)
{
printf("sqlite3_get_table: %s\n", sqlite3_errmsg(ppdb));
return 1;
}
int Index = column;
for (int i = 0; i < row; i++)
{
for (int j = 0; j < column; j++)
{
// 检查当前列是否为第一列
// 如果是第一列,比较其值与要查找的 ID
if (Index % column == 0)
{
if (strcmp(result[Index], data->id) == 0)
{
flag = 1;
break;
}
}
Index++;
}
if (flag == 1)
{
break;
}
}
// 根据查找结果返回相应的值
if (flag == 1)
{
sqlite3_free_table(result);
return -1; // ID 存在
}
sqlite3_free_table(result);
return 0; // ID 不存在
}
/*该函数使用 sqlite3_get_table() 函数执行一个 SELECT 查询,从名为 mytable 的表中检索所有数据,并将结果保存在 result 数组中。
然后,通过遍历结果数组来查找与指定 ID (data->id) 匹配的值。函数通过在第一列进行比较来判断是否匹配。
如果找到匹配的 ID,则将 flag 设置为 1,并退出循环。
最后,根据 flag 值判断是否找到了匹配的 ID。如果找到,则释放结果表并返回 -1,表示 ID 存在。如果没有找到匹配的 ID,则释放结果表并返回 0,表示 ID 不存在。
请注意,该代码仅执行了简单的 SELECT 查询,如果需要更具体的查询条件或其他操作,请根据具体需求进行修改。此外,确保代码中包含适当的错误处理和资源释放,以避免内存泄漏和错误的数据库操作。*/
//注册
void Register(thread_node * node,Message *data)
{
//记录cfd 传回注册成功
data->cfd = node->cfd;
//注册前查找是否被注册
if(-1 == FindId(node->ppdb , data))
{
char arr[128] = {"账号已经存在,请重新注册"};
send(data->cfd,arr,strlen(arr),0);
return ;
}
else
{
//向数据库中插入数据
InsertData(node->ppdb,data);
}
}
//向数据库中插入数据
void InsertData(sqlite3 *ppdb,Message *data)
{
char str[128];
char *sql = str;
char *errmsg = NULL;
sprintf(sql,"insert into mytable(id,name,passwd,secret) values('%s','%s','%s','%s');",
data->id,data->name,data->passwd,data->secret);
if(SQLITE_OK != sqlite3_exec(ppdb,sql,NULL,NULL,&errmsg))
{
printf("insert record fail! %s \n",errmsg);
sqlite3_close(ppdb);
exit(-1);
}
char arr[100] = {"账号注册成功"};
send(data->cfd,arr,strlen(arr),0);
}
//创建表(用于保存注册用户的信息)
void CreatTable(sqlite3 *ppdb)
{
//创建表
char sq1[128] = {0};
sprintf(sq1,"create table if not exists mytable(id char,name char,passwd char,secret char);");
int ret = sqlite3_exec(ppdb,sq1,NULL,NULL,NULL);
if(ret != SQLITE_OK)
{
printf("sqlite3_exec: %s\n",sqlite3_errmsg(ppdb));
exit(-1);
}
}
//忘记密码
void ForgetSecret(thread_node * node,Message *data)
{
//记录cfd 传回注册成功
data->cfd = node->cfd;
//更改密码前查询账号和密保是否有误
if(-1 == FindSecret(node->ppdb , data))
{
//更新数据库
UpdateData(node->ppdb,data);
}
else
{
char arr[128] = {"账号或密保错误\n"};
send(data->cfd,arr,strlen(arr),0);
return ;
}
}
//查找id和密保是否存在
int FindSecret(sqlite3 * ppdb , Message *data)
{
char sq1[128] ={0};
sprintf(sq1,"select *from mytable;");
char **result;
int row,column;
int flag = 0;
int ret = sqlite3_get_table(ppdb,sq1,&result,&row,&column,NULL);
if(ret != SQLITE_OK)
{
printf("sqlite3_get_table: %s\n",sqlite3_errmsg(ppdb));
return -1;
}
int Index = column;
/*首先,Index%column == 0用来检查当前列是否是第一列。
这里利用了取模运算符(%),如果Index能够被column整除,那么它就是一个新的行的第一列。
然后,使用strcmp函数将result[Index]和data->id进行比较,
将比较结果存储在变量ret1中。同样地,使用strcmp函数将result[Index+3]和data->secret进行比较,将比较结果存储在变量ret2中。
最后,检查ret1和ret2是否都等于0,如果相等,则表示找到了匹配的ID和密保。
在这种情况下,将flag设置为1,表示找到了匹配,并且使用break语句退出内层循环。*/
for(int i = 0; i < row ; i++)
{
for(int j = 0; j < column; j++)
{
if(Index%column == 0)
{
int ret1 = strcmp(result[Index] , data->id);
int ret2 = strcmp(result[Index+3] , data->secret);
if( ret1== 0 && ret2 == 0)
{
flag = 1;
break;
}
}
Index++;
}
if(flag == 1)
{
break;
}
}
if(flag == 1)
{
sqlite3_free_table(result);
return -1;
}
sqlite3_free_table(result);
}
//更新数据库(更改密码)
void UpdateData(sqlite3 *ppdb,Message *data)
{
char sq1[128] ={0};
sprintf(sq1,"update mytable set passwd='%s' where id= '%s' ;",data->passwd, data->id);
char **result;
int row,column;
int flag = 0;
int ret = sqlite3_get_table(ppdb,sq1,&result,&row,&column,NULL);
if(ret != SQLITE_OK)
{
printf("sqlite3_get_table: %s\n",sqlite3_errmsg(ppdb));
exit(-1);
}
char arr[100] = {"密码更改成功"};
send(data->cfd,arr,strlen(arr),0);
}
//检查账号是否重复登录
int RepeatLogin(thread_node *node , Message *data)
{
OnlineLinkList *head = NULL;
head = node->head;
OnlineLinkList *p= NULL;
p = head->next;
if(p == NULL)
{
//无用户在线
return 0;
}
while(p != NULL && strcmp(p->id,data->id) != 0)
{
p = p->next;
}
if(p == NULL)
{
//该id不在线
return 0;
}
else
{
//已经登录
return -1;
}
}
//登录
void Login(thread_node *node , Message *data)
{
if(-1 == RepeatLogin(node , data))
{
char arr[128] = {"您已经在线,无需重复登录"};
send(node->cfd,arr,strlen(arr),0);
return;
}
OnlineLinkList *new_node;
OnlineLinkList *head = node->head;
//记录cfd 传回成功
data->cfd = node->cfd;
//接下来,声明了两个指针变量 new_node 和 head。new_node 可以用于创建新增的链表节点,而 head 则是为了遍历链表或者访问头节点的信息。
if(-1 != FindId(node->ppdb,data))
{
char arr[128] = {"该账号不存在,请重新登录"};
send(data->cfd,arr,strlen(arr),0);
return;
}
else
{
//登录前查找账号跟密码是否正确
int ret = VerifyIdPassword(node->ppdb, data);
if(ret == -1)
{
char arr[128] = {"登录成功"};
send(data->cfd,arr,strlen(arr),0);
//创建新的节点
CreateNode(&new_node);
/*
//根据id查找name
char sq1[128] ={0};
sprintf(sq1,"select *from mytable;");
char **result;
int row,column;
int flag = 0;
int ret = sqlite3_get_table(node->ppdb,sq1,&result,&row,&column,NULL);
if(ret != SQLITE_OK)
{
printf("sqlite3_get_table: %s\n",sqlite3_errmsg(node->ppdb));
return ;
}
int Index = column;
char aa[128];
for(int i = 0; i < row ; i++)
{
for(int j = 0; j < column; j++)
{
if(Index%column == 0)
{
if(strcmp(result[Index] , data->id) == 0)
{
strcpy(aa,result[Index + 1]);
flag = 1;
break;
}
}
Index++;
}
if(flag == 1)
{
break;
}
}
*/
//把该账户的id复制的链表里
strcpy(new_node->id,data->id);
new_node->cfd = data->cfd;
new_node->root = 0;
new_node->forbid_flag = 0;
//头插法插入新的数据
InsertNodeHead(head,new_node);
}
else
{
char arr[128] = {"账号或密码错误"};
send(data->cfd,arr,strlen(arr),0);
}
}
}
//验证登录的账号和密码
int VerifyIdPassword(sqlite3 *ppdb , Message *data)
{
char sq1[128] ={0};
sprintf(sq1,"select *from mytable;");
char **result;
int row,column;
int flag = 0;
int ret = sqlite3_get_table(ppdb,sq1,&result,&row,&column,NULL);
if(ret != SQLITE_OK)
{
printf("sqlite3_get_table: %s\n",sqlite3_errmsg(ppdb));
return -1;
}
int Index = column;
for(int i = 0; i < row ; i++)
{
for(int j = 0; j < column; j++)
{
if(Index%column == 0)
{
int ret1 = strcmp(result[Index] , data->id);
int ret2 = strcmp(result[Index + 2] , data->passwd);
if( ret1 == 0 && ret2 == 0 )
{
flag = 1;
break;
}
}
Index++;
}
if(flag == 1)
{
break;
}
}
if(flag == 1)
{
sqlite3_free_table(result);
return -1;
}
sqlite3_free_table(result);
}
//收发消息
void MsgSendRecv(thread_node * node)
{
int ret;
Message RecvInfo;
ret = recv(node->cfd,&RecvInfo,sizeof(RecvInfo),0);
if(ret == 0)
{
DeleteNode(node);
pthread_exit(NULL);
}
else
{
//注册
if(strcmp(RecvInfo.cmd,"REG") == 0)
{
Register(node,&RecvInfo);
}
//忘记密码
else if(strcmp(RecvInfo.cmd,"FORGET") == 0)
{
ForgetSecret(node,&RecvInfo);
}
//登录
else if(strcmp(RecvInfo.cmd,"LOGIN") == 0)
{
Login(node,&RecvInfo);
}
//查看在线用户
else if(strcmp(RecvInfo.cmd,"LOOKUSERS") == 0)
{
if(-1 == InspectOwnOnline(node))
{
char arr[128] ={"你未在线,不能查看在线用户,请先登录"};
send(node->cfd,arr,strlen(arr),0);
}
else
{
LookOnlineUsers(node);
}
}
//群聊
else if(strcmp(RecvInfo.cmd,"GROUP") == 0)
{
if(-1 == InspectOwnOnline(node))
{
char arr[128] ={"你未在线,不能群发消息,请先登录"};
send(node->cfd,arr,strlen(arr),0);
}
else
{
GroupChat(node->ppdb ,node->head,&RecvInfo);
}
}
//私聊
else if(strcmp(RecvInfo.cmd,"PRIVATE") == 0)
{
if(-1 == InspectOwnOnline(node))
{
char arr[128] ={"你未在线,不能私发消息,请先登录"};
send(node->cfd,arr,strlen(arr),0);
}
else
{
PrivateChat(node,&RecvInfo);
}
}
//群聊(匿名聊天)
else if(strcmp(RecvInfo.cmd,"ANONGROUP") == 0)
{
if(-1 == InspectOwnOnline(node))
{
char arr[128] ={"你未在线,不能群发消息,请先登录"};
send(node->cfd,arr,strlen(arr),0);
}
else
{
AnonGroupChat(node->head,&RecvInfo,node->ppdb);
}
}
//私聊(匿名聊天)
else if(strcmp(RecvInfo.cmd,"ANONPRIVATE") == 0)
{
if(-1 == InspectOwnOnline(node))
{
char arr[128] ={"你未在线,不能私发消息,请先登录"};
send(node->cfd,arr,strlen(arr),0);
}
else
{
AnonPrivateChat(node,&RecvInfo);
}
}
//申请管理员
else if(strcmp(RecvInfo.cmd,"ADMINISTRATOR") == 0)
{
if(-1 == InspectOwnOnline(node))
{
char arr[128] ={"请先登录"};
send(node->cfd,arr,strlen(arr),0);
}
int i;
printf("账号%s正在申请管理员身份,是否同意\n",RecvInfo.id);
printf("1: 是 2: 否\n");
scanf("%d",&i);
if(i == 1)
{
char arr[100] = {"管理员身份申请成功"};
send(node->cfd,arr,strlen(arr),0);
//将管理员标志置位1 cfd查找
DaministerUser(node);
}
if(i == 2)
{
char arr[100] = {"不同意管理员身份申请"};
send(node->cfd,arr,strlen(arr),0);
}
}
//撤销管理员身份
else if(strcmp(RecvInfo.cmd,"OUTADMINISTRATOR") == 0)
{
if(-1 == InspectOwnOnline(node))
{
char arr[128] ={"请先登录"};
send(node->cfd,arr,strlen(arr),0);
}
int i;
printf("账号%s正在申请取消管理员身份,是否同意\n",RecvInfo.id);
printf("1: 是 2: 否\n");
scanf("%d",&i);
if(i == 1)
{
char arr[100] = {"撤销管理员身份成功"};
send(node->cfd,arr,strlen(arr),0);
//将管理员标志置位1 cfd查找
OutDaministerUser(node);
}
if(i == 2)
{
char arr[100] = {"不同意撤销管理员身份申请"};
send(node->cfd,arr,strlen(arr),0);
}
}
//踢人
else if (strcmp(RecvInfo.cmd,"KICK") == 0)
{
KickUser(node,RecvInfo.id);
}
//禁言
else if(strcmp(RecvInfo.cmd,"EXCUSE") == 0)
{
int ret = ForbidWorld(node,RecvInfo.id);
if(0 == ret)
{
char arr[100] = {"该id用户不在线"};
send(node->cfd,arr,strlen(arr),0);
}
else if(-1 == ret)
{
usleep(1);
char arr[100] = {"禁言成功"};
send(node->cfd,arr,strlen(arr),0);
}
else
{
char arr[100] = {"对方是管理员,不可禁言"};
send(node->cfd,arr,strlen(arr),0);
}
}
//解禁
else if(strcmp(RecvInfo.cmd,"WORLD") == 0)
{
int ret = CancelForbidWorld(node,RecvInfo.id);
if(0 == ret)
{
char arr[100] = {"该id用户不在线"};
send(node->cfd,arr,strlen(arr),0);
}
else if( 1 == ret)
{
char arr[100] = {"解禁成功"};
send(node->cfd,arr,strlen(arr),0);
}
else
{
char arr[100] = {"该用户不需要解禁"};
send(node->cfd,arr,strlen(arr),0);
}
}
//注销用户
else if(strcmp(RecvInfo.cmd,"KILLUSER") == 0)
{
CancelUser(node,&RecvInfo);
}
//查看聊天记录
else if(strcmp(RecvInfo.cmd,"LOOKCHATRECORD") == 0)
{
if(-1 == InspectOwnOnline(node))
{
char arr[128] ={"你未在线,不能查看在线用户,请先登录"};
send(node->cfd,arr,strlen(arr),0);
}
else
PrintChatRecord(node->ppdb , node);
}
//传输文件
else if(strcmp(RecvInfo.cmd,"FILE") == 0)
{
if(-1 == InspectOwnOnline(node))
{
char arr[128] ={"你未在线,不能传输文件,请先登录"};
send(node->cfd,arr,strlen(arr),0);
}
else
FileRecv(node , &RecvInfo);
}
}
}
void FileRecv(thread_node *node,Message *data)
{
if (-1 == InspectOwnOnline(node))
{
char arr[100] = {"你未在线,不能发文件"};
send(node->cfd,arr,strlen(arr),0);
return ;
}
int len;
OnlineLinkList *p = NULL;
p = node->head->next;
//寻找在线用户链表中的cfd与私聊的cfd是否一致
while(p != NULL && strcmp(p->id, data->id) != 0)
{
//printf("****%s\n",p->id);
p = p->next;
}
//判断是否在线
if(p == NULL)
{
//printf("client is not online!\n");
char arr[100] = {"该用户不在线"};
send(node->cfd,arr,strlen(arr),0);
}
else
{
printf("正在接收中......\n");
char arr[100] = {"AAAAA"};
send(p->cfd,arr,strlen(arr),0);
usleep(30);
//向客户端发送文件
send(p->cfd,data->buffer,strlen(data->buffer),0);
}
}
//遍历用户注册信息
void Id(sqlite3 * ppdb )
{
char sq1[128] ={0};
sprintf(sq1,"select *from mytable;");
char **result;
int row,column;
int flag = 0;
int ret = sqlite3_get_table(ppdb,sq1,&result,&row,&column,NULL);
if(ret != SQLITE_OK)
{
printf("sqlite3_get_table: %s\n",sqlite3_errmsg(ppdb));
exit(-1);
}
int Index = column;
for(int i = 0; i < row ; i++)
{
for(int j = 0; j < column; j++)
{
printf("%s = %s ",result[j] , result[Index]);
Index++;
}
putchar(10);
}
}
//群聊
void GroupChat(sqlite3 *ppdb , OnlineLinkList *head , Message *data)
{
int length;
OnlineLinkList *p = NULL;
p = head->next;
length = strlen(data->msg);
char aa[100];
int len;
memset(aa,0,sizeof(aa));
char chat[1000] = {0};
strcpy(chat ,data->name);
strcat(chat ,":");
strcat(chat , data->msg);
InsertChatData(ppdb,chat);
while(p != NULL)
{
//发给每一个客户端
strcpy(aa,data->name);
strcat(aa,":");
len = strlen(aa);
send(p->cfd,aa,len,0);
send(p->cfd,data->msg,length,0);
p = p->next;
}
}
//私聊
void PrivateChat(thread_node *node , Message *data)
{
int length;
OnlineLinkList *p = NULL;
p = node->head->next;
length = strlen(data->msg);
char aa[100];
int len;
memset(aa,0,sizeof(aa));
//寻找在线用户链表中的cfd与私聊中的cfd是否一致
while(p != NULL && strcmp(p->id , data->id) != 0)
{
p = p->next;
}
if(p == NULL)
{
char arr[100] = {"该用户不在线"};
send(node->cfd,arr,strlen(arr),0);
}
else
{
strcpy(aa,data->name);
strcat(aa,":");
len = strlen(aa);
send(p->cfd,aa,len,0);
send(p->cfd,data->msg,length,0);
char chat[1000] = {0};
strcpy(chat ,data->name);
strcat(chat ,":");
strcat(chat , data->msg);
InsertChatData(node->ppdb,chat);
}
//群聊(匿名聊天)
void AnonGroupChat(OnlineLinkList *head , Message *data , sqlite3 *ppdb)
{
int length;
OnlineLinkList *p = NULL;
p = head->next;
length = strlen(data->msg);
char aa[100];
int len;
memset(aa,0,sizeof(aa));
char chat[1000] = {0};
strcpy(chat ,"匿名消息");
strcat(chat ,":");
strcat(chat , data->msg);
InsertChatData(ppdb,chat);
while(p != NULL)
{
//发给每一个客户端
strcpy(aa,"匿名消息");
strcat(aa,":");
len = strlen(aa);
send(p->cfd,aa,len,0);
send(p->cfd,data->msg,length,0);
p = p->next;
}
}
//私聊(匿名聊天)
void AnonPrivateChat(thread_node *node , Message *data )
{
int length;
OnlineLinkList *p = NULL;
p = node->head->next;
length = strlen(data->msg);
char aa[100];
int len;
memset(aa,0,sizeof(aa));
//寻找在线用户链表中的cfd与私聊中的cfd是否一致
while(p != NULL && strcmp(p->id , data->id) != 0)
{
p = p->next;
}
if(p == NULL)
{
char arr[100] = {"该用户不在线"};
send(node->cfd,arr,strlen(arr),0);
}
else
{
strcpy(aa,"匿名消息");
strcat(aa,":");
len = strlen(aa);
send(p->cfd,aa,len,0);
send(p->cfd,data->msg,length,0);
char chat[1000] = {0};
strcpy(chat ,"匿名消息");
strcat(chat ,":");
strcat(chat , data->msg);
InsertChatData(node->ppdb,chat);
}
}
//查看在线用户
int LookOnlineUsers(thread_node * node)
{
OnlineLinkList *head = NULL;
head = node->head;
OnlineLinkList *p = NULL;
p = head->next;
char bb[128];
if(p == NULL)
{
return 0; //当前无用户在线
}
while(p != NULL)
{
memset(bb,0,sizeof(bb));
strcpy(bb,p->id);
send(node->cfd,bb,strlen(bb),0);
p = p->next;
usleep(4);
}
return 1;
}
//检查自己是否在线
int InspectOwnOnline(thread_node *node)
{
OnlineLinkList *head= NULL;
head = node->head;
OnlineLinkList *p= NULL;
p = head->next;
if(p == NULL)
{
//无用户在线
return -1;
}
while(p != NULL && p->cfd != node->cfd)
{
p = p->next;
}
if(p == NULL)
{
//自己不在线
return -1;
}
else
{
//自己已经登录
return 0;
}
}
//申请管理员身份
void DaministerUser(thread_node *node)
{
OnlineLinkList *p = NULL;
OnlineLinkList *head = NULL;
head = node->head;
p =head->next;
if(p == NULL)
{
return;
}
else
{
while(p != NULL && p->cfd != node->cfd)
{
p = p->next;
}
if(p == NULL)
{
return;
}
else
{
p->root = 1;
return;
}
}
}
//撤销管理员身份
void OutDaministerUser(thread_node *node)
{
OnlineLinkList *p = NULL;
OnlineLinkList *head = NULL;
head = node->head;
p =head->next;
if(p == NULL)
{
return;
}
else
{
while(p != NULL && p->cfd != node->cfd)
{
p = p->next;
}
if(p == NULL)
{
return;
}
else
{
p->root = 0;
return;
}
}
}
//踢人
void KickUser(thread_node *node,char * ID)
{
OnlineLinkList *p1 = NULL;
OnlineLinkList *p2 = NULL;
OnlineLinkList *head = NULL;
char *id = ID;
head = node->head;
p1 = head->next;
p2 = head;
if(p1 == NULL)
{
char arr[100] = {"该用户不在群组,踢人失败"};
send(node->cfd,arr,strlen(arr),0);
}
else
{
//cfd判断下线
while(p1 != NULL && strcmp(p1->id,id) != 0)
{
p2 = p1;
p1 = p1->next;
}
if(p1 == NULL)
{
char arr[100] = {"该用户不存在"};
send(node->cfd,arr,strlen(arr),0);
}
else
{
if(p1->root != 1)
{
p2->next = p1->next;
//踢人
free(p1);
char arr[100] = {"对方已被强制下线"};
send(node->cfd,arr,strlen(arr),0);
memset(arr,0,sizeof(arr));
char *ar;
ar = "你已经被管理员强制下线";
send(p1->cfd,ar,strlen(ar),0);
}
else
{
char arr[100] = {"对方是管理员,无权强制对方下线"};
send(node->cfd,arr,strlen(arr),0);
}
}
}
}
//禁言
int ForbidWorld(thread_node *cfd_node,char *ID)
{
OnlineLinkList *head = NULL;
head = cfd_node->head;
OnlineLinkList *p = NULL;
p = head->next;
if(p == NULL)
{
//无用户在线
return 0;
}
while(p != NULL && strcmp(p->id,ID) !=0 )
{
p = p->next;
}
if(p == NULL)
{
//该用户不在线
return 0;
}
else
{
if(p->root == 1)
{
//对方是管理员不可禁言
return 1;
}
else
{
p->root = 1;
char arr[100] = {"你已经被管理员禁言"};
send(p->cfd,arr,strlen(arr),0);
return -1;
}
}
}
//解禁
int CancelForbidWorld(thread_node *cfd_node,char *ID)
{
OnlineLinkList *head = NULL;
head = cfd_node->head;
OnlineLinkList *p = NULL;
p = head->next;
if(p == NULL)
{
//无用户在线
return 0;
}
while(p != NULL && strcmp(p->id,ID) !=0 )
{
p = p->next;
}
if(p == NULL)
{
//该用户不在线
return 0;
}
else
{
char arr[100] = {"你已经被管理员解禁"};
p->forbid_flag = 0;
send(p->cfd,arr,strlen(arr),0);
usleep(1);
}
}
//注销用户
void CancelUser(thread_node * node,Message *data)
{
//记录cfd 传回注册成功
data->cfd = node->cfd;
//注销账户前查询账号和密保是否有误
if(-1 == FindSecret(node->ppdb , data))
{
//删除注册信息
DeleteData(node->ppdb,data);
KillLinkListUser(node,data->id);
}
else
{
char arr[128] = {"账号或密保错误\n"};
send(data->cfd,arr,strlen(arr),0);
return ;
}
}
//删除注册信息
void DeleteData(sqlite3 *ppdb,Message *data)
{
char sq1[128] ={0};
sprintf(sq1,"delete from mytable where id = '%s';",data->id);
char **result;
int row,column;
int ret = sqlite3_get_table(ppdb,sq1,&result,&row,&column,NULL);
if(ret != SQLITE_OK)
{
printf("sqlite3_get_table: %s\n",sqlite3_errmsg(ppdb));
exit(-1);
}
char arr[100] = {"注销成功"};
send(data->cfd,arr,strlen(arr),0);
}
//删除注销用户在链表内的信息
void KillLinkListUser(thread_node *node,char * ID)
{
OnlineLinkList *p1 = NULL;
OnlineLinkList *p2 = NULL;
OnlineLinkList *head = NULL;
char *id = ID;
head = node->head;
p1 = head->next;
p2 = head;
if(p1 == NULL)
{
}
else
{
//cfd判断下线
while(p1 != NULL && strcmp(p1->id,id) != 0)
{
p2 = p1;
p1 = p1->next;
}
if(p1 == NULL)
{
char arr[100] = {"该用户不存在"};
send(node->cfd,arr,strlen(arr),0);
}
else
{
p2->next = p1->next;
free(p1);
}
}
}
//创建第二张表用于保存聊天记录
void CreatTable2(sqlite3 *ppdb)
{
//创建表
char sql[128] = {0};
sprintf(sql , "create table if not exists chat(chat char);");
int ret = sqlite3_exec(ppdb,sql,NULL,NULL,NULL);
if(ret != SQLITE_OK)
{
printf("sqlite3_exec: %s\n",sqlite3_errmsg(ppdb));
exit(-1);
}
}
//向第二张表中插入聊天记录
void InsertChatData(sqlite3 *ppdb , char *chat)
{
char str[128];
char *sql = str;
char *errmsg = NULL;
sprintf(sql,"insert into chat(chat) values('%s');", chat);
if(SQLITE_OK != sqlite3_exec(ppdb,sql,NULL,NULL,&errmsg))
{
printf("insert record fail! %s \n",errmsg);
sqlite3_close(ppdb);
exit(-1);
}
sprintf(sql,"insert into chat(chat) values('%s');", "\n");
if(SQLITE_OK != sqlite3_exec(ppdb,sql,NULL,NULL,&errmsg))
{
printf("insert record fail! %s \n",errmsg);
sqlite3_close(ppdb);
exit(-1);
}
}
//遍历聊天记录
void PrintChatRecord(sqlite3 *ppdb , thread_node *node)
{
OnlineLinkList *head = NULL;
head = node->head;
OnlineLinkList *p = NULL;
p = head->next;
char sql[128] = {0};
//sprintf(sql , "selete *from mytable;");
sprintf(sql,"select *from chat");
char **result;
int row,column;
int ret = sqlite3_get_table(ppdb,sql,&result,&row,&column,NULL);
if(ret != SQLITE_OK)
{
printf("sqlite3_get_table: %s\n",sqlite3_errmsg(ppdb));
exit(-1);
}
int Index = column;
char chat[1000] = {0};
for(int i = 0; i < row ; i++)
{
for(int j = 0; j < column; j++)
{
strcpy(chat , result[Index]);
send(node->cfd , chat ,strlen(chat), 0);
Index++;
}
}
if(row == 0) //当聊天记录为0行的时候聊天记录为空
{
strcpy(chat , "当前还没有聊天记录");
send(node->cfd , chat ,strlen(chat), 0);
return;
}
else
{
return;
}
}
//线程池
void * threadpool_function(void *arg) //任务队列取数据 执行任务
{
struct threadpool *pool = (struct threadpool *)arg;
struct job *pjob = NULL;
while (1)
{
//我访问时别人不能让问 访问任务队列10个一个一个来
pthread_mutex_lock(&(pool->mutex));
while(pool->queue_cur_num == 0)
{
//当目前任务队列没有任务 等待任务队列不为空的条件被置位(添加任务处成功过来唤醒)
pthread_cond_wait(&(pool->queue_not_emtpy), &(pool->mutex));
//线程要结束时 退出
if (pool->pool_close == 1)
{
pthread_exit(NULL);
}
}
pjob = pool->head; //将对头任务拿出去处理
pool->queue_cur_num--; //任务数量减一个
if (pool->queue_cur_num != pool->queue_max_num)
{
//如果任务不满 不满条件唤醒
pthread_cond_broadcast(&(pool->queue_not_full));
}
if (pool->queue_cur_num == 0)
{
//当前任务队列没有任务
pool->head = pool->tail = NULL;
//当任务队列为空时 唤醒空条件,去销毁线程池
pthread_cond_broadcast(&(pool->queue_empty));
}
else
{
pool->head = pjob->next; //处理完一个 队头向后移动一个
}
pthread_mutex_unlock(&(pool->mutex));
(*(pjob->func))(pjob->arg);//让线程执行任务队列里的任务
free(pjob);//执行完释放
pjob = NULL;
}
}
struct threadpool * threadpool_init(int thread_num, int queue_max_num)
{
struct threadpool *pool = (struct threadpool *)malloc(sizeof(struct threadpool));
// malloc
pool->queue_max_num = queue_max_num;
pool->queue_cur_num = 0;
pool->pool_close = 0; //线程退出标志 0不退出
pool->head = NULL;
pool->tail = NULL;
pthread_mutex_init(&(pool->mutex), NULL);
pthread_cond_init(&(pool->queue_empty), NULL);
pthread_cond_init(&(pool->queue_not_emtpy), NULL);
pthread_cond_init(&(pool->queue_not_full), NULL);
pool->thread_num = thread_num;
pool->pthread_ids = (pthread_t *)malloc(sizeof(pthread_t) * thread_num);//乘 线程数量
// malloc
for (int i = 0; i < pool->thread_num; i++)
{
//创建线程
pthread_create(&pool->pthread_ids[i], NULL, (void *)threadpool_function, (void *)pool);
}
return pool;
}
void threadpool_add_job(struct threadpool *pool, void *(*func)(void *), void *arg)
{
pthread_mutex_lock(&(pool->mutex));
//队列满
while (pool->queue_cur_num == pool->queue_max_num)
{
//阻塞等待 不满条件发生
//队列任务满 不得添加
pthread_cond_wait(&pool->queue_not_full, &(pool->mutex));
}
//定义函数链表
struct job *pjob = (struct job *)malloc(sizeof(struct job));
//malloc
pjob->func = func;
pjob->arg = arg;
pjob->next = NULL;
// pjob->func(pjob->arg);
if (pool->head == NULL) //队列为空
{
pool->head = pool->tail = pjob; //队头队尾指向链表
//唤醒 告诉别人任务队列不为空
pthread_cond_broadcast(&(pool->queue_not_emtpy));
}
else //队尾向后移1个
{
pool->tail ->next = pjob;
pool->tail = pjob;
}
pool->queue_cur_num++; //队列任务加1
pthread_mutex_unlock(&(pool->mutex));
}
void thread_destroy(struct threadpool *pool)
{
pthread_mutex_lock(&(pool->mutex));
while (pool->queue_cur_num != 0)
{
//等任务队列为空 才能销毁 阻塞等待 空条件
pthread_cond_wait(&(pool->queue_empty),&(pool->mutex));
}
pthread_mutex_unlock(&(pool->mutex));
//为空 唤醒不满条件 看有没有阻塞的线程
pthread_cond_broadcast(&(pool->queue_not_full));
//pthread cond broadcast(&( pool->queue_not_empty));
//任务队列为空时 置为1 告诉其他线程要退出了
pool->pool_close = 1;
//回收线程资源
for (int i = 0; i < pool->thread_num; i++)
{
//每次都唤醒 不唤醒 阻塞无法执行 线程释放
pthread_cond_broadcast(&(pool->queue_not_emtpy));
// pthread_cancel(pool->pthread_ids[i]); / /有系统调用,才能销毁掉;有bug
printf("thread exit!\n");
pthread_join(pool->pthread_ids[i], NULL);
}
pthread_mutex_destroy(&(pool->mutex));
pthread_cond_destroy(&(pool->queue_empty));
pthread_cond_destroy(&(pool->queue_not_emtpy));
pthread_cond_destroy(&(pool->queue_not_full));
free(pool->pthread_ids);
//再次,释放每个节点
struct job *temp;
while(pool->head != NULL)
{
temp = pool->head;
pool->head = temp->next;
free(temp);
}
free(pool);
printf("destroy finish!\n");
}
6.项目总结