即时通讯系统————基于TCP协议的C/S架构

服务器端

#ifndef SERVER_H
#define SERVER_H

#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <string.h>
#include <sqlite3.h>
#include <pthread.h>
#include <signal.h>

#define SIZ   20
#define SIZE  10
#define SIZE1  20 //线程数
#define FAILURE  10000
#define SUCCESS  10001
#define FALSE    10002
#define TRUE     10003

struct sockaddr_in server_addr;//保存服务器信息

enum Enum
{
    REG,
    LOGIN,
    PRIVATE,
    GROUP
};
typedef enum Enum ENUM;
/* 定义一个结构体,含有用户的所有信息类型*/
struct user            
{
    char petname[32];    //昵称
    char age[32];
    char sex[32];
    char key[32];
    int cmd;            //用户指令
    int member;         //会员状态
    char buf[32];        //文件内容
};
typedef struct user User;

struct judge
{
    int cmd;            //用户命令
    char buf[32];        //文件名
    char petname[32];    //昵称
    char t2[128];        //发送消息的时间
};
typedef struct judge Judge;
struct info        
{
    int ToFd;            //要发送消息的套接字
    char petname[32];    //用户昵称
    char ptr[64];        //聊天内容
};
typedef struct info InFo;


int sockfdInit(int sockfd);
int show2(void *para, int columnCount, char **columnValue, char **columnName);
void send0(int fd);
void send1(int fd);
void sendlogin(int fd);
void sendLogin(int fd);
void sendlogin1(int fd);
int count(sqlite3 *pdb);

#endif
/**************************************************************
  > File Name: Server.c
  > Author: yangxuan
  > Mail: [email protected] 
  > Created Time: 2018年08月23日 星期四 23时23分18秒
 **************************************************************/

#include <stdio.h>
#include "Server.h"
#include <time.h>
#include <unistd.h>

//全局变量
char sql[128] = {0};  //存储数据库要用的命令字符串
sqlite3 *pdb;   //定义一个sqlite3指针, 指向数据库
int ret;              //接受函数调用后的返回值
int sockfd;           //定义一个文件描述符,基于ipv4,采用TCP协议的套接字
//char *data = NULL;
//int k = 0;
pthread_mutex_t mutex; //定义pthread_mutex_t 类型的锁,实现线程同步
pthread_cond_t con;    //当某些线程在工作时, 有必要让一些线程等待
char buf[32] = {0};    //定义一个buf数组,即时方便的向客户端传递信息
User RecvInfo;        //定义一个结构体变量
User T;
int Tofd;             //记下被邀请私聊好友的文件描述符
int fdon;             //记下私聊发起人的fd    
fd_set ReadFd, tmpfd;   //定义一个可读集合,中间变量集合
char ptr[32] = {0};   //记下私聊发起人的昵称
int j;                //用来计数连了多少客户端 j + 1
int fd[SIZE] = {0};
char reg[32] = {0};     //记下注册好的用户名
int grpfd = 0;          //记群聊发起人的fd
int S = 0;              //记群聊邀请了几个好友
int groupfd[SIZ];          //群聊最大人数
int receivefd;
int fdcancel;
int fdcheck;        //记下私聊记录查询用户的fd
int ID;         //用来记录聊天记录中的数据条数
int group;
int a[2];
int I;
char namegroup[32];
int FD;

int count1(void *para, int columnCount, char **columnValue, char **columnName)
{
    ID = atoi(*columnValue);
    printf("IDID %d\n", ID);
    return 0;
}

/*
	作者:杨宣
	函数原型: 回调函数:int show(void *para, int columnCount, char **columnValue, char **columnName)
	函数说明:参1:为调用函数传过来的参数, 参2: 为查询到的信息有几条  参3:查询到的结果  参4:查询结构的名称即类型
    函数功能:登录时查询该用户是否存在,不存在返回0, 存在返回-1
*/
int select1(void *para, int columnCount, char **columnValue, char **columnName)
{
    memset(buf, 0, sizeof(buf));
    char buf[32] = {0};
    strcpy(buf, columnValue[0]);

    if(strcmp(buf, RecvInfo.petname) == 0)  //将读取的用户名与数据库查询到的结果比较,若存在返回-1
    {
        return -1;
    }

    return 0;
}


/*
	作者:杨宣
	函数原型:回调函数:int show(void *para, int columnCount, char **columnValue, char **columnName)
	函数说明:参1:为调用函数传过来的参数, 参2: 为查询到的信息有几条  参3:查询到的结果  参4:查询结构的名称即类型
	函数功能:登录时查询该用户密码,正确返回-1, 不正确返回0
*/
int select2(void *para, int columnCount, char **columnValue, char **columnName)
{
    char buf[32] = {0};

    strcpy(buf, columnValue[0]);
    if(strcmp(buf, RecvInfo.key) == 0) //与数据库中查到的密码比较,相同则说明密码正确,返回-1
    {
        return -1;
    }

    return 0;
}

/*
	作者:杨宣
	函数原型: 回调函数:int show(void *para, int columnCount, char **columnValue, char **columnName)
	函数说明: 参1:为调用函数传过来的参数, 参2: 为查询到的信息有几条  参3:查询到的结果  参4:查询结构的名称即类型
    函数功能:登录时查询该用户是否在线,不在线返回-1, 在线返回0,
*/
int loginfd(void *para, int columnCount, char **columnValue, char **columnName)
{
    int a = *columnValue[0] - '0';       //char型数据转换成int型    取值减去0的ASCII码值

    if(a == 1)                      	//标志位为 1说明不在线,返回-1
    {
        return -1;
    }

    return 0;
}

int filecmd(void *para, int columnCount, char **columnValue, char **columnName)
{
    Judge J;
    J.cmd = 18;
    FILE *fp;
    int fd = *(int *)para;
    if(columnCount >= 0)
    {
        strcpy(J.petname, columnValue[0]);
        strcpy(J.buf, columnValue[2]);

        ret = send(fd, &J, sizeof(J), 0);
        if(ret == -1)
        {
            perror("send");
        }

        fp = fopen(J.buf, "r");
        if(fp == NULL)
        {
            printf("failure");
        }
        while(1)
        {
            memset(J.buf, 0, sizeof(J.buf));
            ret = fread(J.buf, 1, sizeof(J.buf) - 1, fp);
            if(ret == -1)
            {
                perror("fread failure");
            }
            if(ret == 0)
            {
                strcpy(J.buf, "SUCCESS");
                ret = send(fd, &J, sizeof(J), 0);
                if(ret == -1)
                {
                    perror("send");
                }
                break;
            }
            else
            {
                J.cmd = ret;
                ret = send(fd, &J, sizeof(J), 0);
                if(ret == -1)
                {
                    perror("send");
                }
            }
        }
        fclose(fp);
    }

    sprintf(sql, "update file set cmd = 2 where namerecv = '%s';", RecvInfo.petname);
    ret = sqlite3_exec(pdb, sql, NULL, NULL, NULL);
    if(ret != SQLITE_OK)
    {
        perror("update cmdfile");
    }
    return 0;
}

/*  
	作者:杨宣
	函数原型:void *pthread(void *arg)
	函数说明:带有一个void *型的参数,返回值为void *
	函数功能:处理用户登录请求  
*/
void *pthreadlogin(void *arg)
{
    pthread_detach(pthread_self()); //线程分离,线程运行完自动释放线程

    int fd = *(int *)arg;      		//取出文件描述符
    int ret;
    User U;
    U = RecvInfo;

    while(1)
    {
       memset(sql, 0, sizeof(sql)); 
        sprintf(sql, "select petname  from User where petname = '%s';", U.petname);    //查找数据库中是否存在该用户
        ret = sqlite3_exec(pdb, sql, select1, NULL, NULL);
        if(ret == SQLITE_OK)														//若不存在发送FALSE 给客户端
        {
            Judge J;
            strcpy(J.petname, U.petname);
            printf("12%s\n", U.petname);
            J.cmd = 2;
            strcpy(J.buf, "FALSE");
            ret = send(fd, &J, sizeof(J), 0);
            if(ret == -1)
            {
                perror("sendlogin1");
            }
            break;
        }
	
        else														
        {
            sprintf(sql, "select key from User where petname = '%s';", U.petname);  //若存在,再查找密码是否正确
        
        ret = sqlite3_exec(pdb, sql, select2, NULL, NULL);                       //若不正确,则发送FAILURE 给客户端
        if(ret == SQLITE_OK)
        {
            Judge J;
            strcpy(J.petname, U.petname);
            J.cmd = 2;
            strcpy(J.buf, "FAILURE");
            ret = send(fd, &J, sizeof(J), 0);
            if(ret == -1)
            {
                perror("sendlogin1");
            }
            break;
        }
	
        else
        {
            sprintf(sql, "select cmd from User where petname = '%s';", U.petname);  //若正确, 再判断该用户是否已在线
            ret = sqlite3_exec(pdb, sql, loginfd, NULL, NULL);
            if(ret == SQLITE_OK)									//若在线,发送TRUE给客户端
            {
                Judge J;
                strcpy(J.petname, U.petname);
                J.cmd = 2;
                strcpy(J.buf, "TRUE");
            
			    ret = send(fd, &J, sizeof(J), 0);
                if(ret == -1)
                {
                    perror("send");
                }
            }
    
            else
            {
            Judge J;
            strcpy(J.petname, U.petname);
            J.cmd = 2;
            strcpy(J.buf, "SUCCESS");						//各种情况排除完毕, 发送SUCCESS给客户端
        
		    ret = send(fd, &J, sizeof(J), 0);
            if(ret == -1)
            {
                perror("sendlogin1");
            }
            T = U;

            sprintf(sql, "update User set cmd = '2' where petname = '%s';", U.petname);   //更新数据库用户在线状态
            ret = sqlite3_exec(pdb, sql, NULL, NULL, NULL);
            if(ret != SQLITE_OK)
            {
                perror("update");
            }
		
            sprintf(sql, "update User set fd = '%d' where petname = '%s';", fd,  U.petname);   //更新用户登录的客户端fd
            ret = sqlite3_exec(pdb, sql, NULL, NULL, NULL);
            if(ret != SQLITE_OK)
            {
                perror("update");
            }

            sprintf(sql, "select * from file where cmd = 1 and  namerecv = '%s';", U.petname);
            ret = sqlite3_exec(pdb, sql, filecmd, &fd, NULL);
            if(ret != SQLITE_OK)
            {
                perror("filecmd");
            }

            }
		
            break;
        }
     }
    }
	
    return NULL;
}


