(c语言)即时通讯系统—epoll+线程池 版本

  • Post author:
  • Post category:其他



目录


注意


一 .项目功能


二.项目简介


三.项目演示


四.项目功能基本介绍


四.代码部分


server.c


server.h


mian.c


客户端


client.c


client.h


main.c


注意

相比较第一个版本  第二个版本将服务器的多线程并发服务器改成了epoll+线程池处理

客户端依旧运用的是上个版本的客户端

一 .项目功能

目前该项目实现基本功能有:

基本功能:

1.注册 2.登录 3.群聊 4快捷短语 5.保存聊天记录  6.查看聊天记录


7.文件传输  8.敏感词汇判断   9.私聊  10.离线消息发送


11.查看在线用户   12.管理员申请 13.管理员专属头像   14.禁言用户


15.解除禁言   16.管理员踢人 17.修改密码  18.注销用户 19.撤销管理员身份

二.项目简介


epoll

1.相比较: select内部使用数组实现,poll是链表。他们需要做内核区到用户区的转换,还需要做数据拷贝,因此效率低

2.epoll不需要做内核区到用户区的转换,因为数据存在共享内存中。epoll维护的树在共享内存中,内核区和用户区去操作共享内存,因此不需要区域转换,也不需要拷贝操作。


线程池

(1)降低销毁资源:重复利用线程池中已经存在的线程,减少了线程的创建和消亡造成的性能开销。

(2)提高了相应速率:当任务到达时,任务可以不需要等到线程创建就能够执行。

(3)防止服务器过载:形成内存溢出,或者cpu耗尽。

(4)提高线程的可管理性:线程时稀缺资源,若无限的创建线程,不仅会消耗资源,还会降低系统的稳定性,使用线程池可以统一的分配,调优和监控

三.项目演示

即使通讯系统项目

四.项目功能基本介绍

## 三 .项目具体介绍

### 1.注册

```
注册有重复注册判断 在数据库  普通用户表中遍历  如果存在该用户则 停止注册

将注册的信息保存在通信协议中 服务器收到注册的功能标志 调用注册函数  将注册的信息插入数据库的普通用户表中;

```

#### 2.登录

```
用户登录  
用一个链表保存在线用户 链表里包含用户的文件描述符 负责与用户之间通信

登录成功将会给所有在线用户发送自己已上线;

登录第一步在数据库中遍历该用户是否注册  只有注册了才能进行登录操作 第一步是遍历链表是否已经登录如果已经在别处登录 将不能进行登录操作.
```


### 3.群聊

```
因为登录过程中所有的用户的文件描述符 都会保存在在线链表中  群聊直接遍历在线用户链表
对每个文件描述符对应的用户发送消息
群聊的第一步  首先判断除了自己以外是否还有别的用户在线  
第二步才是遍历链表 对除了自己的每个在线用户发送消息
```

### 4.快捷短语

```
在发送消息之前 判断 所要发送的消息  是否有对应的快捷短语 如果有快捷短语 则将快捷短语strcpy  至通信协议
聊天内容的字符串数组 进行发送
如果没有找对对应的快捷消息 则正常发送
```

### 5.聊天记录的保存以及查看

```
在服务器有一个recv函数循环的阻塞接受 客户端发来的结构体  也就是通信协议 每一次的信息传递都要经过这个函数
只用在recv函数下面  运用数据库相关的操作将 发送信息的用户  发送时间  以及消息内容存储至数据库中

查看聊天记录遍历数据库内容 将遍历的内容以固定格式保存在buf  发送给客户端;
```

### 6.文件传输

```
使用read函数 从打开的文件中循环读取内容 将内容与读取到的字节数 
保存在通信协议中内容保存专用于文件传输的字符串数组中,将读取到的字节数一并传输  为了写入时 按读取的字节数进行写入 
发送给服务器,再由服务器进行转发到对应的客户端  为了防止粘包现象采用传输结构体   
```

### 7.敏感词判断

```
敏感词判断  数据中有一张表保存了各种敏感词汇 当客户端进行发送消息时  服务器遍历数据库判断消息是否为敏感词汇  
一旦是敏感词汇 返回客户端 发送失败信号 并停止将敏感词汇转发给其他客户端
```

## 8.私聊

```
私聊第一步就是   服务器遍历在线用户链表  判断私聊对象是否在线  不在线返回给客户端  不在线消息
私聊和群聊相似   当客户端发送私聊信号时  服务器遍历链表  遍历出私聊对象  再将消息内容转发给对应的客户端
```

### 9.离线消息的发送

```
当私聊不在线是私聊信息会保存在数据库的 离线消息表中  当对方上线时 立即将消息发送给对方
```


### 10.查看在线用户

```
查看在线用户 和群聊相似 都是一个遍历链表的过程 当客户端发送查看在线用户的信号给 服务器  
服务器在进行遍历在线链表将每遍历一个用户 将其用户名按固定格式 发送给 需要查看在线用户的用户

```

## 管理员用户的相关操作

### 11.申请称为管理员设置管理员头像

```
只有先成为普通用户以后 在能申请管理员  管理员有专属的管理员头像
申请成管理员后必需重新以管理员身份进行登录
在群聊过程中  管理员自带头像  

客户端发送申请管理员信号以后  服务器 会将管理员用户信息 姓名密码头像等  
保存在数据库的root表中  
管理员用户 不可以重复申请

```

### 12.管理员禁言用户

### 13.解除禁言

```
管理员禁言  客户端给服务器发送一个 禁言信号  以及禁言对象   服务器首先判断发送禁言指令的是不是管理员  在对方是否在线  其次判断对方是不是管理员
管理员不能对管理员进行踢人 或者 禁言操作
如果对方是普通用则给对方发送禁言信号  这里采用的是全局变量flag  客户端收到禁言信号时flag至0 失去通信权限
收到解禁信号 flag至1 恢复通信权限
```



### 14.管理员踢人

```
管理员踢人  和禁言一样判断是否在线   在判断是否为管理员  如果为普通用户  则向对应的客户端发送退出聊天室指令 
客户端收到退出指令以后自主结束整个进程  同时服务器将在线用户链表中的该用户踢出链表 并给所有在线用户发送消息 
```

#### 15.修改密码

```
没有设置密保 直接根据名字进行修改密码   修改密码以后需要从新登录 
客户端发送 修改密码信号   以及新密码 至服务器  服务器调用封装好的数据库函数 执行update更新数据库的语句  根据名字对密码进行更新
update user set password = '%s' where name = '%s'
```

### 16.注销用户

```
注销用户和修改密码类似
首先进行账户密码判断 账户密码正确才能进行注销用户
数据库找到关于注销名字的记录 然后删除这条记录 同时服务器将在线用户链表中的该用户踢出链表; 
delete from user where name = '%s'

```

#### 17.撤销管理员身份

```
注销用户和注销用户类似
首先进行账户密码判断 账户密码正确才能进行撤销root
数据库找到关于撤销名字的记录 然后删除这条记录 同时服务器将在线用户链表中的该用户踢出链表; 
delete from user where name = '%s'
```

退出聊天室  结束整个进程

四.代码部分

server.c

#include "server.h"
int flag2 = 1;  //发送文件信号
extern  struct pthreadpool *pool ;
extern online_userlist *head;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
int socket_init()
{
    unsigned char ip_str[] = "192.168.91.151";

    unsigned char port_str[] = "8787";
    int sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd == -1)
    {
        perror("socket error");
        return -1;
    }
    //设置套接字端口属性为端口释放后可以重复使用
    int opt = 1;
    setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(int));

    //将套接字进行IP与端口的绑定
    struct sockaddr_in addr;
    memset(&addr, 0, sizeof(addr)); //清空内存
    addr.sin_family = AF_INET;
    addr.sin_port =  htons(atoi(port_str));              //端口和ip要改成大端模式
    addr.sin_addr.s_addr =  inet_addr(ip_str); 
    int ret = bind(sockfd, (struct sockaddr *)&(addr), sizeof(addr));
    if (ret == -1)
    {
        perror("bind error");
        printf("绑定套接字失败.\n");
        return -1;
    }
    //监听
    ret = listen(sockfd, 20);
    if (ret == -1)
    {
        perror("listen error");
        printf("监听套接字失败.\n");
        return -1;
    }
    printf("客户机已经开启,等待用户连接中.....\n");
    return sockfd;
}

// epoll操作
int mypoll(int sockfd)
{
    int N = sizeof(clientlist);//MSg为通信协议  计算通信协议的大小
    struct sockaddr_in clientaddr;//创建被填充的网络信息结构体
    socklen_t addrlen = sizeof(clientaddr); //计算被网络信息结构体的大小



    //第一步:创建epoll对象
    int epfd = epoll_create(2000);  //创建内核事件表 最大保存文件描述符个数为2000
    //如果用epoll_create1一般参数为0  每添加一个自动增加结点保存文件描述符
    if (-1 == epfd)//成功返回非负数的文件描述符 失败返回-1
    {
        ERRLOG("epoll");
    }
//epoll树上挂的就是下面这个结构体
    struct epoll_event ev, events[2000] = {0};//事件初始化  读 写 异常等
    ev.events = EPOLLIN; //监听sockfd可读
    ev.data.fd = sockfd;

    //操作epoll内核时间表
    // 参数一create生成的专用文件描述符  二操作类型 增 删 改  三 关联的文件描述符 四epoll_event结构体也就是上面的结构体
    int ret = epoll_ctl(epfd, EPOLL_CTL_ADD, sockfd, &ev);
    if (-1 == ret)
    {//成功返回0  失败返回-1
        printf("%s\n", strerror(errno));
        return -1;
    }


    //accept接受客户端连接
    int i;
    int acceptfd = 0;
    while (1)
    {
        clientlist agreement;
        //Msg msg;//通信协议
        //参数分析 参数一 create返回值 参数二结点结构体(传出参数) 参数三事件表的最大存储个数 参数四 阻塞方式永久阻塞0是立即返回
        int num = epoll_wait(epfd, events, 2000, -1);//等待IO事件发生
        if (-1 == num)
        {
            printf("epoll_wait() failed\n");
            return -1;
        }//成功:返回准备好的文件描述符的个数,如果没有准备号的,则返回0  错误:返回 -1



        for (i = 0; i < num; i++)
        {
            if (events[i].data.fd == sockfd) //表示有客户端发起链接
            {
                if ((acceptfd = accept(sockfd, (struct sockaddr *)&clientaddr, &addrlen)) == -1)
                {
                    ERRLOG("accept error");
                    continue;
                }
                //为新的文件描述符注册事件
                printf("accept fd  %d\n", acceptfd);
                ev.events = EPOLLIN; //监听sockfd可读
                ev.data.fd = acceptfd;
                int ret = epoll_ctl(epfd, EPOLL_CTL_ADD, acceptfd, &ev);
                if (-1 == ret)
                {
                    ERRLOG("epoll_ctl");
                }
                printf("客户端%s:%d 连接了\n", inet_ntoa(clientaddr.sin_addr), ntohs(clientaddr.sin_port));
            }
            else //有客户端发消息
            {
                //Myarg arg;
                Myarg* arg = (Myarg *)malloc(sizeof(Myarg));
                memset(arg, 0, sizeof(Myarg));
                if (events[i].events & EPOLLIN) //如果事件是可读的
                {
                    memset(&agreement, 0, sizeof(agreement));
                    ret = recv(events[i].data.fd, &agreement, N, 0);
                    if(agreement.flag == 3 || agreement.flag == 5)
                    {
                        insert_uprecord(&agreement);
                    }
                    if (ret == -1)
                    {
                        ERRLOG("recv");
                    }
                    else if (0 == ret)
                    {
                        ev.events = EPOLLIN; //监听sockfd可读
                        ev.data.fd = events[i].data.fd;
                        int ret = epoll_ctl(epfd, EPOLL_CTL_DEL, events[i].data.fd, &ev); //客户端退出,注销事件
                        if (-1 == ret)
                        {
                            ERRLOG("epoll_ctl");
                        }
                        printf("退出的客户端fd=%d\n",events[i].data.fd);
                        Offlineprocessing(events[i].data.fd);//掉线处理函数
                        close(events[i].data.fd);
                    }
                    else
                    {
                        // //信息拷贝
                        arg->msg.flag=agreement.flag;
                        arg->msg.root=agreement.root;
                        arg->msg.num=agreement.num;
                        strcpy(arg->msg.name, agreement.name);
                        strcpy(arg->msg.to_name, agreement.to_name);
                        strcpy(arg->msg.msg, agreement.msg);
                        printf("agreement.msg = %s\n",agreement.msg);
                        strcpy(arg->msg.emojio, agreement.emojio);
                        strcpy(arg->msg.password, agreement.password);
                        strcpy(arg->msg.buf, agreement.buf);
                        arg->fd = events[i].data.fd;
                        ThreadAddJob(pool, work, (void *)arg);//任務添加到任務隊列
                    }

                }
            }
        }
    }
}

