一、项目介绍

实现功能

     用户注册、登录、悄悄话、群聊、私聊、查看在线用户、超级用户权限、踢人、禁言、 解禁、文件传输、查看聊天记录、忘记密码、注销用户


1、该项目采用C/S架构,通过线程池实现了服务器与多个客户端之间的通信

2、采用数据库Sqlite3在服务器端存储用户注册的昵称、姓名、密码、超级用户标志位

3、登录采用用户在线链表进行统一管理

4、创建一个数据库专门保存用户之间的聊天记录,分别存储发送方、接收方、聊天信息、聊天日期

5、超级用户独享踢人权限(只可将普通用户踢下线),禁言以及解禁普通用户的权限,

6、普通用户也可以申请成为超级用户,用户之间也可以进行文件的传输

7、线程池:优化服务器端、控制用户最大数量、防止造成例如请求过多而产生的崩溃现象

二、使用须知


注册用户为普通用户,可手动添加为管理员,但是必须在服务器同意或者拒绝

若被禁言,则为永久禁言,知道管理员解除禁言,或者重启服务器

若被管理员强制下线,退出聊天框重新进入即可

进行聊天时可以选择常用语或者手动输入

三、效果展示

聊天室功能演示

 四、代码展示

服务器代码

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 root;

//创建一个空链表(保存在线用户)
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;

}

//客户端下线将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
    {
        //cfd判断下线
        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;
}

//查找id是否存在
int FindId(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)
            {
                if(strcmp(result[Index] , data->id) == 0)
                {
                   flag = 1;
                   break;
                }
            }
            Index++;
        }
        if(flag == 1)
        {
            break;
        }
        
    }
    if(flag == 1)
    {
        sqlite3_free_table(result);
        return -1;
    }
    sqlite3_free_table(result);  
}

//注册
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;
    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;
    

    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");
}




main.c

#include "server.h"

int main(int argc, char **argv)
{
    printf("正在启动服务器\n");
    sleep(1);
    system("clear");
    int sockfd;
    int ret;

    //打开或者创建数据库
    sqlite3 *ppdb;
    ret = sqlite3_open("stu.db",&ppdb);
    if(ret != SQLITE_OK)
    {
        printf("sqlite3 open: %s\n",sqlite3_errmsg(ppdb));
        exit(-1);
    }

    printf("SOCKET3 INIT SUCCESS!\n");

    //创建表(用于保存注册用户的信息)
    CreatTable(ppdb);
    //创建第二张表用于保存聊天记录
    CreatTable2(ppdb);

    //遍历用户注册信息
    Id(ppdb);

    OnlineLinkList *head;      //id cfd next
    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");
        exit(-1);
    }
    
    printf("SOCKET INIT SUCCESS!\n"); 

    

    int opt = 1;
    setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));  //设置地址可以被重复绑定
    struct sockaddr_in server_addr;          //保存服务器信息
    memset(&server_addr,0,sizeof(server_addr));
    server_addr.sin_family = AF_INET; // 地址族  IPV4 
    server_addr.sin_addr.s_addr = htons(INADDR_ANY);  //ip地址
    //server_addr.sin_addr.s_addr = inet_addr("192.168.12.131");
    server_addr.sin_port = 8888;  //网络字节序端口
    //绑定信息
    ret = bind(sockfd,(struct sockaddr *)&server_addr,sizeof(server_addr));
    if(-1 == ret)
    {
        perror("bind");
        exit(-1);
    }
    //设置监听序列
    ret = listen(sockfd,10);
    if(-1 == ret)
    {
        perror("listen");
        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

        //为每一个客户端创建新的线程
        //pthread_t tid;

        // //创建线程  将cfd与head传给线程函数
        // int fd = pthread_create(&tid,NULL,(void *)MyFun,(void *)&node);
        // if(fd != 0)
        // {
        //     perror("pthread_create");
        //     return -1;
        // }

         //往线程池 任务队列 放任务
        threadpool_add_job(pool, (void *)MyFun,(void *)&node);
    }

    close(sockfd);

    thread_destroy(pool);

    return 0;
}

makefile

run:server.c main.c
	gcc *.c -o run -lsqlite3  -lpthread

客户端代码展示

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();
//常用语
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()
{
    printf("***********聊*************天*************室**************\n\n");
    printf("                          1.登录                       \n\n");
    printf("                          2.注册                       \n\n");
    printf("                          3.匿名聊天                    \n\n");
    printf("                          4.私聊                       \n\n");
    printf("                          5.群聊                       \n\n");
    printf("                          6.踢人                       \n\n");
    printf("                          7.禁言                       \n\n");
    printf("                          8.解除禁言                    \n\n");
    printf("                          9.忘记密码                    \n\n");
    printf("                         10.查看在线用户                 \n\n");
    printf("                         11.请求管理员身份               \n\n");
    printf("                         12.撤销管理员身份               \n\n");    
    printf("                         13.文件传输                  \n\n");
    printf("                         14.查看聊天记录                 \n\n"); 
    printf("                         15.注销账户                   \n\n");
    printf("                         16.退出聊天室                 \n\n");
    printf("*********************************************************\n");
}