/*
	作者:杨宣
	函数原型: 回调函数:int show(void *para, int columnCount, char **columnValue, char **columnName)
	函数说明:参1:为调用函数传过来的参数, 参2: 为查询到的信息有几条  参3:查询到的结果  参4:查询结构的名称即类型
	函数功能:用户注册时查找数据库中是否有同名用户,存在返回-1, 不存在返回0
*/
int show(void *para, int columnCount, char **columnValue, char **columnName)
{
    Judge J;
    J.cmd = 1;
    int fd = *(int *)para;
	
    if(columnCount >= 1)
    {
        strcpy(J.buf, "FAILURE");        //若用户名已存在, 发送FAILURE给客户端
        send(fd, &J, sizeof(J), 0);
        if(ret == -1)
        {
            perror("用户名已存在!\n");
        }
        return -1;
    }
    return 0;
}


/* 
	作者:杨宣
	指针函数, 函数名pthreadHandle, 含有一个void *的参数, 返回指为void * 
	函数说明:循环中不断接受文件描述符的信息, 并将其存到结构体中,接收一个将其写入数据库的表中
	函数功能:处理用户注册请求
 */
void *pthreadHandle(void *arg)
{
    int fd = *(int *)arg;                     //将主函数的传过来的文件描述符赋给变量

    pthread_detach(pthread_self());           //线程分离
	
    sprintf(sql, "select petname  from User where petname = '%s';", RecvInfo.petname);   //查询数据库中是否存在该用户 
    ret = sqlite3_exec(pdb, sql, show, &fd, NULL);
    if(ret != SQLITE_OK)
    {
         pthread_mutex_unlock(&mutex);
        //printf("select %s\n", sqlite3_errmsg(pdb));
    }

   else			//若不存在,将用户信息插入数据库
   {
       strcpy(reg, RecvInfo.petname);
	   sprintf(sql, "insert into User (petname, age, sex, key, cmd, fd, member) values ('%s', '%s', '%s', '%s', '%d', '%d', '%d');", RecvInfo.petname, RecvInfo.age, RecvInfo.sex, RecvInfo.key,RecvInfo.cmd, fd, 0);

        ret = sqlite3_exec(pdb, sql, NULL, NULL, NULL);
        if(ret != SQLITE_OK)
        {
            perror("sqlite_exec");
        }
      
        send0(fd);   //并调用send0()函数发送 SUCCESS给客户端
        memset(&RecvInfo, 0, sizeof(RecvInfo));  //读完后需对结构体进行清空,以便下次再读 
   }
          
    return NULL;    
}


/*
	作者:杨宣
	函数原型: 回调函数:int show(void *para, int columnCount, char **columnValue, char **columnName)
	函数说明:参1:为调用函数传过来的参数, 参2: 为查询到的信息有几条  参3:查询到的结果  参4:查询结构的名称即类型  
	函数功能:登录时显示在线好友,无好友在线时返回0, 有一个或多个好友在线时返回好友名
  
*/
int exhibit(void *para, int columnCount, char **columnValue, char **columnName)
{
    int fd = *(int *)para;
    int i;
    Judge J;
  
    for(i = 0; i < columnCount; i++)  //将查到的用户名发送给客户端
    {
        memset(&J, 0, sizeof(J));
        J.cmd = 3;
        strcpy(J.buf, columnValue[i]);
        if(strcmp(J.buf, RecvInfo.petname) != 0)
        {
        printf("buf %s\n", J.buf);
        printf("Recv %s\n", RecvInfo.petname);
        ret = send(fd, &J, sizeof(J), 0);
        /*if(ret == -1)    //else与最近的未配对的if相匹配,得注掉
        {
            perror("sendp");
        }*/
        }
        else
        {
            continue;
        }
    }

    return 0;                         //回调函数return 0不可以少
}


/*
	作者:杨宣
	函数原型:void *pthreadshow(void *arg)
	函数行参  : 文件描述符  代表连接的客户端
	函数功能:处理客户端显示当前在线好友请求
*/
void *pthreadshow(void *arg)
{
    int fd = *(int *)arg;
    char sql[64] = {0};
    Judge J;

    sprintf(sql, "select petname from User where cmd = '2';");   //采用数据库回调函数,找出cmd 状态为2即登录状态的用户
    ret = sqlite3_exec(pdb, sql, exhibit, &fd, NULL);
    if(ret != SQLITE_OK)
    {
      perror("sqlite3show");
    }

    J.cmd = 3;
    strcpy(J.buf, "SUCCESS"); 

    ret = send(fd, &J, sizeof(J), 0);
    if(-1 == ret)
    {
        perror("send");
    }

    return NULL;   // void不应该没有返回?为何会报错控制流程到结尾
}

char dtr[32] = {0};

/*
	作者:杨宣
	函数原型:void *pthreadexit(void *arg)
	函数说明:行参  : 文件描述符  代表连接的客户端
	函数功能:处理客户端退出登录请求
*/
void *pthreadexit(void *arg)
{
    int fd = *(int *)arg;
    Judge J;
printf("exit = %s\n", RecvInfo.petname);	
    sprintf(sql, "update User set cmd = '1' where petname = '%s';", RecvInfo.petname);  //更新客户端用户登录状态
    ret = sqlite3_exec(pdb, sql, NULL, NULL, NULL);
    if(SQLITE_OK != ret)
    {
        J.cmd = 11;
        strcpy(J.buf, "FAILURE");
        ret = send(fd, &J, sizeof(J), 0);
        if(ret == -1)
        {
            perror("send");
        }

        memset(&J, 0, sizeof(J));
        perror("sqlite3exit");
    }
	
    else
    {
        J.cmd = 11;
        strcpy(J.buf, "SUCCESS");
        ret = send(fd, &J, sizeof(J), 0);
        if(ret == -1)
        {
            perror("send");
        }
        memset(&J, 0, sizeof(J));
		/*close(fd);
		FD_CLR(fd, &ReadFd);   //从集合里面清除
		fd= 0;*/  
    }

    sprintf(sql, "update User set fd = '0' where petname = '%s';", RecvInfo.petname);  //更新客户端设备的文件描述符号
    ret = sqlite3_exec(pdb, sql, NULL, NULL, NULL);
    if(ret != SQLITE_OK)
    {
        perror("sqlite3_exec");
    }

    return NULL;
}

/*
	作者:杨宣
	函数原型: 回调函数:int show(void *para, int columnCount, char **columnValue, char **columnName)
	函数说明:参1:为调用函数传过来的参数, 参2: 为查询到的信息有几条  参3:查询到的结果  参4:查询结构的名称即类型
	函数功能:查找被邀请好友的fd
 */