//线程函数从任务队列去任务
void *ThreadRun(void *arg)  
{
    struct pthreadpool *pool = (struct pthreadpool *)arg;
    struct job *pjob = NULL;//任务队列指针
    while (1)
    {
        pthread_mutex_lock(&pool->mutex);
        if (pool->m_QueueCurNum == 0)
        {
            printf("当前任务队列为空,线程%ld阻塞等待任务到来!\n", pthread_self());
            pthread_cond_wait(&pool->m_QueueNotEmpty, &pool->mutex);
        }
        pjob = pool->head;//指针指向任务队列的头部
        pool->m_QueueCurNum--;//当前任务数量--
        if (pool->m_QueueCurNum != pool->m_QueueMaxNum)//如果当前任务队列没有满 
        {
            pthread_cond_signal(&pool->m_QueueNotFull); //每次线程取出一个任务之后,都要唤醒线程去添加任务
        }
        if (pool->m_QueueCurNum == 0)//如果当前任务对列没有任务
        {
            pool->head = pool->rear = NULL;
            pthread_cond_broadcast(&pool->m_QueueEmpty); //当通知任务队列添加任务无果后,发送条件变量,通知其销毁线程
        }
        else
        {
            pool->head = pool->head->next;
        }
        pthread_mutex_unlock(&pool->mutex);
        pjob->func(pjob->arg);//运行任务函数
        free(pjob);
        pjob = NULL;
    }
}

//参数分析  参数1线程池线程数量   参数二   任务队列最大任务数
struct pthreadpool *InitPthreadPool(int ThreadNum, int QueueMaxNum) //初始化線程池  創建線程池
{
    struct pthreadpool *pool = (struct pthreadpool *)malloc(sizeof(struct pthreadpool)); //创建一个线程池初始化的结构体
    pool->m_QueueCurNum = 0;//当前任务队列的任务数
    pool->m_QueueMaxNum = QueueMaxNum;//任务队列的最大最大任务数
    pool->head = NULL;
    pool->rear = NULL;
    pool->m_threadNum = ThreadNum;//已经开启的线程数量也就是创建的线程数量

    pool->m_pthreadIDs = (pthread_t *)malloc(sizeof(pthread_t) * ThreadNum);//保存已开启的线程IO
        //动态数组名  保证后续指针的移动合法
    pthread_mutex_init(&pool->mutex, NULL);//初始化一把锁
    pthread_cond_init(&pool->m_QueueEmpty, NULL);//任务队列为空条件
    pthread_cond_init(&pool->m_QueueNotEmpty, NULL);//任务队列不为空的条件
    pthread_cond_init(&pool->m_QueueNotFull, NULL);//任务队列不满的条件
    for (int i = 0; i < ThreadNum; i++)
    {
        pthread_create(&pool->m_pthreadIDs[i], NULL, ThreadRun, pool);
    }
    return pool;
}

//將任務函數添加到任務隊列
void ThreadAddJob(struct pthreadpool *pool, void *(*func)(void *arg), void *arg)//
{
    pthread_mutex_lock(&pool->mutex);
    if (pool->m_QueueCurNum == pool->m_QueueMaxNum)
    {
        printf("任务队列已满,挂起等待线程执行完毕。。。\n");
        pthread_cond_wait(&pool->m_QueueNotFull, &pool->mutex);
    }
    struct job *pjob = (struct job *)malloc(sizeof(struct job)); //创建任务队列
    pjob->func = func;//任务函数通过任务传参传入任务函数
    pjob->arg = arg;//任务函数的参数

    // pjob->func(pjob->arg);
    pjob->next = NULL;
    if (pool->head == NULL)//如果任务队列 里面没有任务
    {
        pool->head = pool->rear = pjob;//那么将任务函数放置头结点处
        pthread_cond_broadcast(&pool->m_QueueNotEmpty); //添加任务后,唤醒任意一个线程开始执行任务
    }
    else
    {
        pool->rear->next = pjob;//如果任务队列有任务  就将新的任务函数放在任务队的末尾
        pool->rear = pjob;//尾指针指向最后一盒任务函数
    }
    pool->m_QueueCurNum++;//当前任务数量++
    pthread_mutex_unlock(&pool->mutex);
}

//銷毀線程池
void ThreadDestroy(struct pthreadpool *pool)
{
    pthread_mutex_lock(&pool->mutex);
    while (pool->m_QueueCurNum != 0)
    {
        printf("阻塞等待销毁线程。。。\n");
        pthread_cond_wait(&pool->m_QueueEmpty, &pool->mutex);
    }
    printf("任务结束,线程%ld被销毁\n", pthread_self());
    pthread_mutex_unlock(&pool->mutex);
    pthread_cond_broadcast(&pool->m_QueueNotEmpty);
    pthread_cond_broadcast(&pool->m_QueueNotFull);
    int i;
    for (i = 0; i < pool->m_threadNum; i++)
    {
        pthread_join(pool->m_pthreadIDs[i], NULL);
    }
    pthread_mutex_destroy(&pool->mutex);
    pthread_cond_destroy(&pool->m_QueueEmpty);
    pthread_cond_destroy(&pool->m_QueueNotEmpty);
    pthread_cond_destroy(&pool->m_QueueNotFull);
    free(pool->m_pthreadIDs);
    struct job *tmp;
    while (pool->head != NULL)
    {
        tmp = pool->head;
        pool->head = pool->head->next;
        free(tmp);
    }
    free(pool);
}

//任务工作函数
void *work(void *arg)
{   
    Myarg *workarg = (Myarg *)arg;
    printf("work: %d\n", workarg->fd);
    printf("msg: %s\n",workarg->msg.msg);
    printf("msg: %d\n",workarg->msg.flag);


    switch(workarg->msg.flag)
    {
        case REGISTER:
            Register(workarg,&workarg->msg);//注册用户
            printf(" 注册用户\n");
            break;
        case LOGIN:
            login(workarg,&workarg->msg);//登录
            printf("用户登录功能\n");
            break;

        case PRIVATE:
            privatechat(workarg,&workarg->msg); //私聊
            printf("私聊功能\n");
            break;

        case CHPASSWORD:
        changepassword(workarg,&workarg->msg); //修改密码
        printf("修改密码\n");
        break;

        case GROUPCHAT:
        groupchat(workarg,&workarg->msg);//进行群聊
        break;

        case VIEWUSER:   //查看所有在线用户
        viewonline_user(workarg,&workarg->msg);
        break;

        case LOGUSER://注销用户
        loguser(workarg,&workarg->msg);
        break;

        case CHECKRECORD:
        Checkrecord(workarg);//查看聊天记录
        break;

        case FILETRAN:
        filetransfer(workarg,&workarg->msg); //文件传输
        break;

        case APPLYROOT:
        applyroot(workarg,&workarg->msg); //申请管理员用户
        break;

        case ROOTLOGIN:
        rootlogin(workarg,&workarg->msg); //管理员登录
        break;

        case ROOTKICK:
        rootkick(workarg,&workarg->msg);  //管理员踢人
        break;

        case SILENT:
        silent(workarg,&workarg->msg); //管理员禁言
        break;

        case REMSILENT:
        remsilent(workarg,&workarg->msg); //管理员解除用户禁言
        break;

        case CANCELROOT:
        cancelroot(workarg,&workarg->msg); //取消管理员身份
        break;
        default:

        break;
    }
free(arg);
}  


//在线用户链表
online_userlist* online_userlistcreate()
{
    online_userlist *head = (online_userlist *)malloc(sizeof(online_userlist));
    head->next = NULL;
    return head;
}



//注册部分
void Register(Myarg *node,clientlist *c)
{
    printf("判断:%s 是否在数据库\n",c->name);
    int ret = searchtable(c->name); //在数据库中查找是否已经注册过
    printf("%d",ret);
    if(1 == ret)
    {
        char arr[128] = {"账号已经存在,请重新注册"};
        if(send(node->fd,arr,sizeof(arr),0) == -1)
        {
            ERRLOG("send error");
        }
        return;
    }
    else
    {
        insert_updata(node,c);  //向数据库中插入数据
    }
}
//向数据库中插入注册数据
void insert_updata(Myarg *node,clientlist *c)
{   

    printf("注册的名字%s\n",c->name);
    char *errmsg;
    sqlite3 *db =NULL;
    char sql[200] = "\0";
    memset(sql,0,sizeof(sql));//清空sql
    sprintf(sql,"insert into user(name,password) values('%s','%s')",c->name,c->password);
    printf("数据库sql语句测试\n");
    execstate(sql);
    printf("\033[0;34m插入成功\033\n");
    char buf[100] = {"[系统日志]:注册成功...\n"};
    if(send(node->fd,buf,sizeof(buf),0) == -1)
    {
        ERRLOG("send error");
    }
}
//在数据库查询用户 c存在返回1  不存在返回0
int searchtable(char *tablename)
{   

    int flag;
    sqlite3 *db = NULL;      
    int ret = sqlite3_open("user.db",&db);
    if(SQLITE_OK != ret)
    {
        perror("sqlite3_open");
        exit(1);
    }

    char sql[200] = "\0";
    sprintf(sql,"select *from user;");
    char *errmsg;//定义指着指向错误信息
    char **result;//相当于二维数组  保存查询到的信息
    int nrow,ncolumn;//分别代表行和列

    ret = sqlite3_get_table(db,sql,&result,&nrow,&ncolumn,&errmsg);
    int j,i;
    int Index = ncolumn;

    for(i = 1 ; i < nrow+1;i++)
    {
        int ret = strcmp(result[i*ncolumn + 0],tablename);
        if(ret == 0)
        {   
            flag = 1;
            
        }//0行就是第一行  第i行第j列
    }

    if(flag == 1)
    {   printf("数据库是否存在该用户%d存在\n",flag);

        sqlite3_free_table(result);//释放数据库
        ret = sqlite3_close(db);
        if(SQLITE_OK != ret)
        {
            perror("sqlite3_close:");
            exit(1);
        }
        return flag;
    }
    else
    {   
        flag = 0;
        printf("%d\n",flag);
        sqlite3_free_table(result);//释放数据库
        ret = sqlite3_close(db);
        if(SQLITE_OK != ret)
        {
            perror("sqlite3_close:");
            exit(1);
        }
        return flag;
    }
}
//执行sql语句函数
void execstate(char *sql)//执行SQL语句
{
    sqlite3 *db =NULL;
    int ret = sqlite3_open("user.db",&db);
    if(SQLITE_OK != ret)
    {
        ERRLOG("sqlite3_open");
    }
    char *errmsg;
    ret = sqlite3_exec(db,sql,NULL,NULL,&errmsg);
    if(SQLITE_OK != ret)
    {
        ERRLOG("sqlite3_exec");
    }
    ret = sqlite3_close(db);
    if(SQLITE_OK != ret)
    {
        perror("sqlite_close:");
        exit(1);
    }
}