//常用语功能界面
void menu1()
{
    printf("*******常***********用************语*******\n\n");  
    printf("            1.中国人不骗中国人           \n\n");
    printf("            2.花有重开日,人无再少年       \n\n");
    printf("            3.YYDS                    \n\n");
    printf("            4.大哥,别卷了              \n\n");
    printf("            5.我躺好了                  \n\n");
    printf("            6.摆烂了                    \n\n");
    printf("            7.干饭人                    \n\n");
    printf("            8.哒咩                    \n\n");
    printf("            9.芭比Q了                   \n\n");
    printf("           10.人生无常,大肠包小肠                   \n\n");
    printf("           11.gg                      \n\n");
    printf("           12.emo了                   \n\n");
    printf("           13.破大防了,家银们          \n\n");
    printf("           14.杀伤力不大,侮辱性极强     \n\n");
    printf("           15.啊,对对对对             \n\n");
    printf("           16.牛蛙兄弟                 \n\n");
    printf("           17.人家想要贴贴             \n\n");
    printf("           18.普信女             \n\n");
    printf("           19.你个老六             \n\n");
    printf("           20.这里有老六             \n\n");
    printf("     输入其他编号切换手动输入信息           \n\n");
    printf("********************************************\n");
    
}

//常用语
char *PhrasalVerbs(int *select)
{
    char *world;
    world = (char *)malloc(32);
    if(*select == 1)
    {
        strcpy(world,"中国人不骗中国人");
        return world;
    }
    if(*select == 2)
    {
        strcpy(world,"花有重开日,人无再少年");
        return world;
    }
    if(*select == 3)
    {
        strcpy(world,"YYDS");
        return world;
    }
    if(*select == 4)
    {
        strcpy(world,"大哥,别卷了");
        return world;
    }
    if(*select == 5)
    {
        strcpy(world,"我躺好了");
        return world;
    }
    if(*select == 6)
    {
        strcpy(world,"摆烂了");
        return world;
    }
    if(*select == 7)
    {
        strcpy(world,"干饭人");
        return world;
    }
    if(*select == 8)
    {
        strcpy(world,"哒咩");
        return world;
    }
    if(*select == 9)
    {
        strcpy(world,"芭比Q了");
        return world;
    }
    if(*select == 10)
    {
        strcpy(world,"人生无常,大肠包小肠");
        return world;
    }
    if(*select == 11)
    {
        strcpy(world,"gg");
        return world;
    }
    if(*select == 12)
    {
        strcpy(world,"emo了");
        return world;
    }
    if(*select == 13)
    {
        strcpy(world,"破大防了,家银们");
        return world;
    }
    if(*select == 14)
    {
        strcpy(world,"杀伤力不大,侮辱性极强");
        return world;
    }
    if(*select == 15)
    {
        strcpy(world,"啊,对对对对");
        return world;
    }
    if(*select == 16)
    {
        strcpy(world,"牛蛙兄弟");
        return world;
    }
    if(*select == 17)
    {
        strcpy(world,"人家想要贴贴");
        return world;
    }
    if(*select == 18)
    {
        strcpy(world,"普信女");
        return world;
    }
    if(*select == 19)
    {
        strcpy(world,"你个老六 ");
        return world;
    }
    if(*select == 20)
    {
        strcpy(world,"这里有老六");
        return world;
    }
    else
    {
        strcpy(world,"NO");
        return world; 
    }
}

//读线程
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)
                {
                    int select1;
                    system("clear");
                    sleep(1);
                    menu1();
                    printf("请输入编号:");
                    scanf("%d",&select1);
                    getchar();
                    char *world;
                    world = PhrasalVerbs(&select1);

                    if(strcmp(world,"NO") != 0)
                    {
                         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释放,否则会造成内存泄露  
                        break;                     
                    }
                    a2 =2;

                }
                if(a2 == 2)
                {
                    printf("请输入消息:\n");
                    memset(sendline,0,sizeof(sendline));
                    fgets(sendline,128,stdin);
                    strcpy(message.cmd,"PRIVATE");
                    printf("请输入对方的昵称:\n");
                    scanf("%s",message.id);
                    strcpy(message.name,message.myid);
                    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();
                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,"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);
                    strcpy(message.name,message.myid);
                    strcpy(message.cmd,"GROUP");
                    strcpy(message.msg,sendline);
                    send(sockfd,&message,sizeof(message),0);
                    break;
                }    
            }
            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;
        
        }
    }
}

void 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);
    }

    

    while(1)
    {
        //************
      
        memset(msg_text.buffer,0,sizeof(msg_text.buffer));
        //printf("buffer = %s\n",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);

}

 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 = 8888; //网络字节序端口
    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;
    }
    

    

    ret = pthread_create(&tid_read,NULL,(void *)read_thread,(void *)&sockfd);

    ret = pthread_create(&tid_write,NULL,(void *)write_thread,(void *)&sockfd);


    //阻塞等待回收指定线程
    pthread_join(tid_read,NULL);
    pthread_join(tid_write,NULL);


    close(sockfd);
   


    
    return 0;
}

makefile

run:client.c main.c
	gcc *.c -o run -lpthread 

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