int privatefd(void *para, int columnCount, char **columnValue, char **columnName)
{
    Judge J;
    User U;
    int fd = *(int *)para;  //私聊发起用户的fd
    Tofd = *columnValue[0] - '0';       //被邀请好友的fd, 全局变量

    strcpy(J.petname, RecvInfo.buf);
    strcpy(J.buf, RecvInfo.age);
    J.cmd = 12;

    ret = send(Tofd, &J, sizeof(J), 0);
    if(ret  == -1)
    {
        perror("send");
    }
    time_t t1 = time(NULL);
    char *t2 = ctime(&t1);

    memset(sql, 0, sizeof(sql));
        sprintf(sql, "select count(*) from chat;");
        ret = sqlite3_exec(pdb, sql, count1, NULL, NULL);
        ID++;
        sprintf(sql, "insert into Chat (id, petname, Tofd, ptr, time) values ('%d', '%s', '%d', '%s', '%s');", ID, RecvInfo.petname, Tofd, RecvInfo.buf, t2);
    ret = sqlite3_exec(pdb, sql, NULL, NULL, NULL);
    if(ret != SQLITE_OK)
    {
        perror("chatinsert");
    }

    memset(&J, 0, sizeof(J));
    strcpy(J.buf, "SUCCESS");
    J.cmd = 4;
    ret = send(fd, &J, sizeof(J), 0);
    if(ret == -1)
    {
        perror("send");
    }
    /*strcpy(J.buf, "SUCCESS");
    J.cmd = 12; 
    strcpy(J.petname, ptr);
    printf("2e33\n");
    ret = send(Tofd, &J, sizeof(J), 0);
    if(ret == -1)
    {
        perror("invite");
    }

    memset(&U, 0, sizeof(U));           //接受好友的验证结果
    ret = recv(Tofd, &U, sizeof(U), 0);
    if(ret == -1)
    {
        perror("receive");
    }

    if(strcmp(U.buf, "y") == 0)     //比较结果,并发送给邀请用户
    {
        memset(&J, 0, sizeof(J));
        strcpy(J.buf, "对方已接受你的邀请");
        J.cmd = 4;
        strcpy(dtr, RecvInfo.petname);
        strcpy(J.petname ,T.petname);
        ret = send(fd, &J, sizeof(J), 0);
        if(ret == -1)
        {
            perror("receive");
        }
    }

    else
    {
        memset(&J, 0, sizeof(J));
        strcpy(J.buf, "对方拒绝你的聊天邀请");
        J.cmd = 4;

        ret = send(fd, &J, sizeof(J), 0);
        if(ret == -1)
        {
            perror("receive");
        }

    }*/

    return 0;
}


/*
	作者:杨宣
	函数原型: 回调函数:int show(void *para, int columnCount, char **columnValue, char **columnName)
	函数说明:参1:为调用函数传过来的参数, 参2: 为查询到的信息有几条  参3:查询到的结果  参4:查询结构的名称即类型
	函数功能:查看被邀请私聊的好友是否在线,
 */
int privateon(void *para, int columnCount, char **columnValue, char **columnName)
{
    int fd = *(int *)para;
    if(columnCount >= 1)    //若在线返回非 0 值
    {
    sprintf(sql, "select fd from User where petname = '%s' and cmd = '2';", RecvInfo.petname);
    ret = sqlite3_exec(pdb, sql, privatefd, &fd, NULL);
    if(ret != SQLITE_OK)
    {
        perror("private");
    }

    
    return -1;

    }

    return 0;
}

/*
	作者:杨宣
	函数原型: 回调函数:int show(void *para, int columnCount, char **columnValue, char **columnName)
	函数说明:参1:为调用函数传过来的参数, 参2: 为查询到的信息有几条  参3:查询到的结果  参4:查询结构的名称即类型
	函数功能:查看被邀请私聊的好友是否存在,
 */
int private(void *para, int columnCount, char **columnValue, char **columnName)
{
    int fd = *(int *)para;
    if(columnCount >= 1)    //存在再查看是否在线
    {
    sprintf(sql, "select petname from User where petname = '%s'and cmd = '2';", RecvInfo.petname);
    ret = sqlite3_exec(pdb, sql, privateon, &fd, NULL);
    printf("ret2 = %d\n", ret);
    if(ret != SQLITE_OK)
    {
        perror("2private");
    }

    else
    {
        Judge J;
        J.cmd = 4;
    strcpy(J.buf, "FAILURE");
        printf("bufoo %s\n", buf);
    ret = send(fd, &J, sizeof(J), 0);
    if(ret == -1)
    {
        perror("send not exist");
    }
    return -1;
    }
    }

    return 0;
}

/*
	作者:杨宣
	函数原型: 回调函数:int show(void *para, int columnCount, char **columnValue, char **columnName)
	函数说明:参1:为调用函数传过来的参数, 参2: 为查询到的信息有几条  参3:查询到的结果  参4:查询结构的名称即类型
 	函数功能:全局变量记下发起私聊的fd
 */
int private1(void *para, int columnCount, char **columnValue, char **columnName)
{
    fdon = *columnValue[0] - '0';
    return 0;
}

/*
   作者:杨宣
   函数原型:void *pthreadprivate(void *arg)
   函数说明:线程处理函数,case 4中起的线程,行参  :文件描述符  代表连接的客户端
   函数功能:处理客户端私聊请求
*/
void *pthreadprivate(void *arg)
{
    int ret;
    int fd = *(int *)arg;

    strcpy(ptr, RecvInfo.buf);      //全局变量记下发起邀请好友的fd
    memset(sql, 0, sizeof(sql));
    sprintf(sql, "select fd from User where petname = '%s'", RecvInfo.buf);//查找发起私聊的fd
    ret = sqlite3_exec(pdb, sql, private1, &fd, NULL);
    if(ret != SQLITE_OK)
    {
        perror("private");
    }

    memset(sql, 0, sizeof(sql));
    sprintf(sql, "select petname from User where petname = '%s';", RecvInfo.petname);//查看邀请的好友是否存在
    ret = sqlite3_exec(pdb, sql, private, &fd, NULL);
    if(ret != SQLITE_OK)
    {
        perror("private");
    }

    else        //若不存在,发送FALSE 给客户端
    {
        Judge J;
        J.cmd = 4; 
    strcpy(J.buf, "FALSE");

    ret = send(fd, &J, sizeof(J), 0);
    if(ret == -1)
    {
        perror("sendprivate");
    }
    }

    return NULL;
}

/*
   作者:杨宣
   函数原型:void *pthreadmember(void *arg)
   函数说明:线程处理函数,充值会员,行参  : 文件描述符  代表连接的客户端
   函数功能:处理客户端充值会员请求
*/
void *pthreadmember(void *arg)
{
    Judge J;
    int fd = *(int *)arg;
	
    memset(sql, 0, sizeof(sql));
    sprintf(sql, "update User set member = '%d' where petname = '%s';", RecvInfo.member, RecvInfo.petname);  //更新用户会员信息
    ret = sqlite3_exec(pdb, sql, NULL, NULL, NULL);
    if(ret != SQLITE_OK)    //充值失败
    {
        J.cmd = 9;
        memset(&J, 0, sizeof(J));
        strcpy(J.buf, "FAILURE");

        ret = send(fd, &J, sizeof(J), 0);
        if(ret == -1)
        {
            perror("sendfailure");
        }
    }

    else
    {
        memset(&J, 0, sizeof(J));
        J.cmd = 9;
        strcpy(J.buf, "SUCCESS");       //充值成功

        ret = send(fd, &J, sizeof(J), 0);
        if(ret == -1)
        {
            perror("FAILUREMEm");
        }
    }

    return NULL;
}


/*
	作者:杨宣
	函数原型: 回调函数:int show(void *para, int columnCount, char **columnValue, char **columnName)
	函数说明:参1:为调用函数传过来的参数, 参2: 为查询到的信息有几条  参3:查询到的结果  参4:查询结构的名称即类型
	函数功能:发送点赞信息给好友
 */
int praise(void *para, int cloumnCount, char **columnValue, char **columnName)
{
    Judge J;
    Tofd = *columnValue[0] - '0';
    strcpy(J.buf, "有好友给你点赞");
    J.cmd = 13;

    ret = send(Tofd, &J, sizeof(J), 0);
    if(ret == -1)
    {
        perror("praise");
    }

    return 0;
}

/*
   作者:杨宣
   函数原型:void *pthreadpraise(void *arg)
   函数说明:线程处理函数,点赞好友,,行参  : 文件描述符  代表连接的客户端
   函数功能:处理客户端点赞好友请求
*/
void *pthreadpraise(void *arg)
{
    int fd = *(int *)arg;
    memset(sql, 0, sizeof(sql));

    sprintf(sql, "select fd from User where petname = '%s';", RecvInfo.petname);//找出要点赞好友的文件描述符
    ret = sqlite3_exec(pdb, sql, praise, NULL, NULL);
    if(ret != SQLITE_OK)
    {
        perror("praise");
    }

    return NULL;
}

/*
   作者:杨宣
   函数原型:void *pthreadactive(void *arg)
   函数说明:线程处理函数,主动发起私聊的发送线程,行参  : 文件描述符  代表连接的客户端
   函数功能:不断发送主动发起私聊的用户的信息
*/
void *pthreadactive(void *arg)
{
    int fd = *(int *) arg;
    time_t t1 = time(NULL);
    char *t2 = ctime(&t1);

    Judge J;
    J.cmd = 12;
    strcpy(J.buf, RecvInfo.buf); //将用户发送消息拷贝到结构体 J 中
    strcpy(J.petname, RecvInfo.petname);

    ret = send(Tofd, &J, sizeof(J), 0);
    if(ret == -1)
    {
        perror("sendactive");
    }
    memset(sql, 0, sizeof(sql));

    if(strcmp(J.buf, "bye") != 0)
    {
        //ID = count(pdb);
        sprintf(sql, "select count(*) from chat;");
        ret = sqlite3_exec(pdb, sql, count1, NULL, NULL);
        ID++;
    sprintf(sql, "insert into Chat (id, petname, Tofd, ptr, time) values ('%d','%s',  '%d', '%s', '%s');",ID,  ptr, fdon, J.buf, t2);     //将聊天记录插入到表中
    ret = sqlite3_exec(pdb, sql, NULL, NULL, NULL);
    if(ret != SQLITE_OK)
    {
        perror("charinsert");
    }
    }

    return NULL;
}


