1.main.c文件
要做的事有:
1.初始化客户登录信息表
2.检查启动参数和捕捉线程退出信号
3.定义和初始化线程池
4.初始化TCP
5.创建epoll和用epoll监听sfd
6.进入功能界面
6.(1)判断退出标志位是否改为1,是就进入退出程序
6.(2)若不退出程序,用while(1)监听epoll
6.(3)若现在没有新的sfd连接服务器,给新连接的sfd先来登录,主线程承接登录验证作用
#include "../include/queue.h"
#include "../include/head.h"
#include "../include/threadpool.h"
#include "../include/mainthread.h"
#include "../include/childthread.h"
#include "../include/register_login.h"
//设置全局变量退出,用来表示主线程的退出标志
int exitflag = 0;
void sigFunc(int sigNum)
{
printf("signal%d is coming\n",sigNum);
//主线程收到退出信号,修改退出标志位值,0代表正常运行,1代表退出
exitflag = 1;
}
int main(int argc,char *argv[])
{
//1.初始化客户登录信息表,结构体login_Flag_List存储连接的sfd和连接状态
memset(login_Flag_List,0,sizeof(Login_Flag_t)*CLIENT_NUM);
//2.检查启动参数和捕捉线程退出信号
// ./main ip port threadNum
ARGS_CHECK(argc,4);
//捕捉信号,SIGUSR1为线程退出发出的信号
signal(SIGUSR1,sigFunc);
//接受开启的线程数
int threadNum = atoi(argv[3]);
//3.定义和初始化线程池
ThreadPool_t threadPool;
memset(&threadPool,0,sizeof(threadPool));
//初始化线程池
threadPoolInit(&threadPool,threadNum);
//启动线程池
threadPoolStart(&threadPool);
//4.初始化TCP
int sfd = 0;
tcpInit(argv[1],argv[2],&sfd);
//5.创建epoll和用epoll监听sfd
int epfd = epoll_create(1);
epfdAddfd(sfd,epfd);
//初始化epoll监听的参数
/* epfdAddfd(exitPipe[0],epfd); */
struct epoll_event evs[2];
int newFd = 0;
int readyNum = 0;
//6.进入功能界面
while(1)
{
//6.(1)判断退出标志位是否改为1,是就进入退出程序
if(1 == exitflag)
{
pNote_t pNote = (pNote_t)calloc(threadPool.threadNum,sizeof(Note_t));
pthread_mutex_lock(&threadPool.Queue.mutex);
for(int i =0;i<threadPool.threadNum;++i)
{
pNote[i].clientFd = -1;
queueInsert(&threadPool.Queue,&pNote[i]);
pthread_cond_signal(&threadPool.Queue.cond);
}
pthread_mutex_unlock(&threadPool.Queue.mutex);
for(int i=0;i<threadPool.threadNum;++i)
{
pthread_join(threadPool.pthid[i],NULL);
}
break;
}
//6.(2)若不退出程序,用while(1)监听epoll
readyNum = epoll_wait(epfd,evs,2,-1);
for(int i = 0;i < readyNum; ++i)
{
if(sfd == evs[i].data.fd)//若有新的sfd连接请求到服务器
{
newFd = accept(sfd,NULL,NULL);//接受sfd
epfdAddfd(newFd,epfd);//加入监听集合
for(int j=0;j<CLIENT_NUM;++j)
{
if(0 == login_Flag_List[j].sfd)//
{
login_Flag_List[j].sfd = newFd;
login_Flag_List[j].login_Flag = 0;
break;
}
}
}
else//6.(3)若现在没有新的sfd连接服务器,给新连接的sfd先来登录,主线程承接登录验证作用
{
for(int j=0;j<CLIENT_NUM;++j)
{
if(evs[i].data.fd == login_Flag_List[j].sfd)
{
if(login_Flag_List[j].login_Flag != 3)//若还没完成登录
{
Register_Login_Screen(login_Flag_List[j].sfd);
}
else//若完成该sfd完成登录
{
pNote_t pNote = (pNote_t)calloc(1,sizeof(Note_t));
pthread_mutex_lock(&threadPool.Queue.mutex);
pNote->clientFd = evs[i].data.fd;
queueInsert(&threadPool.Queue,pNote);
pthread_cond_signal(&threadPool.Queue.cond);
pthread_mutex_unlock(&threadPool.Queue.mutex);
}
}
}
}
}
}
return 0;
}
2.function.c
作为补充线程池的函数
#include "../include/head.h"
#include "../include/threadpool.h"
#include "../include/queue.h"
#include "../include/mainthread.h"
#include "../include/childthread.h"
#include "../include/register_login.h"
int threadPoolInit(pThreadPool_t pPool,int threadNum)
{
pPool->threadNum = threadNum;
pPool->pthid = (pthread_t *)calloc(threadNum,sizeof(pthread_t));
queueInit(&pPool->Queue);
return 0;
}
int threadPoolStart(pThreadPool_t pPool)
{
for(int i = 0;i< pPool->threadNum;++i)
{
pthread_create(&pPool->pthid[i],NULL,threadFunc,&pPool->Queue);
}
return 0;
}
void *threadFunc(void *p)
{
pQue_t pQueue = (pQue_t)p;
pNote_t pCur;
int getsuccess = 1;
while(1)
{
pthread_mutex_lock(&pQueue->mutex);
if(0 == pQueue->size)
{
pthread_cond_wait(&pQueue->cond,&pQueue->mutex);
}
getsuccess = queueGet(pQueue,&pCur);
pthread_mutex_unlock(&pQueue->mutex);
//如果收到的任务中文件描述符为-1,代表进行线程退出流程
if(pCur->clientFd == -1)
{
pthread_exit(0);
}
//如果获得任务成功,执行任务
if(0 == getsuccess)
{
/* Register_Login_Screen(pCur->clientFd); */
transFile(pCur->clientFd);
free(pCur);
pCur = NULL;
}
}
}
int queueInit(pQue_t pQueue)
{
pQueue->size = 0;
pQueue->pHead = NULL;
pQueue->pTail = NULL;
pthread_mutex_init(&pQueue->mutex,NULL);
pthread_cond_init(&pQueue->cond,NULL);
return 0;
}
int queueInsert(pQue_t pQueue,pNote_t pNote)
{
if(NULL == pQueue->pHead)
{
pQueue->pHead = pNote;
pQueue->pTail = pNote;
}
else
{
pQueue->pTail->next = pNote;
pQueue->pTail = pNote;
}
pQueue->size++;
return 0;
}
int queueGet(pQue_t pQueue,pNote_t *pGet)
{
if(0 == pQueue->size)
{
return -1;
}
*pGet = pQueue->pHead;
pQueue->pHead = pQueue->pHead->next;
pQueue->size--;
if(0 == pQueue->size)
{
pQueue->pTail = NULL;
}
return 0;
}
int tcpInit(char *ip,char *port,int *sockfd)
{
int sfd = socket(AF_INET,SOCK_STREAM,0);
ERROR_CHECK(sfd,-1,"socket");
struct sockaddr_in saddr;
memset(&saddr,0,sizeof(saddr));
saddr.sin_family = AF_INET;
saddr.sin_addr.s_addr = inet_addr(ip);
saddr.sin_port = htons(atoi(port));
int ret = bind(sfd,(struct sockaddr*)&saddr,sizeof(saddr));
ERROR_CHECK(ret,-1,"bind");
ret = listen(sfd,10);
ERROR_CHECK(ret,-1,"listen");
*sockfd = sfd;
return 0;
}
int epfdAddfd(int fd,int epfd)
{
struct epoll_event event;
memset(&event,0,sizeof(event));
event.events = EPOLLIN;
event.data.fd = fd;
int ret = epoll_ctl(epfd,EPOLL_CTL_ADD,fd,&event);
ERROR_CHECK(ret,-1,"epoll_add");
return 0;
}
int transFile(int clientfd)
{
Train_t train;
memset(&train,0,sizeof(train));
int fd = open("file",O_RDWR);
//传输文件名
train.len = 4;
strcpy(train.buf,"file");
send(clientfd,&train,4+train.len,0);
//传输文件长度
struct stat fileInfo;
bzero(&fileInfo,sizeof(fileInfo));
fstat(fd,&fileInfo);
train.len = sizeof(fileInfo.st_size);
memcpy(train.buf,&fileInfo.st_size,train.len);
send(clientfd,&train,4+train.len,0);
while(1)
{
int ret = read(fd,train.buf,sizeof(train.buf));
train.len = ret;
int ret1 = send(clientfd,&train,4+train.len,0);
if(0 == ret)
{
break;
}
if(-1 == ret1)
{
printf("client fault\n");
break;
}
}
return 0;
}
3.insert.c
插入客户账号,盐值和密文数据,id自增
int mysqlInsert(char *name,char *salt,char* passwd)
{
if(NULL==name||NULL==salt||NULL==passwd){//若输入的字符串为空
printf("大兄弟,你传入mysqlInsert的字符串是空的");
return -1;
}
MYSQL *conn;
char *server = "localhost";
char *user = "root";
char *password = "lily_931216asd";
char *database = "clientInfo";//要访问的数据库名称
char query[200]={0};
//char query[200]="insert into ZGMF (name, salt, passwd) values ('timo', 'sadad12121', '12312sfqwe1')";
int queryResult;
sprintf(query,"insert into clientShadow (nickname,salt,passwd) values ('%s','%s','%s')",name,salt,passwd);
conn = mysql_init(NULL);
//连接数据库,看连接是否成功,只有成功才能进行后面的操作
if(!mysql_real_connect(conn, server, user, password, database, 0, NULL, 0))
{
printf("Error connecting to database: %s\n", mysql_error(conn));
}
else
{
printf("Connected...\n");
}
queryResult = mysql_query(conn, query);
if(queryResult)
{
printf("Error making query:%s\n", mysql_error(conn));
mysql_close(conn);
return -1;//表示插入失败
}
else
{
printf("insert success\n");
}
mysql_close(conn);
return 0;//表示插入成功
}
4.getsalt
根据用户名查盐值,把盐值发给sfd的哪一方
#include "../include/head.h"
#include "../include/register_login.h"
//根据用户名查盐值,把盐值发给sfd的哪一方
int getSalt(char* nameStr,int sfd)
{
MYSQL *conn;
MYSQL_RES *res;
MYSQL_ROW row;
char *server = "localhost";
char *user = "root";
char *password = "lily_931223asd";
char *database = "clientInfo";//要访问的数据库名称
char query[300] = "select salt from clientShadow where nickname='";
unsigned int queryRet;
clientInfo_t clientInfo;
sprintf(query, "%s%s%s", query, nameStr, "'");
/* strcpy(query,"select * from hero"); */
//在输出前先打印查询语句
puts(query);
//初始化
conn = mysql_init(NULL);
if(!conn)
{
printf("MySQL init failed\n");
return -1;
}
//连接数据库,看连接是否成功,只有成功才能进行后面的操作
if(!mysql_real_connect(conn, server, user, password, database, 0, NULL, 0))
{
printf("Error connecting to database: %s\n", mysql_error(conn));
return -1;
}
else
{
printf("MySQL Connected...\n");
}
//把SQL语句传递给MySQL
queryRet = mysql_query(conn, query);
if(queryRet)
{
printf("Error making query: %s\n", mysql_error(conn));
}
else
{
//用mysql_num_rows可以得到查询的结果集有几行
//要配合mysql_store_result使用
//第一种判断方式
res = mysql_store_result(conn);
printf("mysql_num_rows = %lu\n", (unsigned long)mysql_num_rows(res));
//第二种判断方式,两种方式不能一起使用
/* res = mysql_use_result(conn); */
row = mysql_fetch_row(res);
if(NULL == row)
{
printf("Don't find any data\n");
}
else
{
do
{
/* printf("num=%d\n",mysql_num_fields(res));//列数 */
//每次for循环打印一整行的内容
for(queryRet = 0; queryRet < mysql_num_fields(res); ++queryRet)
{
/* printf("%8s ", row[queryRet]); */
char buf[14] = {0};
sprintf(buf,"%d %s",3,row[queryRet]);
send(sfd,buf,strlen(buf),0);
/* sscanf(row[queryRet],"%d%s%s%s",&clientInfo.id,clientInfo.nickName,clientInfo.salt,clientInfo.Passwd); */
}
/* puts(clientInfo.salt); */
printf("\n");
}while(NULL != (row = mysql_fetch_row(res)));
}
mysql_free_result(res);
}
mysql_close(conn);
return 0;
}
5.queryByName
输入名字,看用户表是否有这个用户,若有返回0
//输入名字,打印数据库的名字
int queryByName(char* nameStr)
{
if(NULL==nameStr){
printf("大兄弟,你传给queryByName的字符串是空的呀!\n");
return -1;
}
MYSQL *conn;
MYSQL_RES *res;
MYSQL_ROW row;
char *server = "localhost";//本机
char *user = "root";//登录账户
char *password = "lily_931216asd";//登录密码
char *database = "clientInfo";//要访问的数据库名称
char query[300] = "select * from clientShadow where nickname='";//按名字查找
unsigned int queryRet;
clientInfo_t clientInfo;
sprintf(query, "%s%s%s", query, nameStr, "'");//拼接成命令字符串
/* strcpy(query,"select * from hero"); */
//在输出前先打印查询语句
puts(query);
//初始化数据库
conn = mysql_init(NULL);
if(!conn)
{
printf("MySQL init failed\n");
return -1;
}
//连接数据库,看连接是否成功,只有成功才能进行后面的操作
if(!mysql_real_connect(conn, server, user, password, database, 0, NULL, 0))
{//若连接数据库失败
printf("Error connecting to database: %s\n", mysql_error(conn));
return -1;
}
else//若连接数据库成功
{
printf("MySQL Connected...\n");
}
//把SQL语句传递给MySQL
queryRet = mysql_query(conn, query);
if(queryRet)//若传输失败
{
printf("Error making query: %s\n", mysql_error(conn));
return -1;
}
else//若传递命令成功
{
//用mysql_num_rows可以得到查询的结果集有几行
//要配合mysql_store_result使用
//第一种判断方式
res = mysql_store_result(conn);//表示查到结果集有几列
printf("mysql_num_rows = %lu\n", (unsigned long)mysql_num_rows(res));
//第二种判断方式,两种方式不能一起使用
/* res = mysql_use_result(conn); */
row = mysql_fetch_row(res);
if(NULL == row)//若没读到数据
{
printf("Don't find any data\n");
}
else//读到数据
{
do
{
/* printf("num=%d\n",mysql_num_fields(res));//列数 */
//每次for循环打印一整行的内容
for(queryRet = 0; queryRet < mysql_num_fields(res); ++queryRet)
{
//printf("%8s ", row[queryRet]);
if(row[queryRet]!=NULL){
printf("find account in database!\n");
break;//找到就退出,否则会打印四行find account
}
}
printf("\n");//换行
}while(NULL != (row = mysql_fetch_row(res)));
}
mysql_free_result(res);
}
mysql_close(conn);//关闭mysql服务
return 0;//表示找到
}
6.queryByPasswd
根据密文,看是否在数据库中,在就返回0
//输入名字,打印数据库的名字
int queryByPasswd(char* passWd)
{
if(NULL==passWd){
printf("大兄弟,你给queryByPasswd的字符串是空的呀!\n");
return -1;
}
MYSQL *conn;
MYSQL_RES *res;
MYSQL_ROW row;
char *server = "localhost";//本机
char *user = "root";//登录账户
char *password = "lily_931223asd";//登录密码
char *database = "clientInfo";//要访问的数据库名称
char query[300] = "select * from clientShadow where passwd='";//按密文查找
unsigned int queryRet;
sprintf(query, "%s%s%s", query, passWd, "'");//拼接成命令字符串
/* strcpy(query,"select * from hero"); */
//在输出前先打印查询语句
puts(query);
//初始化数据库
conn = mysql_init(NULL);
if(!conn)
{
printf("MySQL init failed\n");
return -1;
}
//连接数据库,看连接是否成功,只有成功才能进行后面的操作
if(!mysql_real_connect(conn, server, user, password, database, 0, NULL, 0))
{//若连接数据库失败
printf("Error connecting to database: %s\n", mysql_error(conn));
return -1;
}
else//若连接数据库成功
{
printf("MySQL Connected...\n");
}
//把SQL语句传递给MySQL
queryRet = mysql_query(conn, query);
if(queryRet)//若传输失败
{
printf("Error making query: %s\n", mysql_error(conn));
return -1;
}
else//若传递命令成功
{
//用mysql_num_rows可以得到查询的结果集有几行
//要配合mysql_store_result使用
//第一种判断方式
res = mysql_store_result(conn);//表示查到结果集有几列
printf("mysql_num_rows = %lu\n", (unsigned long)mysql_num_rows(res));
//第二种判断方式,两种方式不能一起使用
/* res = mysql_use_result(conn); */
row = mysql_fetch_row(res);
if(NULL == row)//若没读到数据
{
printf("Don't find any data\n");
return -1;//返回-1表示没找到
}
else//读到数据
{
do
{
/* printf("num=%d\n",mysql_num_fields(res));//列数 */
//每次for循环打印一整行的内容
for(queryRet = 0; queryRet < mysql_num_fields(res); ++queryRet)
{
//printf("%8s ", row[queryRet]);
if(row[queryRet]!=NULL){
printf("find account in database!\n");
break;//找到就退出,否则会打印四行find account
}
}
printf("\n");//换行
}while(NULL != (row = mysql_fetch_row(res)));
}
mysql_free_result(res);
}
mysql_close(conn);//关闭mysql服务
return 0;//表示找到
}
7.Screem.c
生成盐值和用户登录程序
#include "../include/head.h"
#include "../include/register_login.h"
//生成盐值
int GenerateStr(char *str)
{
int i,flag;
srand(time(NULL));
for(i = 0;i<STR_LEN;i++){
flag = rand()%3;
switch(flag){
case 0: str[i] = rand()%26+'a'; break;
case 1: str[i] = rand()%26+'A'; break;
case 2: str[i] = rand()%10+'0'; break;
}
}
return 0;
}
int Register_Login_Screen(int sfd)
{
//sfd为传过来的客户端的socket描述符
//获取客户登录信息中login_Flag标记的值
int i = 0;
for(i=0;i<CLIENT_NUM;++i)
{
if(sfd == login_Flag_List[i].sfd)
{
break;
}
}
//0代表还没有注册或者登录,1代表已经注册,还没有登录,2代表准备登录,3代表已登录
char buf[1024] = {0};
int ret = recv(login_Flag_List[i].sfd,buf,sizeof(buf),0);//接收客户端是否进入登录界面的回复,有回复就进入,没回复就返回
if(0 == memcmp("n",buf,1))//客户端退出界面,主线程也退出界面
{
return 0;
}
if(-1 == ret)//客户端异常,主线程退出界面
{
return -1;
}
printf("client enter\n");
while(1)
{
//flag标志为0,表示客户还没确定后续操作
//flag标志为1,表示客户选择注册界面
//flag标志为2,表示客户登录
//flag标志为3,表示客户登录成功
if(0 == login_Flag_List[i].login_Flag)//flag标志为0,表示客户还没确定后续操作
{
send(login_Flag_List[i].sfd,"1 Please choose one followed\n1.Register\n2.Login\nInput:",54,0);
memset(buf,0,sizeof(buf));
recv(login_Flag_List[i].sfd,buf,sizeof(buf),0);//等客户端发送选择结果
if(1 == atoi(buf))//如果客户选择1,就把登录标记改为1,再次循环进入注册界面
{
login_Flag_List[i].login_Flag= 1;//登录标记改为1
continue;
}
else if(2 == atoi(buf))//如果客户选择2,就把标记改为2,再次循环进入登录界面
{
login_Flag_List[i].login_Flag= 2;//登录标记改为2
continue;
}
else//如果输入有误,提示重新输入
{
send(login_Flag_List[i].sfd,"1 Input wrong message ,please try again\n\n",39,0);
continue;
}
}
if(1 == login_Flag_List[i].login_Flag)//客户选择注册界面
{
clientInfo_t clientInfo;//存储客户信息
memset(&clientInfo,0,sizeof(clientInfo));
char salt[128] = {0};
//请客户输入昵称
send(login_Flag_List[i].sfd,"1 Please input your nickname:",29,0);
memset(buf,0,sizeof(buf));
int ret = recv(login_Flag_List[i].sfd,buf,sizeof(buf),0);//等客户端发送选择结果
if(-1 == ret)
{
send(login_Flag_List[i].sfd,"1 nickname recv error,register again\n",37,0);
printf("nickname recv error,register again\n");
continue;
}
memcpy(clientInfo.nickName,buf,strlen(buf));
//产生随机数,生成盐值
memset(buf,0,sizeof(buf));
GenerateStr(buf);
sprintf(salt,"%s%s","$6$",buf);
memcpy(clientInfo.salt,salt,strlen(salt));
memset(buf,0,sizeof(buf));
sprintf(buf,"%d%s",3,salt);
//发送盐值
send(login_Flag_List[i].sfd,buf,sizeof(buf)-1,0);
memset(buf,0,sizeof(buf));
ret = recv(login_Flag_List[i].sfd,buf,sizeof(buf),0);
printf("salt send succeed\n");
if(-1 == ret)
{
printf("salt value recv error,register again\n");
continue;
}
//请客户输入登录密码
send(login_Flag_List[i].sfd,"2 :",3,0);
//接收客户端生成的密文
memset(buf,0,sizeof(buf));
ret = recv(login_Flag_List[i].sfd,buf,sizeof(buf),0);
if(-1 == ret)
{
printf("passwd recv error,register again\n");
continue;
}
//密文存储到客户信息结构体中
memcpy(clientInfo.Passwd,buf,strlen(buf));
//把clientInfo结构体中的信息存到文件中
memset(buf,0,sizeof(buf));
//把客户端注册信息存入mysql中
ret = mysqlInsert(clientInfo.nickName,clientInfo.salt,clientInfo.Passwd);
if(-1 == ret)
{
printf("insert error,register again\n");
continue;
}
//退出注册
send(login_Flag_List[i].sfd,"0 You have been registered succeed,please login\n",48,0);
login_Flag_List[i].login_Flag= 2;//修改flag,进入登录界面
memset(buf,0,sizeof(buf));
ret = recv(login_Flag_List[i].sfd,buf,sizeof(buf),0);
printf("register succeed\n");
continue;
}
if(2 == login_Flag_List[i].login_Flag)
{
//请客户输入昵称
int ret = send(login_Flag_List[i].sfd,"1 Please input your nickname:",29,0);
memset(buf,0,sizeof(buf));
ret = recv(login_Flag_List[i].sfd,buf,sizeof(buf),0);//等客户端发送选择结果
if(-1 == ret)
{
printf("nickname recv error,login again\n");
continue;
}
ret = queryByName(buf);//从数据库比对是否有客户输入的昵称
if(-1 == ret)
{
send(login_Flag_List[i].sfd,"1 nickname error,login again\n",29,0);
continue;
}
printf("nickname check ok\n");
ret = getSalt(buf,sfd);//从数据库获取盐值
if(-1 == ret)
{
send(login_Flag_List[i].sfd,"1 slat send error,login again\n",30,0);
printf("salt value send error,login again\n");
continue;
}
//接收客户端接收盐值成功返回信号
memset(buf,0,sizeof(buf));
ret = recv(login_Flag_List[i].sfd,buf,sizeof(buf),0);
if(-1 == ret)
{
send(login_Flag_List[i].sfd,"1 passwd error,login again\n",27,0);
printf("salt value recv error,login again\n");
continue;
}
//请客户输入登录密码
send(login_Flag_List[i].sfd,"2 :",3,0);
//接收密文
memset(buf,0,sizeof(buf));
ret = recv(login_Flag_List[i].sfd,buf,sizeof(buf),0);
if(-1 == ret)
{
printf("passwd recv error,register again\n");
continue;
}
//比对密文
ret = queryByPasswd(buf);//从数据库比对密文是否一致
if(0 == ret)
{
printf("login succeed\n");
}
else
{
send(login_Flag_List[i].sfd,"1 passwd error,login again\n",27,0);
continue;
}
//登录成功
send(login_Flag_List[i].sfd,"4 You have been logined succeed\n",32,0);
login_Flag_List[i].login_Flag= 3;
}
else//若不是012,表示已经登录
{
return 0;
}
}
}