//登录部分
void login(Myarg *node,clientlist *c)
{
    //判断是否注册
    if(0 == (searchtable(c->name)))
    {
        char buf[32] = "该用户不存在,请注册...";
        if(send(node->fd,buf,sizeof(buf),0) == -1)
        {
            ERRLOG("send error");
        }
        
    }
    else if( 0 == (veridandpassword(c)))//密码错误
    {

        char buf[20] = "密码输入错误..";
        if(send(node->fd,buf,sizeof(buf),0) == -1)
        {
            ERRLOG("send error");
        }
    }
    else if(1 == (veridandpassword(c)))//返回值为1账号密码正确
    {   
        if(1 ==Repeatlogin(node,c))//判断重复登录
        {
            online_insertion(node,c);
            char buf[32] = "登录成功..";
            if(send(node->fd,buf,sizeof(buf),0) == -1)
            {
                ERRLOG("send error");
            }

            online_userlist *p = head;
            p=p->next;
            while(p!=NULL)//如果用p->next!=NULL  当p在最后一个结点是 p->next=NULL 无法进入循环
            {   
                if(p->c_fd != 0)
                {   
                    char buf[50] = "\0";
                    sprintf(buf,"[%s]:上线了",c->name);
                    if(send(p->c_fd,buf,sizeof(buf),0) == -1)
                    {
                        ERRLOG("send error");
                    }
                    memset(buf,0,sizeof(buf));
                }
                p=p->next;
            }
            sleep(2);
            sendOfflinemessage(node,c);
        }
        else
        {
            return;
        }
    }
}
//重复登录判断
int Repeatlogin(Myarg *node,clientlist *c)
{   
    printf("正在判断是否重复登录\n");
    char s1[32]={0};
    strcpy(s1,c->name);//判断自己是否登录
    printf("正在判断%s是否在线\n",c->to_name);
    online_userlist *p = head;
    while(p!=NULL)
    {   
        if(strcmp(s1,p->name) == 0 ) 
        {   printf("%s在线,重复登录\n",p->name);
            char buf[32] = "您已在线,请勿重复登录";
            if(send(node->fd,buf,sizeof(buf),0) == -1)
            {
                ERRLOG("send error");
            }
            return 0;
        }
        p=p->next;
    }
    printf("未遍历到该用户,可以进行登录\n");
    return 1;
}
//登录插入链表
void online_insertion(Myarg *node,clientlist *c)
{
    online_userlist *tmp=(online_userlist *)malloc(sizeof(online_userlist));
    tmp->next=NULL;
    online_userlist *p =head;//定义一个指针为表头

    strcpy(tmp->name,c->name);
    tmp->c_fd = node->fd;
    
    printf("判断:%s,%d是否进行插入链表\n",tmp->name,tmp->c_fd);
    if(1==onlineempty())
    {printf("1");
        tmp->next=p->next;//令新插入的结点指针指向下一个结点
        p->next=tmp;
    }
    else
    {printf("2");
        while(p->next!=NULL&&strncmp(p->next->name , tmp->name,1)<0)//当目的name小于源name
        {
            p=p->next;
        }
        //当p->next指向的数据大于或者等于需要插入的数据是跳出循环
        //此事p->next  向的是第一个大于value的值
        tmp->next=p->next;
        p->next=tmp;  //然后将其插入大于value的值之前
        putchar(10);
    }
    printf("链表插入完成\n");
}


//验证账户与密码 正确返回1 错误返回0;
int veridandpassword(clientlist *p)
{   
    printf("已进入密码效验工作\n");
    int flag;
    sqlite3 *db = NULL;      //数据库的名字记得更换
    int ret = sqlite3_open("user.db",&db);
    if(SQLITE_OK != ret)
    {
        perror("sqlite3_open");
        exit(1);
    }
    printf("已打开数据库\n");
    char sql[200] = "\0";
    sprintf(sql,"select *from user;");
    char *errmsg;//定义指着指向错误信息
    char **result;//相当于二维数组  保存查询到的信息
    int nrow,ncolumn;//分别代表行和列
    ret = sqlite3_get_table(db,sql,&result,&nrow,&ncolumn,&errmsg);
    int j,i;
    int Index = ncolumn;
    int ret1,ret2;
    printf("已进行遍历数据库\n");
    for(i = 0 ; i < nrow+1;i++)
    {

        ret1 = (strcmp(result[i*ncolumn + 0],p->name));
        ret2 = (strcmp(result[i*ncolumn + 1],p->password));
        printf("正在进行账户密码效验\n");
        printf("账户:%s\n",result[i*ncolumn + 0]);
        printf("密码:%s\n",result[i*ncolumn + 1]);
        printf("密码:%s\n",p->password);
        if(0 == ret1 && 0 == ret2)
        {
                flag = 1;
                break;
        }//0行就是第一行  第i行第j列
    }
    if(flag == 1)
    {   printf("核对返回值为%d\n",flag);
        sqlite3_free_table(result);//释放数据库
        ret = sqlite3_close(db);
        if(SQLITE_OK != ret)
        {
            perror("sqlite3_close:");
            exit(1);
        }
        return flag;
    }
    else{
            flag = 0;
            sqlite3_free_table(result);//释放数据库
            ret = sqlite3_close(db);
            if(SQLITE_OK != ret)
            {
                perror("sqlite3_close:");
                exit(1);
            }
            return flag;
        }
}
//判断链表是否为空
int onlineempty()
{
    if (head->next==NULL)
    {
        return  1 ;
    }
    else 
    {printf("kong3\n");
        return 0;
    }

}
//判断除自己是否还有别人在线
int onlineempty1()
{
    if (head->next->next==NULL)
    {
        return  1 ;
    }
    else 
    {
        return 0;
    }
}



//私聊部分
void privatechat(Myarg *node,clientlist *c)
{   
    int flag = 0;
    printf("正在运行私聊函数\n");
    int to_cfd = Find_person(c);  //在线返回对方的文件描述符
    printf("私聊对象的文件描述符为%d\n",to_cfd);
    if(1 == sensitiveword(c->msg))
    {
        printf("该消息存在敏感词\n");
        char buf2[128] = "\0";
        sprintf(buf2,"[系统日志]:你的消息中存在敏感词汇,无法发送");
        if(send(node->fd,buf2,sizeof(buf2),0) == -1)
        {
            ERRLOG("send error");
        }
        return;
    }
    if(0 == to_cfd)
    {
        saveOfflinemessage(c);
        char buf[32] = "对方不在线...";
        if(send(node->fd,buf,sizeof(buf),0) == -1)
        {
            ERRLOG("send error");
        }
    }
    else
    {   
        if(0 == flag)
        {
            char buf1[128] = "\0";
            sprintf(buf1,"[系统日志]:正在与%s进行私聊",c->to_name);
            if(send(node->fd,buf1,sizeof(buf1),0) == -1)
            {
                ERRLOG("send error");
            }
            flag++;
        }
        printf("正在进行消息:%s转发\n",c->msg);
        char buf[128] = "\0";
        sprintf(buf,"[%s悄悄对你说了一句]:%s",c->name,c->msg);
        if(send(to_cfd,buf,sizeof(buf),0) == -1)
        {
            ERRLOG("send error");
        }
    }
    
}

//判断是否在线 在线返回对方文件描述符 不在线返回0
int Find_person(clientlist *c)
{
    char s1[32]={0};
    strcpy(s1,c->to_name);//想跟s1进行私聊
    printf("正在判断%s是否在线\n",c->to_name);
    online_userlist *p = head;
    while(p!=NULL)
    {   
        if(strcmp(s1,p->name) == 0 ) 
        {   printf("%s在线\n",p->name);
            return p->c_fd;//私聊对象的文件描述符
        }
        p=p->next;
    }
    return 0;
}

//判断是否为敏感词  存在返回1 不存在返回0
int sensitiveword(char *sensitive)
{
    printf("正在进行敏感词判断\n");
    int flag;
    sqlite3 *db = NULL;      
    int ret = sqlite3_open("user.db",&db);
    if(SQLITE_OK != ret)
    {
        perror("sqlite3_open");
        exit(1);
    }

    char sql[200] = "\0";
    sprintf(sql,"select *from sensitive;");
    char *errmsg;//定义指着指向错误信息
    char **result;//相当于二维数组  保存查询到的信息
    int nrow,ncolumn;//分别代表行和列

    ret = sqlite3_get_table(db,sql,&result,&nrow,&ncolumn,&errmsg);
    int j,i;
    int Index = ncolumn;

    for(i = 1 ; i <nrow+1;i++)
    {
        printf("数据库遍历结果%s\n",result[i*ncolumn + 0]);
        int ret = strcmp(result[i*ncolumn + 0],sensitive);
        if(ret == 0)
        {   
            flag = 1;
            break;
        }//0行就是第一行  第i行第j列
    }
    if(flag == 1)
    {   printf("数据库是否存在该用户%d存在\n",flag);

        sqlite3_free_table(result);//释放数据库
        ret = sqlite3_close(db);
        if(SQLITE_OK != ret)
        {
            perror("sqlite3_close:");
            exit(1);
        }
        return flag;
    }
    else
    {   
        flag = 0;
        printf("数据库遍历返回值%d\n",flag);
        sqlite3_free_table(result);//释放数据库
        ret = sqlite3_close(db);
        if(SQLITE_OK != ret)
        {
            perror("sqlite3_close:");
            exit(1);
        }
        return flag;
    }
}

//离线消息保存至表中
void saveOfflinemessage(clientlist *c)
{
    time_t timep;
    struct tm *p;
    time (&timep);
    p=gmtime(&timep);
    char newtime[32] = {0};
    memset(newtime,0,32);
    sprintf(newtime,"%d-%d-%d:%d:%d\n",1+p->tm_mon,p->tm_mday,8+p->tm_hour,p->tm_min,p->tm_sec);
    printf("获取时间%s\n",newtime);

    char *errmsg;
    sqlite3 *db =NULL;
    char sql[200] = "\0";
    memset(sql,0,sizeof(sql));//清空sql
    sprintf(sql,"insert into message(name,toname,message,time) values('%s','%s','%s','%s')",c->name,c->to_name,c->msg,newtime);
    printf("数据库sql语句测试\n");
    execstate(sql);
    printf("\033[0;34m插入成功\033\n");
}



//修改密码部分
void changepassword(Myarg *node,clientlist *c)
{
    
    printf("用户%s将吧密码修改为%s",c->name,c->msg);
    Insert_updated_data(c->name,c->msg);
    char buf1[600] = "\0";

    sprintf(buf1,"[系统日志]:密码修改成功");
    if(send(node->fd,buf1,sizeof(buf1),0) == -1)
    {
        ERRLOG("send error");
    }
    printf("用户%s已成功修改密码",c->name);
    exituser(c);
}
//根据名字修改密码
void Insert_updated_data(char *name,char *password)
{
    char sql[500] = "\0";
    sprintf(sql,"update user set password = '%s' where name = '%s';",password,name);
    execstate(sql);
    
}
//退出群聊 删除链表内的用户结点
void exituser(clientlist *c)
{
    online_userlist *p = head;
    online_userlist *q;
    while(p->next!=NULL)
    {
        if(strcmp(c->name,p->next->name)==0)
        {
            q=p->next;
            p->next=p->next->next;
            free(q);
            q=NULL;
            printf("将%s踢出链表成功\n",c->name);
            return;
        }
        p=p->next;
    }
}



//群聊部分  
void groupchat(Myarg *node,clientlist *c)
{
    printf("正在运行管理员群聊函数\n");
    online_userlist *p = head;
    int ret;
    ret = onlineempty1(p);
    if(1 == ret)
    {
            char buf1[128] = "\0";
            strcpy(buf1,"[系统日志]:当前没有别的用户在线");
            if(send(node->fd,buf1,sizeof(buf1),0) == -1)
            {
                ERRLOG("send error");
            }
        return;
    }
    printf("正在遍历链表给每个用户发送消息\n");
    printf("打印管理员标志%d\n",c->root);
    if(1 == sensitiveword(c->msg))
    {
        printf("该消息存在敏感词%s\n",c->msg);
        char buf2[128] = "\0";
        sprintf(buf2,"[系统日志]:你的消息中存在敏感词汇,无法发送");
        if(send(node->fd,buf2,sizeof(buf2),0) == -1)
        {
            ERRLOG("send error");
        }
        return;
    }

    if(c->root == 1)
    {   
        getheadflag(c);//获取管理员头像
        printf("打印管理员头像%s\n",c->emojio);
        p=p->next;
        while(p!=NULL)//如果用p->next!=NULL  当p在最后一个结点是 p->next=NULL 无法进入循环
        {   
            if(strcmp(p->name,c->name) != 0)
            {   printf("打印文件描述符%d \n",p->c_fd);
                char buf[128] = "\0";
                sprintf(buf,"%s[%s]:%s",c->emojio,c->name,c->msg);
                if(send(p->c_fd,buf,sizeof(buf),0) == -1)
                {
                    ERRLOG("send error");
                }
                memset(buf,0,sizeof(buf));
            }
            p=p->next;
        }
    }
    else
    {
        p=p->next;
        while(p!=NULL)//如果用p->next!=NULL  当p在最后一个结点是 p->next=NULL 无法进入循环
        {   
            if(strcmp(p->name,c->name) != 0)
            {   printf("打印文件描述符%d \n",p->c_fd);
                char buf[100] = "\0";
                sprintf(buf,"[%s]:%s",c->name,c->msg);
                if(send(p->c_fd,buf,sizeof(buf),0) == -1)
                {
                    ERRLOG("send error");
                }
                memset(buf,0,sizeof(buf));
            }
            p=p->next;
        }
    }
}
//获取管理员头像
void getheadflag(clientlist *c)
{
    int flag;
    sqlite3 *db = NULL;      
    int ret = sqlite3_open("user.db",&db);
    if(SQLITE_OK != ret)
    {
        perror("sqlite3_open");
        exit(1);
    }

    char sql[200] = "\0";
    sprintf(sql,"select *from root;");
    char *errmsg;//定义指着指向错误信息
    char **result;//相当于二维数组  保存查询到的信息
    int nrow,ncolumn;//分别代表行和列

    ret = sqlite3_get_table(db,sql,&result,&nrow,&ncolumn,&errmsg);
    int j,i;
    int Index = ncolumn;

    for(i = 1 ; i < nrow+1;i++)
    {
        int ret = strcmp(result[i*ncolumn + 1],c->name);
        if(ret == 0)
        {   strcpy(c->emojio,result[i*ncolumn + 0]);
            flag = 1;
            printf("当前管理员头像%s\n",result[i*ncolumn + 0]);
            printf("c->emojio=%s\n",c->emojio);
        }//0行就是第一行  第i行第j列
    }

    if(flag == 1)
    {   printf("管理员头像获取成功%s存在\n",c->emojio);

        sqlite3_free_table(result);//释放数据库
        ret = sqlite3_close(db);
        if(SQLITE_OK != ret)
        {
            perror("sqlite3_close:");
            exit(1);
        }
        return;
    }
    else
    {   
        printf("管理员头像获取失败%d不存在\n",flag);
        flag = 0;
        printf("%d\n",flag);
        sqlite3_free_table(result);//释放数据库
        ret = sqlite3_close(db);
        if(SQLITE_OK != ret)
        {
            perror("sqlite3_close:");
            exit(1);
        }
        return;
    }
}