/*
   作者:杨宣
   函数原型:void *pthreadnegtive(void *arg)
   函数说明:线程处理函数,发送被邀请私聊用户的消息,行参  : 文件描述符  代表连接的客户端
   函数功能:不断发送被邀请私聊好友的消息
*/
void *pthreadnegtive(void *arg)
{
    //int fd = *(int *)arg;
    Judge J;
    J.cmd = 4;
    strcpy(J.buf, RecvInfo.buf);
    strcpy(J.petname, RecvInfo.petname);

    time_t t1 = time(NULL);
    char *t2 = ctime(&t1);

    ret = send(fdon, &J, sizeof(J), 0);
    if(ret == -1)
    {
        perror("sendnegtive");
    }

    memset(sql, 0, sizeof(sql));

    if(strcmp(J.buf, "bye") != 0)
    {
        sprintf(sql, "select count(*) from chat;");
        ret = sqlite3_exec(pdb, sql, count1, NULL, NULL);
        ID++;
        sprintf(sql, "insert into Chat (id, petname, Tofd, ptr, time) values ('%d', '%s', '%d', '%s', '%s');", ID, RecvInfo.petname, Tofd, RecvInfo.buf, t2);
    ret = sqlite3_exec(pdb, sql, NULL, NULL, NULL);
    if(ret != SQLITE_OK)
    {
        perror("chatinsert");
    }
    }

    return NULL;
}

/*
	作者:杨宣
	函数原型: 回调函数:int show(void *para, int columnCount, char **columnValue, char **columnName)
	函数说明:参1:为调用函数传过来的参数, 参2: 为查询到的信息有几条  参3:查询到的结果  参4:查询结构的名称即类型
	函数功能:处理客户端主动发起的私聊请求,验证被邀请好友是否愿意私聊
 */
int checkchat(void *para, int columnCount, char **columnValue, char **columnName)
{
    Judge J;
    int i;
    fdcheck = *(int *)para;
    //for(i = 0; i < columnCount - 1; i++)
   
        strcpy(J.petname, columnValue[1]);
        strcpy(J.buf, columnValue[3]);
        strcpy(J.t2, columnValue[4]);
        J.cmd = 7;
        ret = send(fdcheck, &J, sizeof(J), 0);
        if(ret == -1)
        {
            perror("sengchat");
        }
        memset(&J, 0, sizeof(J));
        /*sprintf(sql, "select petname from Chat where ptr = '%s';", columnValue[i]);
        ret = sqlite3_exec(pdb, sql, checkname, columnValue[i], NULL);
        if(ret != SQLITE_OK)
        {
            perror("petname");
        }*/
    return 0;
}


/*
	作者:杨宣
	函数原型:void *pthreadcheck(void *arg)
    函数说明:线程处理函数,查看私聊聊天记录,行参  : 文件描述符  代表连接的客户端
    函数功能:处理客户端想要查看聊天记录的请求
*/
void *pthreadcheck(void *arg)
{
    int fd = *(int *)arg;
    Judge J;
    sprintf(sql, "select * from Chat where petname = '%s' or petname = '%s';", RecvInfo.petname, RecvInfo.buf);
    ret = sqlite3_exec(pdb, sql, checkchat, &fd, NULL);
    if(ret != SQLITE_OK)
    {
        perror("checkchat");
    }

        J.cmd = 7;
        strcpy(J.buf, "SUCCESS");       //发送完毕
        ret = send(fdcheck, &J, sizeof(J), 0);
        if(ret == -1)
        {
            perror("sengchat");
        }
    /*ret = sqlite3_exec(pdb, sql, checkchat2, &fd, NULL);
    if(ret != SQLITE_OK)
    {
        perror("checkchat");
    }*/
    return NULL;
}

/*
	作者:杨宣
	函数原型:void *keyprotest(void *arg)
    函数说明:线程处理函数,密码保护,行参  : 文件描述符  代表连接的客户端
    函数功能:处理客户端设置密保请求
*/
void *keyprotest(void *arg)
{
    int fd = *(int *)arg;
    Judge J;

    sprintf(sql, "insert into protest(petname, que1, que2, que3) values ('%s', '%s', '%s', '%s');",reg, RecvInfo.age, RecvInfo.sex, RecvInfo.key); //将密保答案存入表中

    ret = sqlite3_exec(pdb, sql, NULL, NULL, NULL);
    if(ret != SQLITE_OK)
    {
    J.cmd = 14;
    strcpy(J.buf, "FAILURE");
    ret = send(fd, &J, sizeof(J), 0);
    if(ret == -1)
    {
        perror("senfkey");
    }
        perror("keyprotest");
    }

    else
    {
        J.cmd = 14;
        strcpy(J.buf, "SUCCESS");
        ret = send(fd, &J, sizeof(J), 0);
        if(ret == -1)
        {
            perror("senfkey");
        }
    }

    return NULL;
}

/*
	作者:杨宣
	函数原型:void StopClient(int num)
    函数说明:自定义函数:正常关闭客户端
    函数功能:当服务器捕捉到 ctrl + c信号,挨个发送BYE 给客户端,让客户端正常关闭自身
 */
void StopClient(int num)  //进程收到SIGINT信号,退出, num 表述信号值,只能有着一个参数
{
    sprintf(sql, "update User set cmd = '1';");
    ret = sqlite3_exec(pdb, sql, NULL, NULL, NULL);
    if(ret != SQLITE_OK)
    {
        perror("setcmd1");
    }
    Judge J;
    strcpy(J.buf, "BYE");
    while(j >= 0)
    {
    ret = send(fd[j], &J, sizeof(J), 0);
    if(ret == -1)
    {
        perror("senfstop");
    }
    j--;
    close(fd[j]);
    }

    close(sockfd);
}

/*
	作者:杨宣
	函数原型: 回调函数:int show(void *para, int columnCount, char **columnValue, char **columnName)
	函数说明:参1:为调用函数传过来的参数, 参2: 为查询到的信息有几条  参3:查询到的结果  参4:查询结构的名称即类型
	函数功能:挨个比较每一问题答案
 */
int keyfind(void *para, int columnCount, char **columnValue, char **columnName)
{
            if(strcmp(RecvInfo.age, columnValue[1]) != 0)
                {
                     return -1;
                }
            else
            {
                if(strcmp(RecvInfo.sex, columnValue[2]) == 0)
                {
                    if(strcmp(RecvInfo.key, columnValue[3]) == 0)
                    {
                        printf("******\n");
                        return 0;
                    }
                    else
                    {
                        return -1;
                    }
                }
                else
                {
                    return -1;
                }
            }

}

/*
   作者:杨宣
   函数原型:void *findkey(void *arg)
   函数说明:线程处理函数:重设密码, 行参  : 文件描述符  代表连接的客户端
   函数功能:处理客户端核对密保问题请求
*/
void *findkey(void *arg)
{
    int fd = *(int *)arg;
    memset(sql, 0, sizeof(sql));
    sprintf(sql, "select *from protest where petname = '%s';", RecvInfo.petname);//找出表中所以问题的答案

    ret = sqlite3_exec(pdb, sql, keyfind, NULL, NULL);
    if(ret != SQLITE_OK)        //若有一个不正确,就发送FAILURE给客户端
    {
        Judge J;
        J.cmd = 15;
        strcpy(J.buf, "FAILURE");

        ret = send(fd, &J, sizeof(J), 0);
        if(ret == -1)
        {
            perror("send");
        }
    }

    else
    {
        Judge J;
        J.cmd = 15;
        strcpy(J.buf, "FALSE");     //回答正确,则发送 FALSE给客户端

        ret = send(fd, &J, sizeof(J), 0);
        if(ret == -1)
        {
            perror("send");
        }

    }

    return NULL;
}

/*
   作者:杨宣
   函数原型:void *correctkey(void *arg)
   函数说明:线程处理函数,重设密码,行参  : 文件描述符  代表连接的客户端
   函数功能:处理客户端重新设置密码请求
*/
void *correctkey(void *arg)
{
    int fd = *(int *)arg;;

    sprintf(sql, "update User set key = '%s' where petname = '%s';", RecvInfo.key, RecvInfo.petname);       //更新用户表中密码信息
    ret = sqlite3_exec(pdb, sql, NULL, NULL, NULL);
    if(ret == SQLITE_OK)        //正常执行的话,发送SUCCESS给客户端
    {
        Judge J;
        J.cmd = 16;
        strcpy(J.buf, "SUCCESS");

        ret = send(fd, &J, sizeof(J), 0);
        if(ret == -1)
        {
            perror("sendcorrect");
        }
    }

    else
    {
        Judge J;
        J.cmd = 16;
        strcpy(J.buf, "FAILURE");

        ret = send(fd, &J, sizeof(J), 0);
        if(ret == -1)
        {
            perror("sendcorrect");
        }

    }

    return NULL;
}

