服务器端
#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: 2503327051@qq.com
> 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不可以少
}
/*
作