//查看所有在线用户
void viewonline_user(Myarg *node,clientlist *c)
{

    online_userlist *p = head->next;
    int ret;
    ret = onlineempty(p);
    if(1 == ret)
    {
            char buf1[128] = "\0";
            strcpy(buf1,"[系统日志]:当前没有别的用户在线");
            if(send(node->fd,buf1,sizeof(buf1),0) == -1)
            {
                ERRLOG("send error");
            }
        return;
    }
    while(p!=NULL)//如果用p->next!=NULL  当p在最后一个结点是 p->next=NULL 无法进入循环
    {   
        if(p->c_fd != 0)
        {   
            char buf[70] = {0};
            memset(buf,0,sizeof(buf));
            printf("%s当前在线3\n",p->name);
            sprintf(buf,"[%s]:当前在线",p->name);
            if(send(node->fd,buf,sizeof(buf),0) == -1)
            {
                ERRLOG("send error");
            }
            sleep(1);
        }
        p=p->next;
    }
}


//注销用户
void loguser(Myarg *node,clientlist *c)
{
    deleteuser(c);
    if(c->root == 1)
    {
        deleteroot(c);
    }
    printf("注销用户删除数据库成功\n");
    char buf[32] = "注销成功..";
    if(send(node->fd,buf,sizeof(buf),0) == -1)
    {
        ERRLOG("send error");
    }
    exituser(c);
    printf("注销用户删除链表结点成功\n");
}
//指定删除数据库里面的用户
void deleteuser(clientlist *c)
{   

    char sql[200] = "\0";
    memset(sql,0,sizeof(sql));//清空sql
    sprintf(sql,"delete from user where name = '%s';",c->name);
    execstate(sql);
    printf("删除数据库内容成功\n");
    return;
}
//指定删除数据库里面的root用户
void deleteroot(clientlist *c)
{   

    char sql[200] = "\0";
    memset(sql,0,sizeof(sql));//清空sql
    sprintf(sql,"delete from root where name = '%s';",c->name);
    execstate(sql);
    printf("删除root内容成功\n");
    return;
}





//查看聊天记录部分
//将聊天记录插入记录表(保存聊天记录)
void insert_uprecord(clientlist *c)
{   
    printf("正在进行聊天记录存储\n");
    printf("聊天方式标志位:%d",c->flag);
    printf("聊天内容:%s",c->msg);
    if(1 == sensitiveword(c->msg))
    {
        return;
    }

    time_t timep;
    struct tm *p;
    time (&timep);
    p=gmtime(&timep);
    char newtime[32] = {0};
    sprintf(newtime,"%d-%d-%d:%d:%d\n",1+p->tm_mon,p->tm_mday,8+p->tm_hour,p->tm_min,p->tm_sec);
    printf("获取时间%s\n",newtime);
    char *errmsg;
    sqlite3 *db =NULL;
    char sql[200] = "\0";
    memset(sql,0,sizeof(sql));//清空sql

    pthread_mutex_lock(&mutex);
    sprintf(sql,"insert into record(time,name,record) values('%s','%s','%s')",newtime,c->name,c->msg);
    printf("数据库sql语句测试\n");
    execstate(sql);
    pthread_mutex_unlock(&mutex);
    printf("\033[0;34m聊天记录保存成功\033\n");
}
//查看聊天记录
void Checkrecord(Myarg *node)
{
    printf("已进入聊天记录返回客户端工作\n");
    int flag;
    sqlite3 *db = NULL;      
    int ret = sqlite3_open("user.db",&db);
    if(SQLITE_OK != ret)
    {
        perror("sqlite3_open");
        exit(1);
    }
    printf("已打开数据库\n");
    char sql[200] = "\0";
    sprintf(sql,"select *from record;");
    char *errmsg;//定义指着指向错误信息
    char **result;//相当于二维数组  保存查询到的信息
    int nrow,ncolumn;//分别代表行和列
    ret = sqlite3_get_table(db,sql,&result,&nrow,&ncolumn,&errmsg);
    int j,i;
    int Index = ncolumn;
    int ret1,ret2;
    printf("已进行遍历数据库\n");
    char buf[256] = {0};
    for(i = 1 ; i < nrow+1;i++)
    {
        printf("正在将聊天记录发送给客户端\n");
        sprintf(buf,"%s\n[%s]:%s",result[i*ncolumn + 0],result[i*ncolumn + 1],result[i*ncolumn + 2]);
        printf("时间:%s\n",result[i*ncolumn + 0]);
        printf("用户:%s\n",result[i*ncolumn + 1]);
        printf("消息内容:%s\n",result[i*ncolumn + 2]);
        printf("%s",buf);
        if(send(node->fd,buf,sizeof(buf),0) == -1)
        {
            ERRLOG("send error");
        }
    }
}



//管理员申请部分
//管理员用户的申请
void applyroot(Myarg *node,clientlist *c)
{
    printf("正在判断:%s 是否已经是管理员\n",c->name);
    int ret = searchtableroot(c->name);
    printf("%d",ret);
    if(1 == ret)
    {
        char arr[128] = {"您已是管理员.."};
        if(send(node->fd,arr,sizeof(arr),0) == -1)
        {
            ERRLOG("send error");
        }
        return;
    }
    else
    {
        insert_updataroot(node,c);
    }
}
//在数据库中管理员表中查看是否存在该管理员
int searchtableroot(char *tablename)
{   

    int flag;
    sqlite3 *db = NULL;      
    int ret = sqlite3_open("user.db",&db);
    if(SQLITE_OK != ret)
    {
        perror("sqlite3_open");
        exit(1);
    }

    char sql[200] = "\0";
    sprintf(sql,"select *from root;");
    char *errmsg;//定义指着指向错误信息
    char **result;//相当于二维数组  保存查询到的信息
    int nrow,ncolumn;//分别代表行和列

    ret = sqlite3_get_table(db,sql,&result,&nrow,&ncolumn,&errmsg);
    int j,i;
    int Index = ncolumn;

    for(i = 1 ; i < nrow+1;i++)
    {
        int ret = strcmp(result[i*ncolumn + 1],tablename);
        if(ret == 0)
        {   
            flag = 1;
            
        }//0行就是第一行  第i行第j列
    }

    if(flag == 1)
    {   printf("root表中该用户%d存在\n",flag);

        sqlite3_free_table(result);//释放数据库
        ret = sqlite3_close(db);
        if(SQLITE_OK != ret)
        {
            perror("sqlite3_close:");
            exit(1);
        }
        return flag;
    }
    else
    {   
        printf("root表中该用户%d不存在\n",flag);
        flag = 0;
        printf("%d\n",flag);
        sqlite3_free_table(result);//释放数据库
        ret = sqlite3_close(db);
        if(SQLITE_OK != ret)
        {
            perror("sqlite3_close:");
            exit(1);
        }
        return flag;
    }
}
//向root表中插入数据
void insert_updataroot(Myarg *node,clientlist *c)
{
        printf("申请管理员的用户%s\n",c->name);
    char *errmsg;
    sqlite3 *db =NULL;
    char sql[200] = "\0";
    memset(sql,0,sizeof(sql));//清空sql

    pthread_mutex_lock(&mutex);
    sprintf(sql,"insert into root(head,name,password) values('%s','%s','%s');",c->emojio,c->name,c->password);
    printf("将 头像:%s,名字;%s,密码:%s 插入表中\n",c->emojio,c->name,c->password);
    execstate(sql);
    pthread_mutex_unlock(&mutex);


    printf("\033[0;34m插入成功\033\n");
    char buf[100] = {"管理员申请成功"};
    if(send(node->fd,buf,sizeof(buf),0) == -1)
    {
        ERRLOG("send error");
    }
    exituser(c);//注册完将其踢出链表让其进入初始界面从新登录
    return;
}



//管理员登录部分
//管理员登录
void rootlogin(Myarg *node,clientlist *c)
{
    if(0 == (searchtableroot(c->name)))
    {
        char buf[32] = "该用户非管理员";
        if(send(node->fd,buf,sizeof(buf),0) == -1)
        {
            ERRLOG("send error");
        }
        
    }
    else if( 0 == (rootveridandpassword(c)))//密码错误
    {
        char buf[20] = "密码输入错误";
        if(send(node->fd,buf,sizeof(buf),0) == -1)
        {
            ERRLOG("send error");
        }
    }
    else if(1 == (rootveridandpassword(c)))//返回值为1账号密码正确
    {  
        if(rootRepeatlogin(node,c))
        {
            online_insertion(node,c);
            char buf[32] = "管理员登录成功..";
            if(send(node->fd,buf,sizeof(buf),0) == -1)
            {
                ERRLOG("send error");
            }
            online_userlist *p = head;
            p=p->next;
            while(p!=NULL)//如果用p->next!=NULL  当p在最后一个结点是 p->next=NULL 无法进入循环
            {   
                if(p->c_fd != 0)
                {   
                    char buf[70] = "\0";
                    sprintf(buf,"%s[%s]:上线了",c->emojio,c->name);
                    if(send(p->c_fd,buf,sizeof(buf),0) == -1)
                    {
                        ERRLOG("send error");
                    }
                    memset(buf,0,sizeof(buf));
                }
                p=p->next;
            }
        }
        else
        {
            return;
        }
    }
}
//判断管理员用户与密码
int rootveridandpassword(clientlist *c)
{   
    printf("已进入管理员的密码效验工作\n");
    int flag;
    sqlite3 *db = NULL;      //数据库的名字记得更换
    int ret = sqlite3_open("user.db",&db);
    if(SQLITE_OK != ret)
    {
        perror("sqlite3_open");
        exit(1);
    }
    printf("已打开数据库\n");
    char sql[200] = "\0";
    sprintf(sql,"select *from root;");
    char *errmsg;//定义指着指向错误信息
    char **result;//相当于二维数组  保存查询到的信息
    int nrow,ncolumn;//分别代表行和列
    ret = sqlite3_get_table(db,sql,&result,&nrow,&ncolumn,&errmsg);
    int j,i;
    int Index = ncolumn;
    int ret1,ret2;
    printf("已进行遍历数据库\n");
    for(i = 0 ; i < nrow+1;i++)
    {
        ret1 = (strcmp(result[i*ncolumn + 1],c->name));
        ret2 = (strcmp(result[i*ncolumn + 2],c->password));
        printf("正在进行账户密码效验\n");
        
        printf("账户:%s\n",result[i*ncolumn + 1]);
        printf("密码:%s\n",result[i*ncolumn + 2]);
        printf("密码:%s\n",c->password);
        if(0 == ret1 && 0 == ret2)
        {
                strcpy(c->emojio,result[i*ncolumn + 0]);
                printf("打印管理员存在数据库中的头像%s",result[i*ncolumn + 0]);
                printf("打印被赋值后的当前结构体头像%s",c->emojio);
                flag = 1;
                break;
        }//0行就是第一行  第i行第j列
    }
    if(flag == 1)
    {   printf("核对返回值为%d\n",flag);
        sqlite3_free_table(result);//释放数据库
        ret = sqlite3_close(db);
        if(SQLITE_OK != ret)
        {
            perror("sqlite3_close:");
            exit(1);
        }
        return flag;
    }
    else{
            flag = 0;
            sqlite3_free_table(result);//释放数据库
            ret = sqlite3_close(db);
            if(SQLITE_OK != ret)
            {
                perror("sqlite3_close:");
                exit(1);
            }
            return flag;
        }
}
//管理员重复登录判断
int rootRepeatlogin(Myarg *node,clientlist *c)
{   
    printf("正在判断是否重复登录\n");
    char s1[32]={0};
    strcpy(s1,c->name);//判断自己是否登录
    printf("正在判断%s是否在线\n",c->to_name);
    online_userlist *p = head;
    while(p!=NULL)
    {   
        if(strcmp(s1,p->name) == 0 ) 
        {   printf("%s在线,重复登录\n",p->name);
            char buf[32] = "您已在线,请勿重复登录";
            if(send(node->fd,buf,sizeof(buf),0) == -1)
            {
                ERRLOG("send error");
            }
            return 0;
        }
        p=p->next;
    }
    printf("未遍历到该用户,可以进行登录\n");
    return 1;
}