/*
	作者:杨宣
	函数原型: 回调函数:int show(void *para, int columnCount, char **columnValue, char **columnName)
	函数说明:参1:为调用函数传过来的参数, 参2: 为查询到的信息有几条  参3:查询到的结果  参4:查询结构的名称即类型
	函数功能:查看群聊邀请好友是否在线
*/
int groupcmd(void *para, int columnCount, char **columnValue, char **columnName)
{
    int a = *columnValue[0] - '0';      //若状态 为 1说明不在线, 返回非 0 值 
    if(a == 1)
    {
        return -1;
    }

    return 0;
}

/*
	作者:杨宣
	函数原型: 回调函数:int show(void *para, int columnCount, char **columnValue, char **columnName)
	函数说明:参1:为调用函数传过来的参数, 参2: 为查询到的信息有几条  参3:查询到的结果  参4:查询结构的名称即类型
	函数功能:查找被邀请好友的fd
*/
int fdgroup(void *para, int columnCount, char **columnValue, char **columnName)
{
    int fd = *columnValue[0] - '0';
    int i;

    Judge J;
    J.cmd = 10;
    strcpy(J.petname, RecvInfo.petname); //发送询问信息给被邀请好友(某某 邀请你进行群聊)
    strcpy(J.buf, RecvInfo.age);
    ret = send(fd, &J, sizeof(J), 0);
    if(ret == -1)
    {
        perror("groupfd");
    }

    User U;
    ret = recv(fd, &U, sizeof(U), 0);//接收询问结果
    if(ret == -1)
    {
        perror("recv");
    }

    if(strcmp(U.sex, "y") == 0)
    {
        sprintf(sql, "select count(*) from chat;");
        ret = sqlite3_exec(pdb, sql, count1, NULL, NULL);

        //ID = count(pdb);
        ID++;
        strcpy(namegroup, RecvInfo.sex);
        sprintf(sql, "insert into Chat (id, petname) values ('%d', '%s');", ID, RecvInfo.sex);       //将聊天信息存入表中
        ret = sqlite3_exec(pdb, sql, NULL, NULL, NULL);
        if(ret != SQLITE_OK)
        {
            perror("Insertgroup");
        }
        groupfd[S] = fd;        //若同意群聊,记下被邀请好友的fd, 并且用户数加 1
        S++;

        Judge J;
        strcpy(J.buf, "SUCCESS");   //发送邀请成功信息给邀请人
        strcpy(J.petname, RecvInfo.petname);
        J.cmd = 5;

        ret = send(grpfd, &J, sizeof(J), 0);
        if(ret == -1)
        {
            perror("send1");
        }

        J.cmd = 17;   //每一位在此用户进入群聊之前的用户,都可看到该用户进群的消息
        for(i = 0; i < S; i++)
        {
            if(groupfd[i] != fd) //该用户自身无须接收
            {
                ret = send(groupfd[i], &J, sizeof(J), 0);
                if(ret == -1)
                {
                    perror("send");
                }
            }
        }
    }

        else
        {
        Judge J;
        strcpy(J.buf, "FAILURE");   //若用户拒绝,则发送FAILURE 给客户端,提示邀请失败
        strcpy(J.petname, RecvInfo.petname);
        J.cmd = 5;

        ret = send(grpfd, &J, sizeof(J), 0);
        if(ret == -1)
        {
            perror("send1");
        }
        }

    return 0;
}


/*
	作者:杨宣
	函数原型:void *groupchat(void *arg)
    函数说明:线程处理函数,群聊,行参  : 文件描述符  代表连接的客户端
    函数功能:处理客户端发起群聊的请求
*/
void *groupchat(void *arg)
{
    int fd = *(int *)arg;

    grpfd = fd;             //将群聊发起人的fd 用全局变量记下

    sprintf(sql, "select cmd from User where petname = '%s';", RecvInfo.petname);//查看邀请的好友是否在线
    ret = sqlite3_exec(pdb, sql, groupcmd, NULL, NULL);
    if(ret != SQLITE_OK)        //用户不在线,发送FALSE 给邀请人
    {
        Judge J;
        strcpy(J.buf, "FALSE");
        J.cmd = 5;
        strcpy(J.petname, RecvInfo.petname);

        ret = send(fd, &J, sizeof(J), 0);
        if(ret == -1)
        {
            perror("groupcmd");
        }

    }

    else
    {
        sprintf(sql, "select fd from User where petname = '%s';", RecvInfo.petname);//若在线,找出好友的fd
        ret = sqlite3_exec(pdb, sql, fdgroup, NULL, NULL);
        if(ret != SQLITE_OK)
        {     
            perror("qroupfd");
        }
    }

    return NULL;
}


/*
	作者:杨宣
	函数原型: 回调函数:int show(void *para, int columnCount, char **columnValue, char **columnName)
	函数说明:参1:为调用函数传过来的参数, 参2: 为查询到的信息有几条  参3:查询到的结果  参4:查询结构的名称即类型
    函数功能: 查看发言好友的fd
 */
int fdreceive(void *para, int columnCount, char **columnValue, char **columnName)
{
    receivefd = *columnValue[0] - '0';  //用全局变量记下发言好友的fd
    return 0;
}

/*
   线程处理函数,发送群聊信息,
  行参  : 文件描述符  代表连接的客户端
  功能:不断发送群聊用户聊天信息
*/
void *receivegroup(void *arg)
{
    int i;
    Judge J;
    int fd = *(int *)arg;
    int flag, j;

    sprintf(sql, "select fd from User where petname = '%s';", RecvInfo.petname);//找出发言用户的fd
    ret = sqlite3_exec(pdb, sql, fdreceive, NULL, NULL);
    if(ret != SQLITE_OK)
    {     
        perror("qroupfd");
    }

    if(strcmp(RecvInfo.age, "e") == 0 || strcmp(RecvInfo.age, "bye") == 0)//若用户收到bye 或 e,说明有用户退出群聊
    {
        for(i = 0; i < S; i++)  //找出该用户fd在集合中的位置
        {
            if(groupfd[i] == fd)
            {
                flag = i;
                break;
            }
        }

        if(flag < S -1)     //将该用户的fd从集合中删除,若不为最后一个
        {
            for(j = 0; j < S - flag - 1; j++)
            {
                groupfd[flag + j] = groupfd[flag + 1 + j];
            }
            S--;            //用户数减 1
        }
    
        else        //若位于集合最后一位,直接将用户数减 1
        {
            S--;
        }

        if(S == 0)          //全部用户退出群聊
        {
            sprintf(sql, "select count(*) from chat;");
            ret = sqlite3_exec(pdb, sql, count1, NULL, NULL);
            ID++;
            printf("ID %d\n", ID);
            sprintf(sql, "insert into chat (id, petname) values ('%d', '%s');", ID, namegroup);
            ret = sqlite3_exec(pdb, sql, NULL, NULL, NULL);
            if(ret != SQLITE_OK)
            {
                perror("sqlite");
            }
        }
    }
        else
        {
            time_t t1 = time(NULL);
            char *t2 = ctime(&t1);
            sprintf(sql, "select count(*) from chat;");
            ret = sqlite3_exec(pdb, sql, count1, NULL, NULL);
            //ID = count(pdb);
            ID++;
    printf("ID %d\n", ID);

            sprintf(sql, "insert into Chat (id, petname, Tofd, ptr, time ) values ('%d', '%s', '%d', '%s', '%s');", ID, RecvInfo.petname, groupfd[S], RecvInfo.age, t2);       //将聊天信息存入表中
            ret = sqlite3_exec(pdb, sql, NULL, NULL, NULL);
            if(ret != SQLITE_OK)
            {
                perror("Insertgroup");
            }
        }

    

    for(i = 0; i < S; i++)      //将发言信息发给每一位群聊用户
    {
        if(groupfd[i] != fd)
        {
            J.cmd = 17;
            strcpy(J.buf, RecvInfo.age);
            strcpy(J.petname, RecvInfo.petname);

            ret = send(groupfd[i], &J, sizeof(J), 0);
            if(ret == -1)
            {
                perror("send");
            }
        }
    }

    J.cmd = 5;

    strcpy(J.buf, RecvInfo.age);
    strcpy(J.petname, RecvInfo.petname);

    ret = send(grpfd, &J, sizeof(J), 0);
    if(ret == -1)
    {
        perror("send");
    }
    
    
    return 0;
}

