聊天室 epoll+线程池版本

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.项目总结

 

 

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值