//文件传输部分
//文件传输
void filetransfer(Myarg *node,clientlist *c)
{
    MSG msg;
    printf("正在运行文件传输函数\n");
    int to_cfd = Find_person(c);  //在线返回对方的文件描述符
    printf("私聊对象的文件描述符为%d\n",to_cfd);

    char buf[256] = "\0";
    printf("***********11111\n");
    printf("%s",c->buf);
    printf("***********22222\n");

    strcpy(msg.text,c->buf);
    msg.num = c->num;
    if(0 == to_cfd)
    {
        char buf1[32] = "对方不在线...";
        if(send(node->fd,buf1,sizeof(buf),0) == -1)
        {
            ERRLOG("send error");
        }
        sleep(2);
    }
    else
    {   
        while(flag2)
        {   
            printf("正在向对方发送文件传输信号\n");
            if(send(to_cfd,"文件存在进行文件传输",M,0) == -1)
            {
                ERRLOG("send error");
            }
            flag2 = 0;
        }
        if(c->num == 0)
        {   
            printf("文件内容全部发送完成\n");
            flag2 = 1;
        }
        sleep(1);
        printf("本次向对方送的内容%s\n",msg.text);
        if(send(to_cfd,&msg,sizeof(msg),0) == -1)
        {
            ERRLOG("send error");
        }
        printf("单次发送内容完成\n");
        memset(&msg,0,sizeof(msg));
    }
    return;
}




//管理员功能部分
//管理员踢人
void rootkick(Myarg *node,clientlist *c)
{
    int to_cfd = Find_person(c);  //在线返回对方的文件描述符
    printf("踢出对象的文件描述符为%d\n",to_cfd);
    if(0 == to_cfd)
    {
        char buf[32] = "对方不在线...";
        if(send(node->fd,buf,sizeof(buf),0) == -1)
        {
            ERRLOG("send error");
        }
    }
    else
    {   
        if(searchtableroot(c->to_name) == 1)
        {
            char buf[128] = "对方是管理员,无法进行操作";
            if(send(node->fd,buf,sizeof(buf),0) == -1)
            {
                ERRLOG("send error");
            }
            return;
        }
        else
        {
            char buf[128] = "管理员已将你踢出聊天室";
            if(send(to_cfd,buf,sizeof(buf),0) == -1)
            {
                ERRLOG("send error");
            }
        }
    }
}
//管理员禁言
void silent(Myarg *node,clientlist *c)
{
    int to_cfd = Find_person(c);  //在线返回对方的文件描述符
    printf("禁言对象的文件描述符为%d\n",to_cfd);
    if(0 == to_cfd)
    {
        char buf[32] = "对方不在线...";
        if(send(node->fd,buf,sizeof(buf),0) == -1)
        {
            ERRLOG("send error");
        }
    }
    else
    {
        if(searchtableroot(c->to_name) == 1)
        {
            char buf[128] = "对方是管理员,无法进行操作";
            if(send(node->fd,buf,sizeof(buf),0) == -1)
            {
                ERRLOG("send error");
            }
            return;
        }
        else
        {
            char buf[128] = "管理员已将你禁言";
            if(send(to_cfd,buf,sizeof(buf),0) == -1)
            {
                ERRLOG("send error");
            }
        }
    }
}
//管理员解除禁言
void remsilent(Myarg *node,clientlist *c)
{
    int to_cfd = Find_person(c);  //在线返回对方的文件描述符
    printf("解除禁言对象的文件描述符为%d\n",to_cfd);
    if(0 == to_cfd)
    {
        char buf[32] = "对方不在线...";
        if(send(node->fd,buf,sizeof(buf),0) == -1)
        {
            ERRLOG("send error");
        }
    }
    else
    {
        char buf[128] = "管理员已将你解除禁言";
        if(send(to_cfd,buf,sizeof(buf),0) == -1)
        {
            ERRLOG("send error");
        }
    }
}
//撤销管理员身份
void cancelroot(Myarg *node,clientlist *c)// 功能标志15 撤销管理员身份
{
    char buf[128] = "撤销成功";
    if(send(node->fd,buf,sizeof(buf),0) == -1)
    {
        ERRLOG("send error");
    }
    deleteroot(c);
    exituser(c);
    return;
}




//离线消息的保存与发送
//离线消息发送
void sendOfflinemessage(Myarg *node,clientlist *c)
{
    if(searchtuser(c->name) == 1)
    {
        sqlite3 *db = NULL;      
        int ret = sqlite3_open("user.db",&db);
        if(SQLITE_OK != ret)
        {
            perror("sqlite3_open");
            exit(1);
        }
        char buf[128] = {0};
        char sql[200] = "\0";
        memset(buf,0,128);
        memset(sql,0,200);
        sprintf(sql,"select *from message;");
        char *errmsg;//定义指着指向错误信息
        char **result;//相当于二维数组  保存查询到的信息
        int nrow,ncolumn;//分别代表行和列
        ret = sqlite3_get_table(db,sql,&result,&nrow,&ncolumn,&errmsg);
        int j,i;
        int Index = ncolumn;
        for(i = 1 ; i < nrow+1;i++)
        {
            int ret = strcmp(result[i*ncolumn + 1],c->name);
            if(ret == 0)
            {   
                printf("正在将离线消息发送给客户端\n");
                sprintf(buf,"%s\n[%s对你说了一句]:%s",result[i*ncolumn + 3],result[i*ncolumn + 0],result[i*ncolumn + 2]);
                printf("时间:%s\n",result[i*ncolumn + 0]);
                printf("用户:%s\n",result[i*ncolumn + 1]);
                printf("消息内容:%s\n",result[i*ncolumn + 2]);
                printf("%s",buf);
                if(send(node->fd,buf,sizeof(buf),0) == -1)
                {
                    ERRLOG("send error");
                }
                
            }//0行就是第一行  第i行第j列
        }
        deleteumessage(c);
            sqlite3_free_table(result);//释放数据库
            ret = sqlite3_close(db);
            if(SQLITE_OK != ret)
            {
                perror("sqlite3_close:");
                exit(1);
            }
    }
    else
    {
        return;
    }
}
//在数据库中查找是否有该用户的离线消息 存在返回1 不存在返回0
int searchtuser(char *username)
{   

    int flag;
    sqlite3 *db = NULL;      
    int ret = sqlite3_open("user.db",&db);
    if(SQLITE_OK != ret)
    {
        perror("sqlite3_open");
        exit(1);
    }

    char sql[200] = "\0";
    sprintf(sql,"select *from message;");
    char *errmsg;//定义指着指向错误信息
    char **result;//相当于二维数组  保存查询到的信息
    int nrow,ncolumn;//分别代表行和列

    ret = sqlite3_get_table(db,sql,&result,&nrow,&ncolumn,&errmsg);
    int j,i;
    int Index = ncolumn;

    for(i = 1 ; i < nrow+1;i++)
    {
        int ret = strcmp(result[i*ncolumn + 1],username);
        if(ret == 0)
        {   
            flag = 1;
            
        }//0行就是第一行  第i行第j列
    }

    if(flag == 1)
    {   printf("数据库是否存在该用户%d存在\n",flag);

        sqlite3_free_table(result);//释放数据库
        ret = sqlite3_close(db);
        if(SQLITE_OK != ret)
        {
            perror("sqlite3_close:");
            exit(1);
        }
        return flag;
    }
    else
    {   
        flag = 0;
        printf("%d\n",flag);
        sqlite3_free_table(result);//释放数据库
        ret = sqlite3_close(db);
        if(SQLITE_OK != ret)
        {
            perror("sqlite3_close:");
            exit(1);
        }
        return flag;
    }
}
//指定删除数据库里面的离线消息
void deleteumessage(clientlist *c)
{   

    char sql[200] = "\0";
    memset(sql,0,sizeof(sql));//清空sql
    sprintf(sql,"delete from message where toname = '%s';",c->name);
    execstate(sql);
    printf("删除数据库消息成功\n");
}


//创建表  注意更换数据库
void createtable()
{
    sqlite3 *db = NULL;  //目标数据库
    int ret = sqlite3_open("user.db",&db);
    if(SQLITE_OK != ret)
    {
        perror("sqlite3_open");
        exit(1);
    }
    char *errmsg;
    char sql[500] = "\0";
    strcpy(sql,"create table user (name text,password text);");
    ret = sqlite3_exec(db,sql,NULL,NULL,&errmsg);
    if(SQLITE_OK != ret)
    {
        perror("sqlite3_exec:");
        printf("errmsg:%s\n",errmsg);
        exit(1);
    }
    ret = sqlite3_close(db);
    if(SQLITE_OK != ret)
    {
        perror("sqlite3_close:");
        exit(1);
    }
    return;
}


//异常掉线处理
void Offlineprocessing(int fd)
{
    char buf2[20] = "\0";
    online_userlist *p = head;
    online_userlist *q;
    online_userlist *p1 = head;
    while(p->next!=NULL)
    {
        if(p->next->c_fd==fd)
        {
            strcpy(buf2,p->next->name);
            q=p->next;
            p->next=p->next->next;
            free(q);
            q=NULL;
            printf("将%s踢出链表成功\n",buf2);
            break;
        }
        p=p->next;
    }
    char buf[50] = "\0";
    memset(buf,0,sizeof(buf));
    sprintf(buf,"[%s]:退出了",buf2);
    printf("退出信息拷贝确认");
    p1=p1->next;
    while(p1!=NULL)//如果用p->next!=NULL  当p在最后一个结点是 p->next=NULL 无法进入循环
    {   printf("客户退出遍历链表测试\n");
            if(send(p1->c_fd,buf,sizeof(buf),0) == -1)
            {
                ERRLOG("send error");
            }
            
        p1=p1->next;
    }
}

server.h

#ifndef SERVER_H_
#define SERVER_H_
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/socket.h>
#include <pthread.h>
#include <arpa/inet.h>
#include <sys/errno.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/time.h>
#include <sys/epoll.h>
#include <sqlite3.h>
#define M 256
#define REGISTER 1//注册
#define LOGIN 2 //登录
#define PRIVATE 3 //私聊
#define CHPASSWORD 4 //修改密码
#define GROUPCHAT 5 //群聊
#define VIEWUSER 6 //查看在线用户
#define LOGUSER 7 //注销用户
#define CHECKRECORD 8 //查看聊天记录
#define FILETRAN  9 //文件传输
#define APPLYROOT 10 //管理员注册
#define ROOTLOGIN 11 //管理员登录
#define ROOTKICK 12 //管理员踢人
#define SILENT 13 //管理员禁言
#define REMSILENT 14 //解除禁言
#define CANCELROOT 15 //撤销管理员身份

#define ERRLOG(errmsg) do{\
                            perror(errmsg);\
                            printf("%s - %s - %d\n", __FILE__, __func__, __LINE__);\
                            exit(1);\
                            }while(0)

//1通信协议
typedef struct client
{
    char emojio[20];    //头像保存
    char name[32];      //姓名        
    char msg[50];       //聊天内容
    char password[32];  //密码
    char to_name[50];   //聊天对象
    char buf[256];      //文件内容 
    int num;            //文件读取大小     
    int root;           //管理员标志
    int flag;           //功能标志 注册标志1 登录2  私聊3 修改密码4 群聊界面5 查看在线用户6 注销用户7  查看聊天记录8  下载文件9
}clientlist;
//文件传输使用的结构体
typedef struct file
{
    char text[M];
    int num;
}MSG;

//在线用户链表
typedef struct online_user
{
    char name[32];//姓名
    int c_fd;
    struct  online_user*next;  //指针域
}online_userlist;

//线程池
typedef struct Myarg
{
    clientlist msg;
    int fd;
}Myarg;