/*
	作者:杨宣
	函数原型:void *activegroup(void *arg)
    函数说明:线程处理函数,发送群聊信息,行参  : 文件描述符  代表连接的客户端
    函数功能:不断发送主动发起聊天的用户聊天信息
*/
void *activegroup(void *arg)
{

    int i;
    Judge J;
    J.cmd = 17;

    for(i = 0; i < S; i++)      //发给每一个群聊用户
    {
        strcpy(J.buf, RecvInfo.age);
        strcpy(J.petname, RecvInfo.petname);

        ret = send(groupfd[i], &J, sizeof(J), 0);
        if(ret == -1)
        {
            perror("send");
        }
    }

    if(strcmp(J.buf, "e") == 0 || strcmp(J.buf, "bye") == 0)
    {
        //ID = count(pdb);
        sprintf(sql, "select count(*) from chat;");
        ret = sqlite3_exec(pdb, sql, count1, NULL, NULL);
        ID++;
    printf("ID %d\n", ID);
        sprintf(sql, "insert into chat (id, petname) values ('%d','%s');", ID, namegroup);
        ret = sqlite3_exec(pdb, sql, NULL, NULL, NULL);
        if(ret != SQLITE_OK)
        {
            perror("sqlite");
        }
    }
    else
    {

    time_t t1 = time(NULL);
    char *t2 = ctime(&t1);
   // ID = count(pdb);
        sprintf(sql, "select count(*) from chat;");
        ret = sqlite3_exec(pdb, sql, count1, NULL, NULL);
    ID++;
    printf("ID1 %d\n", ID);

    sprintf(sql, "insert into Chat (id, petname, Tofd, ptr, time) values ('%d','%s', '%d', '%s', '%s');", ID, RecvInfo.petname, groupfd[S], RecvInfo.age, t2);       //将聊天信息存入表中
    ret = sqlite3_exec(pdb, sql, NULL, NULL, NULL);
    if(ret != SQLITE_OK)
    {
        perror("Insertgroup");
    }
    }

    return NULL;
}


/*
	作者:杨宣
	函数原型: 回调函数:int show(void *para, int columnCount, char **columnValue, char **columnName)
	函数说明:参1:为调用函数传过来的参数, 参2: 为查询到的信息有几条  参3:查询到的结果  参4:查询结构的名称即类型
	函数功能:禁言群聊用户
	注:禁言也是可以接受消息的,不需要将fd此数组中踢出
 */
int silencefd(void *para, int columnCount, char **columnValue, char **columnName)
{
    int fd = *columnValue[0] - '0';

    Judge J;
    J.cmd = 17;
    if(strcmp(RecvInfo.age, "FAILURE") == 0)    //若相等则说明是禁言用户,
    {
        strcpy(J.buf, "FAILURE");

        ret = send(fd, &J, sizeof(J), 0);
        if(ret == -1)
        {
            perror("send");
        }
    }

    else
    {
        strcpy(J.buf, "TRUE");       //若与TRUE相等,为解禁用户 
        ret = send(fd, &J, sizeof(J), 0);
        if(ret == -1)
        {
            perror("send");
        }
    }

    return 0;
}

/*
	作者:杨宣
	函数原型:void silence(int fd
	函数说明:自定义函数:函数名silence 带有一个int 型参数,返回值为void型
	函数功能:完成客户端解禁言用户请求
 */
void silence(int fd)
{
    sprintf(sql, "select fd from User where petname = '%s';", RecvInfo.petname);
    ret = sqlite3_exec(pdb, sql, silencefd, NULL, NULL); //找出要解禁言用户的fd
    if(ret != SQLITE_OK)
    {     
        perror("qroupfd");
    }    

}

/*
	作者:杨宣
	函数原型: 回调函数:int show(void *para, int columnCount, char **columnValue, char **columnName)
	函数说明:参1:为调用函数传过来的参数, 参2: 为查询到的信息有几条  参3:查询到的结果  参4:查询结构的名称即类型
	函数功能:完成管理员踢出普通用户的请求
 */
int kickfd(void *para, int columnCount, char **columnValue, char **columnName)
{
    int fd = *columnValue[0] - '0';
    int i, flag;
    
    Judge J;
    J.cmd = 17;
    strcpy(J.buf, "FALSE");     //发送踢出群聊信息给被踢用户
    ret = send(fd, &J, sizeof(J), 0);
    if(ret == -1)
    {
        perror("send");
    }

        for(i = 0; i < S; i++)  //将被踢用户fd在数组中下标找出
        {
            if(groupfd[i] == fd)
            {
                flag = i;
                break;
            }
        }

        if(flag < S -1)         //将该用户fd从数组中踢出
        {
            for(j = 0; j < S - flag - 1; j++)
            {
                groupfd[flag + j] = groupfd[flag + 1 + j];
            }
            S--;
        }
    
        else
        {
            S--;
        }

    return 0;
}

/*
	作者:杨宣
	函数原型:void kick()
    函数说明:自定义函数:函数名 kick 不带参数,返回值为void 
    函数功能: 完成客户端踢出群聊用户的请求
 */
void kick()
{
    sprintf(sql, "select fd from User where petname = '%s';", RecvInfo.petname);//找出要踢出用户的fd
    ret = sqlite3_exec(pdb, sql, kickfd, NULL, NULL);
    if(ret != SQLITE_OK)
    {     
        perror("qroupfd");
    }   

}

/*
	作者:杨宣
	函数原型: 回调函数:int show(void *para, int columnCount, char **columnValue, char **columnName)
	函数说明:参1:为调用函数传过来的参数, 参2: 为查询到的信息有几条  参3:查询到的结果  参4:查询结构的名称即类型
    函数功能:/发送设置成为管理员信息给用户
 */
int adminfd(void *para, int columnCount, char **columnValue, char **columnName)
{
    int fd = *columnValue[0] - '0';

    Judge J;
    J.cmd = 17;
    strcpy(J.buf, "TRUE"); //发送设置成为管理员信息给用户

    ret = send(fd, &J, sizeof(J), 0);
    if(ret == -1)
    {
        perror("send");
    }
    return 0;
}

/*
	作者:杨宣
	函数原型:void admin()
    函数说明:自定义函数:函数名 admin 不带参数, 返回值为void 
    函数功能:完成群主设置普通用户成为管理员的功能
 */
void admin()
{
    sprintf(sql, "select fd from User where petname = '%s';", RecvInfo.petname);//找出要设置用户的fd
    ret = sqlite3_exec(pdb, sql, adminfd, NULL, NULL);
    if(ret != SQLITE_OK)
    {     
        perror("qroupfd");
    }    

}

/*
	作者:杨宣
	函数原型:int cancelfd(void *para, int columnCount, char **columnValue, char **columnName)
    函数功能:帐号注销前,先将文件描述符记下,以备发送注销结果
 */
int cancelfd(void *para, int columnCount, char **columnValue, char **columnName)
{
    fdcancel = *columnValue[0] - '0';
    return 0;
}

/*
	作者:杨宣
	函数原型:void cancel()
	函数功能:自定义函数实现功能:注销帐号
 */
void cancel()
{
    memset (sql, 0, sizeof(sql));

    sprintf(sql, "select fd from User where petname  = '%s';",RecvInfo.petname);
    ret = sqlite3_exec(pdb, sql, cancelfd, NULL, NULL);
    if(ret != SQLITE_OK)
    {
        perror("sqlite3");
    }

    memset (sql, 0, sizeof(sql));
    sprintf(sql, "delete from User where petname = '%s';", RecvInfo.petname);
    ret = sqlite3_exec(pdb, sql, NULL, NULL, NULL);
    if(ret != SQLITE_OK)        //注销失败
    {
        Judge J;
        strcpy(J.buf, "FAILURE");
        J.cmd = 8;

        ret = send(fdcancel, &J, sizeof(J), 0);
        if(ret == -1)
        {
            perror("send");
        }
        perror("SQLITE_ok");
    }
    else            //注销成功
    {
        Judge J;
        strcpy(J.buf, "SUCCESS");

        J.cmd = 8;
        ret = send(fdcancel, &J, sizeof(J), 0);
        if(ret == -1)
        {
            perror("cancel success");
        }
    }

}

/*

 */
/*int groupname(void *para, int columnCount, char **columnValue, char **columnName)
{
    Judge J;
    J.cmd = 6;
    char buf[32] = {0};

    strcpy(J.petname, columnValue[0]);
    strcpy(J.buf, para);
    printf("J.buf %s\n", J.buf);

    ret = send(group, &J, sizeof(J), 0);
    if(ret == -1)
    {
        perror("group");
    }
    return 0;
}*/

int sendgroup(void *para, int columnCount, char **columnValue, char **columnName)
{
    Judge J;
   if(strcmp(columnValue[1], RecvInfo.petname) != 0)
   {
        strcpy(J.petname, columnValue[1]);
        strcpy(J.buf, columnValue[3]);
        strcpy(J.t2, columnValue[4]);
        J.cmd = 6;

        ret = send(group, &J, sizeof(J), 0);
        if(ret == -1)
        {
            perror("send");
        }
     printf("j.buf %s\n", J.buf);
        memset(&J, 0, sizeof(J));
   }
   else
   {
    J.cmd = 6;
    strcpy(J.buf, "SUCCESS");
    ret = send(group, &J, sizeof(J), 0);
    if(ret == -1)
    {
        perror("send");
       return 0;
    }
   }

    return 0;
}