//任务队列
struct job{
    void*(*func)(void *arg);
    void *arg;  //给回调函数传参使用
    struct job *next;
};


struct pthreadpool//线程池的初始化
{
    int m_threadNum;  //已开启的线程的数量
    pthread_t *m_pthreadIDs;  //保存线程池中线程的ID

    struct job *head;  //任务队列的头
    struct job *rear;  //任务队列的尾

    int m_QueueMaxNum;  //任务队列的最大数
    int m_QueueCurNum; //目前任务队列的任务数

    pthread_mutex_t mutex;

    pthread_cond_t m_QueueEmpty; //任务队列为空的条件
    pthread_cond_t m_QueueNotEmpty; //任务队列不为空的条件
    pthread_cond_t m_QueueNotFull; //任务队列为空的条件
};


//线程函数
void *work(void *arg);
//函数声明
void *ThreadRun(void *arg);
//初始化线程池
struct pthreadpool *InitPthreadPool(int ThreadNum, int QueueMaxNum);
//添加任务队列
void ThreadAddJob(struct pthreadpool *pool, void *(*func)(void *arg), void *arg);
//销毁线程池
void ThreadDestroy(struct pthreadpool *pool);
//初始化
int socket_init();
//epoll函数
int mypoll(int sockfd);
//在线用户链表
online_userlist* online_userlistcreate();


//注册
void Register(Myarg *node,clientlist *c);
//向数据库中插入注册数据
void insert_updata(Myarg *node,clientlist *c);
//在数据库中查询用户
int searchtable(char *tablename);
//执行sql语句函数
void execstate(char *sql);


//登录部分
void login(Myarg *node,clientlist *c);
//登录插入链表
void online_insertion(Myarg *node,clientlist *c);
//核验密码和账户是否正确
int veridandpassword(clientlist *p);
//重复登录判断
int Repeatlogin(Myarg *node,clientlist *c);
//判断除自己以外是否还有别人在线
int onlineempty1();
//判断链表是否为空
int onlineempty();


//私聊部分
void privatechat(Myarg *node,clientlist *c);
//判断是否在线 在线返回对方文件描述符 不在线返回0
int Find_person(clientlist *c);
//判断是否为敏感词  存在返回1 不存在返回0
int sensitiveword(char *sensitive);
//离线消息保存至表中
void saveOfflinemessage(clientlist *c);

//修改密码部分
void changepassword(Myarg *node,clientlist *c);
//根据名字修改密码
void Insert_updated_data(char *name,char *password);
//退出聊天室 删除链表内的用户结点
void exituser(clientlist *c);


//群聊部分
void groupchat(Myarg *node,clientlist *c);
//获取管理员头像
void getheadflag(clientlist *c);
//查看所有在线用户
void viewonline_user(Myarg *node,clientlist *c);
//注销用户
void loguser(Myarg *node,clientlist *c);
//掉线处理
void Offlineprocessing(int fd);
//将聊天记录保存
void insert_uprecord(clientlist *c);
//指定删除数据库的数据
void deleteuser(clientlist *c);
//指定删除数据库里面的root用户
void deleteroot(clientlist *c);
//查看聊天记录
void Checkrecord(Myarg *node);


//管理员用户的申请
void applyroot(Myarg *node,clientlist *c);
//在数据库中管理员表中查看是否存在该管理员
int searchtableroot(char *tablename);
//向root表中插入数据
void insert_updataroot(Myarg *node,clientlist *c);


//管理员登录
void rootlogin(Myarg *node,clientlist *c);
//判断管理员用户与密码
int rootveridandpassword(clientlist *c);
//管理员重复登录判断
int rootRepeatlogin(Myarg *node,clientlist *c);


//文件传输
void filetransfer(Myarg *node,clientlist *c);


//管理员踢人
void rootkick(Myarg *node,clientlist *c);
//管理员禁言
void silent(Myarg *node,clientlist *c);
//管理员解除禁言
void remsilent(Myarg *node,clientlist *c);
//撤销管理员身份
void cancelroot(Myarg *node,clientlist *c);

//离线消息发送
void sendOfflinemessage(Myarg *node,clientlist *c);
//在数据库中查找是否有该用户的离线消息 存在返回1 不存在返回0
int searchtuser(char *tablename);
//指定删除数据库里面的离线消息
void deleteumessage(clientlist *c);
//异常掉线处理
void Offlineprocessing(int fd);
//char *exit1user(int fd);


#endif

mian.c

#include "server.h"
online_userlist* head = NULL; //在线用户列表
struct pthreadpool *pool;
extern struct pthreadpool *InitPthreadPool(int ThreadNum, int QueueMaxNum);//初始化线程池
int main()
{
    
    head = online_userlistcreate();  //创建在线用户链表
    int sockfd = socket_init();
    pool = InitPthreadPool(1, 10);//1个线程对10个任务
    mypoll(sockfd);
    return 0;
}

客户端

client.c

#include "clientaddr.h"
unsigned char ip_str[] = "192.168.91.151";

unsigned char port_str[] = "8787";
int flag1 = 0;
int flag2 = 1;
int flag3 = 1;//文件传输对方是否在线标志


void n_init()//客户端初始化框架
{
    clientfd2  = socket(AF_INET,SOCK_STREAM,0);
    if(clientfd2 == -1)
    {
        perror("socket");
        return;
    }
    struct sockaddr_in clientaddr;
    clientaddr.sin_family = AF_INET;//协议族  IPV4
    clientaddr.sin_addr.s_addr = inet_addr(ip_str); 
    /*in_addr_t inet_addr(const char *cp);将点分十进制字符串ip地址转化为网络序的无符号4字节
    整数ip地址*/
    int addrlen = sizeof(clientaddr);
    clientaddr.sin_port = htons(atoi(port_str));
    if(connect(clientfd2,(struct sockaddr *)&clientaddr,addrlen)==-1)
    {
        perror("\033[0;31m无法连接到服务器\033[0m\n");
        ERRLOG("connect");
    }
    return;
}

void selectfun()//进入页面选着功能
{       
        usleep(500*1000);
        system("clear");
        printf("\033[0;46m*===================================================================*\033[0m\n");
        printf("\033[0;46m|                                                                   |\033[0m\n");
        printf("\033[0;46m|*********************\033[0;33m欢迎来到防脱发研究中心\033[0;46m************************|\033[0m\n");
        printf("\033[0;46m|                                              🐯 \033[1;43;33m作者:ZJN \033[0;46m         |\033[0m\n");
        printf("\033[0;46m|                                                版本:epoll+线程池  |\033[0m\n");
        printf("\033[0;46m|-------------------------------------------------------------------|\033[0m\n");
        printf("\033[0;46m|                                                                   |\033[0m\n");
        printf("\033[0;46m|                         \033[0;35m1.用户登录💩\033[0;46m                              |\033[0m\n");
        printf("\033[0;46m|                                                                   |\033[0m\n");
        printf("\033[0;46m|                         \033[0;32m2.帐号注册👾\033[0;46m                              |\033[0m\n");
        printf("\033[0;46m|                                                                   |\033[0m\n");
        printf("\033[0;46m|                         \033[0;33m3.管理员登录🐯\033[0;46m                            |\033[0m\n");
        printf("\033[0;46m|                                                                   |\033[0m\n");
        printf("\033[0;46m|                         \033[0;31m 0:退出聊天室🐴\033[0;46m                           |\033[0m\n");
        printf("\033[0;46m|                                                                   |\033[0m\n");
        printf("\033[0;46m*===================================================================*\033[0m\n");
        printf("\n"); 
        int num;
        scanf("%d",&num);
        getchar();
        switch (num)
        {             
            case 0:
            exit_room();//退出聊天室
            break;

            case 1:
            system("clear");
            printf("\033[0;31m正在进行用户登录..\033[0m\n");
            system("./zjn");
            user_login();//登录
            break;

            case 2:
                system("clear");
                printf("\033[0;31m正在进行账号注册\033[0m\n");
                user_register();//返回注册页面
                selectfun();
            break;

            case 3:
                system("clear");
                printf("\033[0;31m正在进行管理员登录\033[0m\n");
                rootlogin();
                putchar(10);
            break;

            case 4:
                system("clear");
                printf("\033[0;31m正在退出聊天室\033[0m\n");
                exit_room();
                putchar(10);
            break;
            default:
                system("clear");
                printf("\033[0;31m对不起输入错误请从新输入..\033[0m\n");
                selectfun();
            return;
        }
}

void funcinto()//第二阶段选择进入
{
    system("clear");
    printf("\033[34m                                         \033[0;36m<1:私聊功能>🐱\033[0;34m            \033[0m\n");
    putchar(10);
    printf("\033[34m                               \033[0;36m🐒<2:进入群聊>\033[0;34m           \033[0m\n");
    putchar(10);
    printf("\033[34m                                         \033[0;36m<3:修改密码>🐶\033[0;34m       \033[0m\n");
    putchar(10);
    printf("\033[0;31m                              \033[0;36m🐩<4:查看当前在看用户>\033[0;34m           \033[0m\n");
    putchar(10);
    printf("\033[34m                                         \033[0;31m<5:注销用户>🦒\033[0;34m    \033[0m\n");
    putchar(10);
    printf("\033[33m                              \033[0;33m🐴<6:申请称为管理员>\033[0;34m    \033[0m\n");
    putchar(10);
    printf("\033[34m                                         \033[0;33m🐆<0:退出聊天室>\033[0;34m    \033[0m\n");
    int num;
    scanf("%d",&num);
    getchar();
    switch (num)
    {               
        case 0:
        printf("\033[0;31m正在退出聊天室..\033[0m\n");
        exit_room(); 
        putchar(10);
        break;

        case 1:
        system("clear");
        printf("\033[0;31m正在进入私聊功能..\033[0m\n");
        privatechat();
        funcinto();
        break;
        case 2:
            system("clear");
            printf("\033[0;31m正在入群聊功能\033[0m\n");
            groupchat();
            funcinto();
            break;
        case 3:
            system("clear");
            printf("\033[0;31m正在进行修改密码\033[0m\n");
            changpassword();
            selectfun();
            putchar(10);
            break;
        case 4:
        
            system("clear");
            printf("\033[0;31m正在查看在线用户\033[0m\n");
            viewonline_user();
            sleep(3);
            funcinto();
            break;
        case 5:
            system("clear");
            printf("\033[0;31m正在进行注销用户\033[0m\n");
            deleteuser();
            sleep(1);
            selectfun();
            putchar(10);
            break;
        case 6:
            system("clear");
            printf("\033[0;31m正在申请成为管理员\033[0m\n");
            applyroot();
            putchar(10);
        break;
        default:
            system("clear");
            printf("\033[0;31m对不起输入错误请从新输入..\033[0m\n");
            funcinto();
            return;
    }
}

void user_register()//功能标志1 用户注册
{
    ssize_t  ret;
    char password[50];
    system("clear");
    printf("\033[0;31m请输入你的名字..\033[0m\n");
    scanf("%s",ts.name);
    system("clear");
    printf("\033[0;31m请输入你的密码\033[0m\n");
    system("stty -echo");
    scanf("%s",password);
    system("stty echo");
    system("clear");
    printf("\033[0;31m请再次确认你的密码\033[0m\n");
    system("stty -echo");
    scanf("%s",ts.password);
    system("stty echo");

    
    if(strcmp(password,ts.password) == 0)
    {
        system("./zjn");
        ts.flag = 1;
        ret =send(clientfd2,&ts,sizeof(clientlist),0);
        if(ret == -1)
        {
            ERRLOG("send");
        }
        return; 
    }
    else
    {
        printf("\033[0;31m输入密码不一致请从新输入...\033[0m\n");
        sleep(1);
        user_register(ts);
    }
    
}

void user_login()//功能标志 2用户登录
{
    
    ssize_t  ret;
    system("clear");
    printf("\033[0;31m请输入你的名字..\033[0m\n");
    scanf("%s",ts.name);
    system("clear");
    printf("\033[0;31m请输入你的密码\033[0m\n");
    system("stty -echo");
    scanf("%s",ts.password);

    sleep(1);
    system("stty echo");
    ts.flag = 2;  //用户登录标志位
    ret =send(clientfd2,&ts,sizeof(clientlist),0);
    if(ret == -1)
    {
        ERRLOG("send");
    }
    sleep(1);
    if(1==flag1)
    {
        flag1 = 0;
        selectfun();
    }
    if(0 == flag1)
    {
        funcinto();
    }
    return;
}

void privatechat()// 功能标志3 用户私聊
{
    ssize_t  ret;
    char to_name[50];
    system("clear");
    printf("\033[0;31m请输入你想私聊的对象..\033[0m\n");
    scanf("%s",ts.to_name);
    sleep(1);
    system("clear");
        ts.flag = 3;  //私聊标志为3
        char buf[200] = "\0";
        char *buf2;
        system("clear");
        printf("\033[0;31m请输入聊天内容..\033[0m\n");  //循环聊天记得加循环
        printf("\033[0;31m                               <输入Q!退出私聊>\033[0m\n");
        printf("\033[0;31m                               <输入W!查看快捷短语>\033[0m\n");
        printf("\033[0;31m                               [当前用户]:%s\033[0m\n",ts.name);
        while(1)
        {
            scanf("%s",buf);
            if(strcmp(buf,"Q!") == 0)
            {
                system("clear");
                return;
            }
            if(strcmp(buf,"W!") == 0)
            {
                phrase();
                continue;
            }
            if(flag2)
            {
                if((buf2 = selectphrase(buf)) == NULL)
                {
                    strcpy(ts.msg,buf);
                    ret =send(clientfd2,&ts,sizeof(clientlist),0);
                    if(ret == -1)
                    {
                        ERRLOG("send");
                    }
                }
                else
                {
                    strcpy(ts.msg,buf2);
                    ret =send(clientfd2,&ts,sizeof(clientlist),0);
                    if(ret == -1)
                    {
                        ERRLOG("send");
                    }
                }
            }
        }
        return;
}

void changpassword()//功能标志4 修改密码
{
    ssize_t  ret;
    char newpassword[50];
    char password[50];
    system("clear");
    printf("\033[0;31m请输入你的新密码\033[0m\n");
    system("stty -echo");
    scanf("%s",newpassword);
    system("stty echo");
    system("clear");
    printf("\033[0;31m请再次确认你的密码\033[0m\n");
    system("stty -echo");
    scanf("%s",password);
    system("stty echo");
    
    if(strcmp(password,newpassword) == 0)
    {   strcpy(ts.msg,newpassword);
        system("./zjn");
        ts.flag = 4;
        ret =send(clientfd2,&ts,sizeof(clientlist),0);
        if(ret == -1)
        {
            ERRLOG("send");
        }
        return; 
    }
    else
    {
        printf("\033[0;31m输入密码不一致请从新输入...\033[0m\n");
        sleep(1);
        changpassword();
    }
}

void groupchat() //功能标志5  群聊 显示群聊界面
{
    int num = 0;
    ssize_t ret;
    system("./zjn");
    ts.flag = 5;  //私聊标志为5
    char buf[200] = "\0";
    char *buf2;
    system("clear");
    printf("\033[0;31m请输入聊天内容..\033[0m\n");  //循环聊天记得加循环
    printf("\033[0;31m                               <输入Q!退出群聊>\033[0m\n");
    printf("\033[0;36m                               <输入W!查看快捷短语>\033[0m\n");
    printf("\033[0;36m                               <输入C!查看聊天记录>\033[0m\n");
    printf("\033[0;36m                               <输入T!进行文件传输>\033[0m\n");
    printf("\033[0;31m                               [当前用户]:%s\033[0m\n",ts.name);
    while(1)
    {   
        scanf("%s",buf);
        if(strcmp(buf,"Q!") == 0)
        {
            system("clear");
            return;
        }
        if(strcmp(buf,"W!") == 0)
        {
            phrase();
            printf("\033[0;32m请输入1继续群聊..\033[0m\n");
            scanf("%d",&num);
            if(1 == num)
            {
                break;
            }
            else
            {
                printf("\033[0;32m输入错误\033[0m\n");
            }
        }
        if(strcmp(buf,"C!") == 0)
        {
            checkrecord();
            printf("\033[0;32m请输入1继续群聊..\033[0m\n");
            scanf("%d",&num);
            if(1 == num)
            {
                break;
            }
            else
            {
                printf("\033[0;32m输入错误\033[0m\n");
            }
        }
        if(strcmp(buf,"T!") == 0)
        {
            filetransfer();
            sleep(1);
            break;
        }
        if(flag2)
        {
            if((buf2 = selectphrase(buf)) == NULL)
            {
                strcpy(ts.msg,buf);
                ret =send(clientfd2,&ts,sizeof(clientlist),0);
                if(ret == -1)
                {
                    ERRLOG("send");
                }
            }
            else
            {
                strcpy(ts.msg,buf2);
                ret =send(clientfd2,&ts,sizeof(clientlist),0);
                if(ret == -1)
                {
                    ERRLOG("send");
                }
            }
        }
    }
    groupchat();
}

void viewonline_user()//功能标志6 查看所有在线用户
{
    ts.flag = 6; 
    int ret;
    ret =send(clientfd2,&ts,sizeof(clientlist),0);
    if(ret == -1)
    {
        ERRLOG("send");
    }
    return;
}

void deleteuser() //功能标志 7 注销用户
{   
    char password[30] = {0};
    char name[30] = {0};
    printf("\033[0;31m                        请认真确认你的用户信息\033[0m\n");
    printf("\033[0;31m<输入姓名>\033[0m\n");
    scanf("%s",name);
    system("clear");
    printf("\033[0;31m<输入密码>\033[0m\n");
    scanf("%s",password);
    int ret1,ret2;
    ret1 =  strcmp(name,ts.name);
    ret2 = strcmp(password,ts.password);
    if(0 == ret1 && 0 == ret2)
    {
        ts.flag = 7; 
        int ret;
        ret =send(clientfd2,&ts,sizeof(clientlist),0);
        if(ret == -1)
        {
            ERRLOG("send");
        }
    }
}

void phrase()//快捷短语打印
{
    printf("\033[34m                  \033[0;36m🐒<快捷短语大全>\033[0;34m           \033[0m\n");
    putchar(10);
    printf("\033[34m               \033[0;36m1.<听说jsetc给分配对象?😍>\033[0;34m           \033[0m\n");

    printf("\033[34m               \033[0;36m2.<😅>\033[0;34m           \033[0m\n");
    
    printf("\033[34m               \033[0;36m3.<😘>\033[0;34m           \033[0m\n");
    
    printf("\033[34m               \033[0;36m4.<🤨>\033[0;34m           \033[0m\n");
    
    printf("\033[34m               \033[0;36m5.<😁>\033[0;34m           \033[0m\n");
    
    printf("\033[34m               \033[0;36m6.<真男人从不食💩,只食💨>\033[0;34m           \033[0m\n");
    
    printf("\033[34m               \033[0;36m7.<👾>\033[0;34m           \033[0m\n");
    
    printf("\033[34m               \033[0;36m8.< 嘟嘟嘟!! 🚋 The car is coming>\033[0;34m           \033[0m\n");
    
    printf("\033[34m               \033[0;36m9.<就这么点👌?>\033[0;34m           \033[0m\n");
    
    printf("\033[34m               \033[0;36m10.< 来🍇🍈🍊🍋多吃点,别让人看扁 >\033[0;34m           \033[0m\n");
    
    printf("\033[34m               \033[0;36m11.<顺便给你分配个👩‍🍳>\033[0;34m           \033[0m\n");
    
    printf("\033[34m               \033[0;36m12.< Other people's money is my belongings😛 >\033[0;34m           \033[0m\n");
    
    printf("\033[34m               \033[0;36m13.<Are you a dog?🐶>\033[0;34m           \033[0m\n");
    
    printf("\033[34m               \033[0;36m14.<你是个什么🐮🐎>\033[0;34m           \033[0m\n");
    
    printf("\033[34m               \033[0;36m15.<一个巴掌拍不响但是加上一个🍑可以>\033[0;34m           \033[0m\n");
    

}

char *selectphrase(char *num)//快捷短语选择
{
     char *buf;
    buf = (char *)malloc(32);

        if(strcmp(num,"1.")==0)
    {
        strcpy(buf,"听说jsetc给分配对象?😍");
        return buf;
    }
        if(strcmp(num,"2.")==0)
    {
        strcpy(buf,"😅");
        return buf;
    }
        if(strcmp(num,"3.")==0)
    {
        strcpy(buf,"😘");
        return buf;
    }
        if(strcmp(num,"4.")==0)
    {
        strcpy(buf,"🤨");
        return buf;
    }

        if(strcmp(num,"5.")==0)
    {
        strcpy(buf,"😁");
        return buf;
    }    
        if(strcmp(num,"6.")==0)
    {
        strcpy(buf,"真男人从不食💩,只食💨");
        return buf;
    }
        if(strcmp(num,"7.")==0)
    {
        strcpy(buf,"嘟嘟嘟!! 🚋 The car is coming");
        return buf;
    }
        if(strcmp(num,"8.")==0)
    {
        strcpy(buf,"就这么点👌?");
        return buf;
    }
        if(strcmp(num,"9.")==0)
    {
        strcpy(buf,"来🍇🍈🍊🍋多吃点,别让人看扁");
        return buf;
    }
        if(strcmp(num,"10.")==0)
    {
        strcpy(buf,"顺便给你分配个👩‍🍳");
        return buf;
    }
        if(strcmp(num,"11.")==0)
    {
        strcpy(buf,"Other people's money is my belongings😛");
        return buf;
    }
        if(strcmp(num,"12.")==0)
    {
        strcpy(buf,"Are you a dog?🐶");
        return buf;
    }
        if(strcmp(num,"13.")==0)
    {
        strcpy(buf,"你是个什么🐮🐎");
        return buf;
    }
        if(strcmp(num,"14.")==0)
    {
        strcpy(buf,"一个巴掌拍不响但是加上一个🍑可以");
        return buf;
    }
        if(strcmp(num,"15.")==0)
    {
        strcpy(buf,"👾");
        return buf;
    }
    return NULL;
}

void checkrecord()//功能标志8 查看聊天记录 
{
        ts.flag = 8; 
        int ret;
        ret =send(clientfd2,&ts,sizeof(clientlist),0);
        if(ret == -1)
        {
            ERRLOG("send");
        }
}

void  filetransfer()//功能标志9 文件传输
{
    clientlist ts1;
    strcpy(ts1.name,ts.name);
    ts1.flag = 9; 
    char filename[128];
    
    printf("\033[0;31m           <请输入要传输的文件名>\033[0m\n");
    scanf("%s",filename);
    printf("\033[0;31m           <请输入要传输的对象>\033[0m\n");
    scanf("%s",ts1.to_name);

    int bytes;
    int fd;
    if((fd = open(filename,O_RDONLY)) == -1)
    {
        perror("open error!\n");
        printf("未找到此文件");
        return;
    }
    printf("打开文件成功\n");
    while(flag3)
    {   
        memset(ts1.buf,0,sizeof(ts1.buf));
        bytes = read(fd,ts1.buf,256);
        if(bytes == -1)
        {
            perror("read");
            return;
        }
        ts1.num = bytes;
        sleep(1);
        send(clientfd2,&ts1,sizeof(ts1),0);
        if(bytes == 0)
        {
            strcpy(ts1.buf,"0");
            send(clientfd2,&ts1,sizeof(ts1),0);
            break;
        }
    }
    flag3 = 1;
    close(fd);
    return;
} 

void applyroot()//功能标志 10 管理员申请  包含头像的选择
{   int num;
    putchar(10);
    printf("\033[5;;35m尊敬的管理员请选择您的专属头像\033[0m\n");
    putchar(10);
    printf("\033[33m\033[0;33m  1.👽 2.👹 3.👻 4.🐉 5.😈 6.🐸 7.🐱 8.👺             \033[0;34m     \033[0m\n");
    putchar(10);
    printf("\033[33m\033[0;33m  9.🐯 10.🐆 11.🐴 12.🐋 13.😹 14.🐮 15.🙀\033[0;34m     \033[0m\n");
    scanf("%d",&num);
    if(1 == num)
    {
        strcpy(ts.emojio,"👽");
    }
        if(2 == num)
    {
        strcpy(ts.emojio,"👹");
    }
        if(3 == num)
    {
        strcpy(ts.emojio,"👻");
    }
    if(4 == num)
    {
        strcpy(ts.emojio,"🐉");
    }
        if(5 == num)
    {
        strcpy(ts.emojio,"😈");
    }
        if(6 == num)
    {
        strcpy(ts.emojio,"🐸");
    }
        if(7 == num)
    {
        strcpy(ts.emojio,"🐱");
    }
        if(8 == num)
    {
        strcpy(ts.emojio,"👺");
    }
        if(9 == num)
    {
        strcpy(ts.emojio,"🐯");
    }
        if(10 == num)
    {
        strcpy(ts.emojio,"🐆");
    }
        if(11 == num)
    {
        strcpy(ts.emojio,"🐴");
    }
        if(12 == num)
    {
        strcpy(ts.emojio,"🐋");
    }
        if(13 == num)
    {
        strcpy(ts.emojio,"😹");
    }
        if(14 == num)
    {
        strcpy(ts.emojio,"🐮");
    }
        if(15 == num)
    {
        strcpy(ts.emojio,"🙀");
    }
    ts.flag = 10; 
    ts.root = 1;
    int ret;
    ret =send(clientfd2,&ts,sizeof(clientlist),0);
    if(ret == -1)
    {
        ERRLOG("send");
    }
    sleep(1);
    system("clear");
    if(flag1 == 2)
    {   flag1 = 0;
        selectfun();
    }
    else
    {

        printf("\033[33m     \033[0;33m申请管理员失败\033[0;34m    \033[0m\n");
        sleep(1);
        //system("clear");
        funcinto();
    }
}