int checkrecord(void *para, int columnCount, char **columnValue, char **columnName)
{
    memset(sql, 0, sizeof(sql));
    a[I] = atoi(*columnValue);
    printf("a[i] %d\n", a[I]);
    I++;
    printf("a= %d\n", I);

    return 0;
}

/*
 */
void groupcheck(int sockfd)
{
    group = sockfd;
    memset(sql, 0, sizeof(sql));
    sprintf(sql, "select id from chat where petname = '%s';", RecvInfo.petname);
    ret = sqlite3_exec(pdb, sql, checkrecord, NULL, NULL);
    if(ret != SQLITE_OK)
    {
        perror("sqlite");
    }

    sprintf(sql, "select * from chat limit '%d','%d';", a[0], a[1] - 1);
    ret = sqlite3_exec(pdb, sql, sendgroup, NULL, NULL);
    if(ret != SQLITE_OK)
    {
        perror("group");
    }

    

}

int convertcmd(void *para, int columnCount, char **columnValue, char **columnName)
{
    FILE *fp;
    User U;
    int fd = *(int *)para;
    int cmd = atoi(*columnValue);
    Judge J;
    char buf[32] = {0};

        fp = fopen(RecvInfo.buf, "w+");
        if(fp == NULL)
        {
            perror("fopen");
        }


        while(1)
        {
            ret = recv(fd, &U, sizeof(U), 0);
            if(ret == -1)
            {
                perror("recv");
            }
            if(strcmp(U.age, "bye") == 0 || ret == 0)
            {
                break;
            }

            ret = fwrite(U.buf, 1, U.cmd, fp);
            if(ret == -1)
            {
                perror("fwrite");
            }
            memset(U.buf, 0, sizeof(U.buf));
        }

        fclose(fp);
        /*Judge J;
        J.cmd = 19;
        strcpy(J.petname, RecvInfo.petname);
        strcpy(J.buf, "SUCCESS");

        ret = send(fd, &J, sizeof(J), 0);
        if(ret == -1)
        {
            perror("convert");
        }*/

    if(cmd == 1)
    {
        sprintf(sql, "Insert into file (namesend, namerecv, file, cmd) values ('%s', '%s', '%s', '%d');", RecvInfo.age, RecvInfo.petname, RecvInfo.buf, 1);
        ret = sqlite3_exec(pdb, sql, NULL, NULL, NULL);
        if(ret != SQLITE_OK)
        {
            perror("insert file failure");
        }

    }
    else
    {

        fp = fopen(RecvInfo.buf, "r");
        if(fp == NULL)
        {
            printf("failure\n");
        }

        J.cmd = 18;
        strcpy(J.petname, RecvInfo.age);
        strcpy(J.buf, RecvInfo.buf);

        ret = send(FD, &J, sizeof(J), 0);
        if(ret == -1)
        {
            perror("send");
        }
        while(1)
        {
            memset(J.buf, 0, sizeof(J.buf));
            ret = fread(J.buf, 1, sizeof(J.buf) - 1, fp);
            if(ret == -1)
            {
                perror("fread");
            }
            if(ret == 0)
            {
                J.cmd = 0;
                strcpy(J.buf, "SUCCESS");
                ret = send(FD, &J, sizeof(J), 0);
                if(ret == -1)
                {
                    perror("send");
                }
                break;
            }

            else
            {
            J.cmd = ret;
            ret = send(FD, &J, sizeof(J), 0);
            if(ret == -1)
            {
                perror("send");
            }
            }

        fclose(fp);

        sprintf(sql, "Inset into file (namesend, namerecv, file, cmd) values ('%s', '%s', '%s', '%d');", RecvInfo.age, RecvInfo.petname, RecvInfo.buf, 2);
        ret = sqlite3_exec(pdb, sql, NULL, NULL, NULL);
        if(ret != SQLITE_OK)
        {
            perror("insert file failure");
        }

    }
    }
    return 0;
}

int convertfd(void *para, int columnCount, char **columnValue, char **columnName)
{
    FD = atoi(*columnValue);        //记下要接受文件的fd
    return 0;
}
void convert(int sockfd)
{
    memset(sql, 0, sizeof(sql));
    sprintf(sql, "select fd from User where petname = '%s';", RecvInfo.petname);//找出要接受用户的fd
    ret = sqlite3_exec(pdb, sql, convertfd, NULL, NULL);
    if(ret != SQLITE_OK)
    {     
        perror("qroupfd");
    }   

    memset(sql, 0, sizeof(sql));
    sprintf(sql, "select cmd from User where petname = '%s';", RecvInfo.petname);//找出要接受文件用户的cmd
    ret = sqlite3_exec(pdb, sql, convertcmd, &sockfd, NULL);
    if(ret != SQLITE_OK)
    {     
        perror("qroupfd");
    }    
}

int findfile(void *para, int columnCount, char **columnValue, char **columnName)
{
    Judge J;
    J.cmd = 19;
    int fd = *(int *)para;
    if(columnCount >= 0)
    {
        strcpy(J.petname, columnValue[0]);
        strcpy(J.buf, columnValue[2]);

        ret = send(fd, &J, sizeof(J), 0);
        if(ret == -1)
        {
            perror("send");
        }
    }
    return 0;
}

void checkfile(int sockfd, User U)
{
    Judge J;
    sprintf(sql, "select * from file where namerecv = '%s';", U.petname);
    ret = sqlite3_exec(pdb, sql, findfile, &sockfd, NULL);
    if(ret != SQLITE_OK)
    {
        perror("findfile");
    }

    J.cmd = 19;
    strcpy(J.buf, "SUCCESS");
    ret = send(sockfd, &J, sizeof(J), 0);
    if(ret == -1)
    {
        perror("send");
    }

}
/*
   main()主函数,
   功能:完成服务器的初始化,绑定,并监测所有已连接的客户端的fd,
 */
int main()
{
    pthread_mutex_init(&mutex, NULL);   //对锁和条件变量进行初始化
    pthread_cond_init(&con, NULL);

    //pthread_detach(pthread_self()); //线程分离
    struct sockaddr_in client_addr;//客户端结构体变量,保存客户端信息

    pthread_t tid;      //线程号
    int i = 0;          //
    fd_set ReadFd, tmpfd;       //定义可读集合和中间变量
    int MaxFd;                      //用来记下最大的文件描述符

    signal(SIGINT, StopClient);     //捕捉ctrl+c信号,当捕捉到这个信号时,执行StopClient函数

/*创建一个套接字, 采用TCP协议*/
    sockfd = socket(PF_INET, SOCK_STREAM, 0);
    if(sockfd == -1)
    {
        perror("sockfd");
        exit(1);
    }


    int opt = 1;            //地址可被复用,man手册可查看函数原型 man setsockopt
    setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));

/*定义好后进行初始化, 并对返回值进行判断*/
    ret = sockfdInit(sockfd);
    if(ret == FAILURE)
    {
        printf("Init FAILURE");
    }

    FD_ZERO(&ReadFd);       //将集合清0, man手册 man FD_SERO可查看一切与select 有关的函数原型
    FD_SET(sockfd, &ReadFd);    //将刚创建的文件描述符sockfd 放入可读集合中
    MaxFd = sockfd;             //目前最大的文件描述符为sockfd,为 4,

/*
   打开一个数据库若不存在则自动创建, 用来建表保存信息
  数据库名字User.db
  pdb为该数据库句柄, 可通过句柄访问数据库
 */
    ret = sqlite3_open("User.db", &pdb);
    if(ret != SQLITE_OK)
    {
        perror("sqlite3_open1");
        exit(1);
    }

/*
   在数据库中建一张表, 用来保存注册信息
  sqlite3_errmsg(sqlite3 *pdb) 用来返回错误说明指针
  pdb 打开的数据库句柄 
  该表中含有用户 昵称,年龄,性别,密码,在线状态,在线设备,会员等级
 */
        sprintf(sql, "create table if not exists User (petname text, age text, sex text, key text, cmd register, fd register, member register);");
        ret = sqlite3_exec(pdb, sql, NULL, NULL, NULL);
        if(ret != SQLITE_OK)
        {
            printf("sqlite3_exec error : %s \n", sqlite3_errmsg(pdb));
        }


/* 
   在数据库中另建一张表,用来存储聊天记录,
   该表含有用户 昵称,在线设备, 聊天记录
  */
        sprintf(sql, "create table if not exists Chat (id register, petname text, Tofd register, ptr text, time register);");
        ret = sqlite3_exec(pdb, sql, NULL, NULL, NULL);
        if(ret != SQLITE_OK)
        {
            printf("sqlite3_exec error : %s \n", sqlite3_errmsg(pdb));
        }

        sprintf(sql, "create table if not exists file (namesend text, namerecv text, file text, cmd register);");
        ret = sqlite3_exec(pdb, sql, NULL, NULL, NULL);
        if(ret != SQLITE_OK)
        {
            printf("sqlite3_exec error : %s \n", sqlite3_errmsg(pdb));
        }