void rootlogin()//功能标志11 管理员登录 登录成功直接进入管理员聊天界面
{
    ssize_t  ret;
    system("clear");
    printf("\033[0;31m请输入你的名字..\033[0m\n");
    scanf("%s",ts.name);
    system("clear");
    printf("\033[0;31m请输入你的密码\033[0m\n");
    system("stty -echo");
    scanf("%s",ts.password);
    sleep(1);
    system("stty echo");
    ts.flag = 11;  //用户登录标志位
    ret =send(clientfd2,&ts,sizeof(clientlist),0);
    if(ret == -1)
    {
        ERRLOG("send");
    }
    sleep(1);
    if(3 == flag1)
    {
        flag1 = 0;
        selectfun();
    }
    if(0 == flag1)
    {
        rootgroupchat();
    } 
    return;
}

void rootgroupchat() //管理员群聊界面 显示群聊界面
{
    usleep(500*1000);
    int num = 0;
    ts.root = 1;
    ssize_t ret;
    ts.flag = 5;  
    char buf[200] = "\0";
    char *buf2;
    system("clear");
    printf("\033[0;31m请输入聊天内容..\033[0m\n");  //循环聊天记得加循环
    printf("\033[0;31m                             <输入Q!退出聊天室>\033[0m\n");
    printf("\033[0;36m                             <输入W!查看快捷短语>\033[0m\n");
    printf("\033[0;36m                             <输入C!查看聊天记录>\033[0m\n");
    printf("\033[0;36m                             <输入T!进行文件传输>\033[0m\n");
    printf("\033[0;36m                             <输入L!查看当前在线人数>\033[0m\n");
    printf("\033[0;36m                                               <输入K!踢人>\033[0m\n");
    printf("\033[0;36m                                               <输入F!禁言>\033[0m\n");
    printf("\033[0;36m                                               <输入U!解除禁言>\033[0m\n");
    printf("\033[0;36m                                               <输入E!撤销管理员身份>\033[0m\n");
    printf("\033[0;33m                               [管理员]😇:%s\033[0m\n",ts.name);
    while(1)
    {
        scanf("%s",buf);
        
        if(strcmp(buf,"Q!") == 0)//退出群聊
        {   
            system("clear");
            exit_room();
            return;
        }
        if(strcmp(buf,"E!") == 0)//取消管理员身份
        {   
            system("clear");
            cancelroot();
            return;
        }
        if(strcmp(buf,"W!") == 0)//查看快捷短语
        {
            phrase();
            continue;
        }
        if(strcmp(buf,"C!") == 0)//查看聊天记录
        {
            checkrecord();
            printf("\033[0;32m请输入1继续群聊..\033[0m\n");
            scanf("%d",&num);
            if(1 == num)
            {
                break;
            }
            else
            {
                printf("\033[0;32m输入错误\033[0m\n");
            }
        }
        if(strcmp(buf,"T!") == 0)//文件传输
        {
            filetransfer();
                break;
        }
        if(strcmp(buf,"L!") == 0)//查看在线用户
        {
            rootviewonline_user();
            printf("\033[0;32m请输入1继续群聊..\033[0m\n");
            scanf("%d",&num);
            if(1 == num)
            {
                break;
            }
            else
            {
                printf("\033[0;32m输入错误\033[0m\n");
            }
        }
        if(strcmp(buf,"K!") == 0)//踢人
        {
            rootkick();
                break;
        }
        if(strcmp(buf,"F!") == 0)//禁言
        {
            silent();
            break;
        }
        if(strcmp(buf,"U!") == 0)//解除禁言
        {
            remsilent();
            break;
        }
        if((buf2 = selectphrase(buf)) == NULL)
        {
            strcpy(ts.msg,buf);
            ret =send(clientfd2,&ts,sizeof(clientlist),0);
            if(ret == -1)
            {
                ERRLOG("send");
            }
            continue;
        }
        else
        {
            strcpy(ts.msg,buf2);
            ret =send(clientfd2,&ts,sizeof(clientlist),0);
            if(ret == -1)
            {
                ERRLOG("send");
            }
        }
    }
    rootgroupchat();
}

void rootviewonline_user()//管理员标志6 查看所有在线用户
{
    ts.flag = 6; 
    int ret;
    ret =send(clientfd2,&ts,sizeof(clientlist),0);
    if(ret == -1)
    {
        ERRLOG("send");
    }
    ts.flag = 5; 
    return;
}

void rootkick()//功能标志12 踢出群聊
{
    ts.root = 1;
    ssize_t  ret;
    system("clear");
    ts.flag = 12;
    printf("\033[0;31m请输入你想踢出群聊的对象..\033[0m\n");
    scanf("%s",ts.to_name);
    ret =send(clientfd2,&ts,sizeof(clientlist),0);
    if(ret == -1)
    {
        ERRLOG("send");
    }
    sleep(1);
    rootgroupchat();
    return;
    
}

void silent()//功能标志13  禁言
{
    ts.root = 1;
    ssize_t  ret;
    system("clear");
    ts.flag = 13;
    printf("\033[0;31m请输入你想禁言的对象..\033[0m\n");
    scanf("%s",ts.to_name);
    ret =send(clientfd2,&ts,sizeof(clientlist),0);
    if(ret == -1)
    {
        ERRLOG("send");
    }
    sleep(1);
    rootgroupchat();
    return;
}

void remsilent()//功能标志14  解除禁言
{
    ts.root = 1;
    ssize_t  ret;
    system("clear");
    ts.flag = 14;
    printf("\033[0;31m请输入你想解除禁言的对象..\033[0m\n");
    scanf("%s",ts.to_name);
    ret =send(clientfd2,&ts,sizeof(clientlist),0);
    if(ret == -1)
    {
        ERRLOG("send");
    }
    rootgroupchat();
    return;
}

void cancelroot()//功能标志15 取消管理员身份
{
    char password[30] = {0};
    char name[30] = {0};
    printf("\033[0;31m                        请认真确认你的用户信息\033[0m\n");
    printf("\033[0;31m<输入姓名>\033[0m\n");
    scanf("%s",name);
    system("clear");
    printf("\033[0;31m<输入密码>\033[0m\n");
    scanf("%s",password);
    int ret1,ret2;
    ret1 =  strcmp(name,ts.name);
    ret2 = strcmp(password,ts.password);
    if(0 == ret1 && 0 == ret2)
    {
        ts.flag = 15; 
        int ret;
        ret =send(clientfd2,&ts,sizeof(clientlist),0);
        if(ret == -1)
        {
            ERRLOG("send");
        }
    }
    sleep(1);
    selectfun();
    return;
}




void *recv_thread(void *arg)//接受线程函数
{   
    // int ret = 1;
    int fd;
    MSG msg;
    //int num;
    char buf[256] = {0};
    int length;
    clientfd2 = *((int *)arg);
    while(1)
    {
        memset(buf,0,sizeof(buf));
        length = recv(clientfd2,buf,sizeof(buf),0);
        if (length == 0)
        {
            printf("服务器宕机!!\n");
            exit(1);
            pthread_exit(NULL);
            
        }
        buf[length] = '\0';
        printf("\033[5;;36m%s\033[0m\n",buf);
        if(strcmp(buf,"该用户不存在,请注册...") == 0)
        {
            flag1 = 1;
        }
        if(strcmp(buf,"对方不在线...") == 0)
        {
            flag3 = 0;
        }
        if(strcmp(buf,"密码输入错误..") == 0)
        {
            flag1 = 1;
        }
        if(strcmp(buf,"您已在线,请勿重复登录") == 0)
        {
            flag1 = 1;
        }
        if(strcmp(buf,"管理员申请成功") == 0)
        {
            flag1 = 2;
        }
        if(strcmp(buf,"该用户非管理员") == 0)
        {
            flag1 = 3;
        }
        if(strcmp(buf,"密码输入错误") == 0)
        {
            flag1 = 3;
        }
        if(strcmp(buf,"管理员已将你踢出聊天室") == 0)
        {
            exit(1);
        }
        if(strcmp(buf,"管理员已将你禁言") == 0)
        {
            flag2 = 0;
        }
        if(strcmp(buf,"管理员已将你解除禁言") == 0)
        {
            flag2 = 1;
        }
        if(strcmp(buf,"文件存在进行文件传输") == 0)
        {   
            printf("正在接受文件\n");
            if((fd = open("1.txt",O_WRONLY | O_CREAT | O_TRUNC,0664)) == -1)
            {
                ERRLOG("open error");
            }
            int ret;
            while((ret = recv(clientfd2,&msg,sizeof(msg),0)) != -1)
            {
                //printf("ret= %d\n",ret);
                if(strcmp(msg.text,"0") == 0)
                {
                    printf("\033[0;36m文件接受完成\033[0m\n");
                    //sleep(1);
                    close(fd);
                    break;
                }
                else
                {
                    write(fd,msg.text,msg.num);
                }
                memset(&msg,0,sizeof(msg));
            }
        }
        putchar(10);
    }
    //pthread_exit(NULL);
}

void *send_thread(void *arg)//发送线程函数
{  
        selectfun();
}

void  exit_room()//退出聊天室
{
    exit(0);
}

client.h

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <sys/socket.h>
#include <stdio.h>
#include <string.h>
#include <string.h>
#include <arpa/inet.h>
#include <pthread.h>
#include <ctype.h>
#include <unistd.h>
#include <fcntl.h>
#include <sqlite3.h>
#define N 256
#define ERRLOG(errmsg) do{\
                        perror(errmsg);\
                        printf("%s - %s - %d\n",__FILE__,__func__,__LINE__);\
                        exit(1);\
                }while(0)

int clientfd2;//客户端sockfd

typedef struct file    //文件传输结构体
{
        char text[N];
        int num;
}MSG;

typedef struct client
{
    char emojio[20];    //头像保存
    char name[32];      //姓名        
    char msg[50];       //聊天内容
    char password[32];  //密码
    char to_name[50];   //聊天对象
    char buf[256];      //文件内容 
    int num;            //文件读取大小     
    int root;           //管理员标志
    int flag;           //功能标志 注册标志1 登录2  私聊3 修改密码4 群聊界面5 查看在线用户6 注销用户7  查看聊天记录8  下载文件9
}clientlist;
clientlist ts;


void funcinto();//第二阶段选择进入

void recvmessage();//消息接受

void n_init();//初始化客户端

void selectfun();//进入目录选择功能

void user_login();//用户登录

void user_register();//用户注册

void privatechat();//私聊

void *recv_thread(void *arg);//客户端接受消息线程函数

void *send_thread(void *arg);//客户端发送消息函数

void changpassword(); //修改密码

void groupchat(); //群聊

void viewonline_user();//查看所有在线用户 

void deleteuser();//注销用户

void phrase();//快捷短语

char *selectphrase(char *num);//快捷短语的选择

void checkrecord();//查看聊天记录

void  filetransfer();//文件传输

void applyroot();//申请成为管理员

void rootlogin();//管理员登录

void rootgroupchat();//管理员群聊界面

void rootkick();//踢出群聊

void silent();//管理员禁言

void remsilent();//解除禁言


void rootviewonline_user();//管理员查看在线用户

void cancelroot();//取消管理员身份



void root_login();//管理员登录

void  exit_room();//退出聊天室



main.c

#include "clientaddr.h"


int main(int argc, char const *argv[])
{
    n_init();
    int ret;
    pthread_t tid_recv;
    pthread_t tid_send;
    ret = pthread_create(&tid_recv,NULL,(void *)recv_thread,(void *)&clientfd2);
    ret = pthread_create(&tid_send,NULL,(void *)send_thread,(void *)&clientfd2);

    pthread_join(tid_recv,NULL);
    pthread_join(tid_send,NULL);
    close(clientfd2);
    return 0;
}



版权声明:本文为m0_70681152原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。