/*
   在表中再建一张表,用来存储用户的密保答案
   该表中含用户昵称,三个问题的答案
 */
        
        sprintf(sql, "create table if not exists protest (petname text, que1 text, que2 text, que3 text);");
        ret = sqlite3_exec(pdb, sql, NULL, NULL, NULL);
        if(ret != SQLITE_OK)
        {
            printf("sqlite3_exec error : %s \n", sqlite3_errmsg(pdb));
        }

        int length = sizeof(client_addr);

/*不断接受客户端请求,并创建线程处理客户端请求*/
    while(1)
    {
        tmpfd = ReadFd;     //用中间变量记下集合状态

/*
    函数原型:int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
    参1:为最大文件描述符+1,
    参2:需要监听的可读集合
    参3:需要监听的可写集合,若不需要监听是否可写,则置为NULL,
 */
        ret = select(MaxFd + 1, &tmpfd, NULL, NULL, NULL);  //用select 去监听这个集合
        if(ret == -1)
        {
            perror("select");
        }

        if (FD_ISSET(sockfd, &tmpfd))    //有客户端发起连接,将集合中的文件描述符遍历一遍,查看之前是否有连接的客户端断开连接, 若有客户端退出连接fd被置为 0,则跳出
        {
            for (j = 0; j < i; j++)
            {
                if (fd[j] == 0)
                {
                    break;
                }
            }

//接受客户端的连接请求
        fd[j] = accept(sockfd, (struct sockaddr *)&client_addr, (socklen_t *)&length);
        if(-1 == fd[j])
        {
            return FAILURE;
        }

        if (MaxFd < fd[j])  //如果最大的fd比刚连接的fd小,则将刚连接的fd赋给MaxFd
        {
            MaxFd = fd[j];
        }

        FD_SET(fd[j], &ReadFd);     //将刚连接的fd放入监听的可读集合中
        if(j == i)              //若i== j说明之前连接的客户端中并没有退出的,计数 i++
        {
            i++;
        }
        }

        else
        {
            for (j = 0; j < i; j++) //有客户端发消息
            {
                if (FD_ISSET(fd[j], &tmpfd))    //挨个监听哪个客户端发了消息
                {
                    memset(&RecvInfo, 0, sizeof(RecvInfo));
                    ret = recv(fd[j], &RecvInfo, sizeof(RecvInfo), 0);

                    printf("Cmd = %d\n", RecvInfo.cmd);
                    if(strcmp(RecvInfo.petname, "BYE") == 0)    //若有客户端发了BYE,则断开该客户端连接
                    {
                        if(RecvInfo.cmd == 1)
                        {
                            sprintf(sql, "update User set cmd = '1' where petname = '%s';", RecvInfo.buf);
                            ret = sqlite3_exec(pdb, sql, NULL, NULL, NULL);
                            if(ret != SQLITE_OK)
                            {
                                perror("setonecmd");
                            }
                        }
                        close(fd[j]);
                        FD_CLR(fd[j], &ReadFd); //并将该客户端从集合中清除
                        fd[j] = 0;
                        j--;
                        break;
                    }

        switch(RecvInfo.cmd)
        {
            case 1:
                ret = pthread_create(&tid, NULL, pthreadHandle, &fd[j]);
                printf("Ccmd\n");
                if(ret == -1)
                {
                    printf("create failure1");
                    exit(1);
                }
                break;

            case 2:
                 ret = pthread_create(&tid, NULL, pthreadlogin, &fd[j]);
                 if(ret == -1)
                 {
                    printf("create failure2");
                    exit(1);
                 }
                 break;

            case 3:
                 ret = pthread_create(&tid, NULL, pthreadshow, &fd[j]);
                 if(ret == -1)
                 {
                    printf("create failure3");
                    exit(1);
                 }
                //memset(&RecvInfo, 0, sizeof(RecvInfo));
                 break;

           case 4:
                 ret = pthread_create(&tid, NULL, pthreadprivate, &fd[j]);
                 if(ret == -1)
                 {
                    printf("create failure4");
                    exit(1);
                 }
                //memset(&RecvInfo, 0, sizeof(RecvInfo));
                 break;

           case 5:
                 ret = pthread_create(&tid, NULL, groupchat, &fd[j]);
                 if(ret == -1)
                 {
                    printf("create failure4");
                    exit(1);
                 }
                 break;

           case 6:
                 groupcheck(fd[j]);
                 break;

           case 7:
                 ret = pthread_create(&tid, NULL, pthreadcheck, &fd[j]);
                 if(ret == -1)
                 {
                    printf("create failure4");
                    exit(1);
                 }
                 break;

           case 8:
                 cancel();
                 break;
           case 9:
                 ret = pthread_create(&tid, NULL, pthreadmember, &fd[j]);
                 if(ret == -1)
                 {
                    printf("create failure9");
                    exit(1);
                 }
                 break;

           case 10:
                 ret = pthread_create(&tid, NULL, pthreadpraise, &fd[j]);
                 if(ret == -1)
                 {
                    printf("create failure10");
                    exit(1);
                 }
                 
                 break;

            case 11:
                 ret = pthread_create(&tid, NULL, pthreadexit, &fd[j]);
                 if(ret == -1)
                 {
                    printf("create failure11");
                    exit(1);
                 }
                 break;

            case 12:
                 ret = pthread_create(&tid, NULL, pthreadactive, &fd[j]);
                 if(ret == -1)
                 {
                    printf("create failure11");
                    exit(1);
                 }
                 break;

            case 13:
                 ret = pthread_create(&tid, NULL, pthreadnegtive, &fd[j]);
                 if(ret == -1)
                 {
                    printf("create failure11");
                    exit(1);
                 }
                 break;

            case 14:
                 printf("buf%s\n", RecvInfo.buf);
                 break;

            case 15:
                 ret = pthread_create(&tid, NULL, keyprotest, &fd[j]);

                 if(ret == -1)
                 {
                    printf("create failure11");
                    exit(1);
                 }
                 break;

            case 16:
                 ret = pthread_create(&tid, NULL, findkey, &fd[j]);
                 if(ret == -1)
                 {
                    printf("create failure11");
                    exit(1);
                 }
                 
                 break;

            case 17:
                 ret = pthread_create(&tid, NULL, correctkey, &fd[j]);
                 if(ret == -1)
                 {
                    printf("create failure11");
                    exit(1);
                 }

                break;

             case 18:
                 ret = pthread_create(&tid, NULL, receivegroup, &fd[j]);
                 if(ret == -1)
                 {
                    printf("create failure11");
                    exit(1);
                 }

                break;

             case 19:
                 ret = pthread_create(&tid, NULL, activegroup, &fd[j]);
                 if(ret == -1)
                 {
                    printf("create failure11");
                    exit(1);
                 }

                break;

             case 20:
                    silence(fd[j]);
                    break;

             case 21:
                    kick();
                    break;
             case 22:
                    admin();
                    break;
             case 23:
                    convert(fd[j]);
                    break;
             case 24:
                    checkfile(fd[j], RecvInfo);
                    break;

        }
                }
            }
        }
    }
    

    //    for(j = 0; j < i; j++) //为什么加了线程等待,就段错误
      //  {
            //void *status;
           // pthread_join(tid, &status);
       // }
    while(j >= 0)
    {
	   close(fd[j]);
       j--;
    }
	   close(sockfd);
       

    return 0;
}

/**************************************************************
  > File Name: SockServer.c
  > Author: yangxuan
  > Mail: [email protected] 
  > Created Time: 2018年08月23日 星期四 23时27分19秒
 **************************************************************/

#include <stdio.h>
#include "Server.h"

#define PORT  8888

/* 函数原型:int  sockfdInit(int sockfd), 函数名:sockfdInit 函数带有一个int型的参数,
   函数返回值为 int型  
   功能: 完成服务端初始化
*/
int  sockfdInit(int sockfd)
{
    int ret;

/*初始化服务器信息*/
    memset(&server_addr, 0, sizeof(server_addr));
    server_addr.sin_family = PF_INET;
    server_addr.sin_port = htons(PORT);
    server_addr.sin_addr.s_addr = inet_addr("192.168.1.113");

/*绑定本地服务器信息*/
    ret = bind(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr));
    if(ret == -1)
    {
        printf("----\n");
        return FAILURE;
    }

/*从sockfd 中一次监听5个客户端*/
    ret = listen(sockfd, 5);
    if(ret == -1)
    {
        return FAILURE;
    }
    return SUCCESS;
}


/*void send1(int fd)
{
    int ret;
    Judge J;
    strcpy(J.buf, "FAILURE");
    J.cmd = 1;
    printf("J.buf %s\n",J.buf);
    ret = send(fd, &J, sizeof(J), 0);
    if(ret == -1)
    {
        perror("send");
    }
}*/

//
void send0(int fd)
{
    int ret;
    Judge J;
    strcpy(J.buf, "SUCCESS");
    J.cmd = 1;
    printf("J.buf %s\n",J.buf);
    ret = send(fd, &J, sizeof(J), 0);
    if(ret == -1)
    {
        perror("send");
    }
}

猜你喜欢

转载自blog.csdn.net/weixin_42720316/article/details/82934607