一.客戶端代碼:
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h> /* 套接字用的頭文件*/
#include <sys/socket.h>
#include <strings.h>
#include <netinet/in.h>//inet_addr ip地址格式的轉換
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#define R 1//user - register
#define L 2//user - login
#define Q 3//user - query
#define H 4//user - history
//定義通信雙方的信息結構體
#define N 32
typedef struct
{
int type;
char name[N];
char data[256];
}MSG;
/***************************************
**argc 表示參數個數 argv表示具體參數****
****************************************/
int do_register(int sockfd,MSG *msg) //需要文件描述符,傳輸結構體
{
msg->type = R;
printf("input name:");
scanf("%s",msg->name);
getchar();
printf("input password:");
scanf("%s",msg->data);
if(send(sockfd,msg,sizeof(MSG),0)<0)
{
printf("fail to send.\n");
return -1;
}
if(recv(sockfd,msg,sizeof(MSG),0)<0)
{
printf("fail to recv...\n");
return -1;
}
//ok! or usr alread exist
printf("%s\n",msg->data);
return 0;
}
int do_login(int sockfd,MSG *msg)
{
msg->type = L;
printf("input name:");
scanf("%s",msg->name);
getchar();
printf("input password:");
scanf("%s",msg->data);
if(send(sockfd,msg,sizeof(MSG),0)<0)//發送給服務器
{
printf("fail to send.\n");
return -1;
}
if(recv(sockfd,msg,sizeof(MSG),0)<0)//從服務器接受
{
printf("fail to recv...\n");
return -1;
}
if(strncmp(msg->data,"OK",3) == 0)
{
printf("login ok\n");
return 1;
}
else
{
printf("%s",msg->data);
}
return 0;//登錄成功
}
int do_query(int sockfd,MSG *msg)
{
msg->type = Q;
puts("-------------------");
while(1)
{
printf("Input word:");
scanf("%s",msg->data);
getchar();
//客戶端輸入# 返回上級菜單。
if(strncmp(msg->data,"#",1)==0)
break;
//將要查詢的單詞發送給服務器
if(send(sockfd,msg,sizeof(MSG),0)<0)
{
printf("fail to send.\n");
return -1;
}
//等待接受服務器,傳遞回來的單詞的註釋信息
if(recv(sockfd,msg,sizeof(MSG),0)<0)
{
printf("fail wo recv.\n");
return -1;
}
printf("%s\n",msg->data);
}
return 0;
}
int do_history(int sockfd,MSG *msg)
{
msg->type = H;
send(sockfd,msg,sizeof(MSG),0);
//接受服務器 傳遞回來的歷史信息
while(1)
{
recv(sockfd,msg,sizeof(MSG),0);
if(msg->data[0] == '\0')
break;
//輸出歷史信息
printf("%s\n",msg->data);
}
return 0;
}
// ./server 192.168.1.9 10000 ip地址用ifconfig查詢
int main(int argc,const char *argv[])
{
int n;
MSG msg;
int sockfd;//定義一個文件描述符,是在查看socket函數之後發現返回值是int
struct sockaddr_in serveraddr; //網絡信息填充的結構體,表示服務器地址。
if(argc != 3)//判斷傳參數個數
{
printf("Usage:%s serverip port.\n",argv[0]);
return -1;//參數輸出錯誤
}
//創建流式套接字
if((sockfd = socket(AF_INET,SOCK_STREAM,0)) < 0) //表示失敗AF_INET表示IPV4,SOCK_STREAM.
{
perror("fail to socket");//創建套接字失敗提醒
return -1;//失敗之後直接退出
}
//網絡信息填充
bzero(&serveraddr,sizeof(serveraddr));//鏈接服務器,先對服務器清空
serveraddr.sin_family = AF_INET;//填充結構體成員(查一下?)
serveraddr.sin_addr.s_addr = inet_addr(argv[1]);//傳輸入的ip地址
serveraddr.sin_port = htons(atoi(argv[2]));//htons將本地數據轉換爲網絡數據 atoi是將字符串轉化爲整數 10000是字符串
//鏈接服務器 調用connect函數
if(connect(sockfd,(struct sockaddr *)&serveraddr,sizeof(serveraddr))<0) //1.sockaddr 和sockaddr_in 的區別以及爲什麼強制轉換
{
perror("fail to connect");
return -1;
}
//一級菜單
while(1)
{
printf("****************************************************\n");
printf("* 1.register 2.login 3.quit *\n");
printf("****************************************************\n");
printf("Please choose:");
scanf("%d",&n);
getchar();//每次輸入玩有個垃圾字符去掉
switch(n)
{
case 1:
do_register(sockfd,&msg);
break;
case 2:
if(do_login(sockfd,&msg)==1)
{
goto next;
}
break;
case 3:
close(sockfd);//退出的話是把文件描述符關閉
exit(0);//正確退出
break;
default:
printf("Invalid data cmd.\n");
}
}
next :
while(1)
{
printf("***********************************************\n");
printf("* 1.query_word 2.history_record 3.quit\n");
printf("***********************************************\n");
printf("Please choose:");
scanf("%d",&n);
getchar();
switch(n)
{
case 1:
do_query(sockfd,&msg);
break;
case 2:
do_history(sockfd,&msg);
break;
case 3:
close(sockfd);
exit(0);
break;
default:
printf("Invalid data cmd.\n");//重新輸入就完了 所以不用break
}
}
return 0;
}
2.服務器代碼:
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h> /* 套接字用的頭文件*/
#include <sys/socket.h>
#include <string.h>
#include <netinet/in.h>//inet_addr ip地址格式的轉換
#include <arpa/inet.h>
#include <unistd.h>
#include <sqlite3.h>
#include <signal.h>
#include <time.h>
#define R 1//user - register
#define L 2//user - login
#define Q 3//user - query
#define H 4//user - history
#define DATABASE "my.db"
//定義通信雙方的信息結構體
#define N 32
typedef struct
{
int type;
char name[N];
char data[256];
}MSG;
/***************************************
**argc 表示參數個數 argv表示具體參數****
****************************************/
void do_register(int acceptfd,MSG *msg,sqlite3 *db) //需要文件描述符,傳輸結構體
{
char *errmsg;
char sql[128];
sprintf(sql,"insert into usr values('%s','%s');",msg->name,msg->data);
printf("%s\n",sql);
if(sqlite3_exec(db,sql,NULL,NULL,&errmsg)!=SQLITE_OK)
{
printf("%s\n",errmsg);
strcpy(msg->data,"usr name already exist.");
}
else
{
printf("client register ok!\n");
strcpy(msg->data,"OK!");
}
if(send(acceptfd,msg,sizeof(MSG),0)<0)
{
perror("fail to send");
return;
}
return;
}
int do_login(int acceptfd,MSG *msg,sqlite3 *db)
{
char sql[128] = {};
char *errmsg;
int nrow;
int ncloumn;
char **resultp;
sprintf(sql,"select *from usr where name = '%s' and pass = '%s';",msg->name,msg->data);
printf("%s\n",sql);
if(sqlite3_get_table(db,sql,&resultp,&nrow,&ncloumn,&errmsg)!=SQLITE_OK)
{
printf("%s\n",errmsg);
return -1;
}
else
{
printf("get table ok!\n");
}
//查詢成功,數據庫有此用戶
if(nrow == 1)
{
strcpy(msg->data,"OK");
send(acceptfd,msg,sizeof(MSG),0);
return 1;
}
if(nrow == 0)//密碼或者用戶名錯誤
{
strcpy(msg->data,"usr/passwd wrong.");
send(acceptfd,msg,sizeof(MSG),0);
return 0;
}
return 0;//登錄成功
}
int do_searchword(int acceptfd,MSG *msg,char word[])
{
FILE *fp;
int len = 0;
char temp[512];
int result;
char *p;
//打開文件,讀取文件,進行比對
if((fp = fopen("dict.txt","r"))==NULL)
{
perror("fail to fopen\n");
strcpy(msg->data,"Failed to open dict.txt\n");
send(acceptfd,msg,sizeof(MSG),0);
return -1;
}
//打印出客戶端要查詢的單詞
len = strlen(word);
printf("%s,len = %d\n",word,len);
//讀文件來查詢單詞
while(fgets(temp,512,fp)!=NULL)
{
result = strncmp(temp,word,len);
if(result > 0)
{
continue;
}
if(result < 0 || temp[len] !=' ')
{
break;
}
//表示找到查詢的單詞
p = temp + len;
while(*p == ' ')
{
p++;
}
//找到註釋,跳躍過所有空格
strcpy(msg->data,p);
printf("%s\n",msg->data);
send(acceptfd,msg,sizeof(MSG),0);
//拷貝完畢之後應該關閉文件
fclose(fp);
return 1;
}
strcpy(msg->data,"word cant be found!");
fclose(fp);
return 0;
}
int get_date(char *date)
{
time_t t;
struct tm *tp;
time(&t);
//進行時間格式轉換
localtime(&t);
sprintf(date,"%d-%d-%d %d:%d:%d",tp->tm_year+1990,tp->tm_mon+1,tp->tm_mday,tp->tm_hour,tp->tm_min,tp->tm_sec);
return 0;
}
int do_query(int acceptfd,MSG *msg,sqlite3 *db)
{
char word[64];
int found = 0;
char date[128];
char sql[128];
char *errmsg;
//拿出msg結構體中要查詢的單詞
strcpy(word,msg->data);
found = do_searchword(acceptfd,msg,word);
//找到單詞,此時應該將用戶名時間和單詞插入到歷史記錄表中去
if(found == 1)
{
//獲取系統時間
get_date(date);
sprintf(sql,"insert into record values('%s','%s','%s');",msg->name,date,word);
printf("%s\n",sql);
if(sqlite3_exec(db,sql,NULL,NULL,&errmsg)!=SQLITE_OK)
{
printf("%s\n",errmsg);
return -1;
}
}
else//表示沒有找到
{
strcpy(msg->data,"Not found!");
}
//將查詢的結果,發送給客戶端
send(acceptfd,msg,sizeof(MSG),0);
//將查詢的結果發送給客戶端
return 0;
}
//得到查詢結果,並且需要將歷史記錄發送給客戶端
int history_callback(void* arg,int f_num,char**f_value,char ** f_name)
{
int acceptfd;
MSG msg;
acceptfd = *((int *)arg);
sprintf(msg.data,"%s , %s",f_value[1],f_value[2]);
send(acceptfd,&msg,sizeof(MSG),0);
return 0;
}
int do_history(int acceptfd,MSG *msg,sqlite3 *db)
{
char sql[128] = {};
char *errmsg;
sprintf(sql,"select * from record where name = '%s';",msg->name);
//查詢數據庫;
if(sqlite3_exec(db,sql,history_callback,(void *)&acceptfd,&errmsg)!=SQLITE_OK)
{
printf("%s\n",errmsg);
}
else
{
printf("Query record done.\n");
}
//所有的記錄查詢完畢之後,給客戶端發出結束信息
msg->data[0] = '\0';
send(acceptfd,msg,sizeof(MSG),0);
return 0;
}
int do_client(int acceptfd,sqlite3 *db);
// ./server 192.168.1.9 10000 ip地址用ifconfig查詢
int main(int argc,const char *argv[])
{
int n;
pid_t pid;
MSG msg;
sqlite3 *db;
int acceptfd;
int sockfd;//定義一個文件描述符,是在查看socket函數之後發現返回值是int
struct sockaddr_in serveraddr; //網絡信息填充的結構體,表示服務器地址。
if(argc != 3)//判斷傳參數個數
{
printf("Usage:%s serverip port.\n",argv[0]);
return -1;//參數輸出錯誤
}
//打開數據庫
if(sqlite3_open(DATABASE,&db)!=SQLITE_OK)
{
printf("%s\n",sqlite3_errmsg(db));
return -1;
}
else
{
printf("open DATABASE success\n");
}
//創建流式套接字
if((sockfd = socket(AF_INET,SOCK_STREAM,0)) < 0) //表示失敗AF_INET表示IPV4,SOCK_STREAM.
{
perror("fail to socket");//創建套接字失敗提醒
return -1;//失敗之後直接退出
}
//網絡信息填充
bzero(&serveraddr,sizeof(serveraddr));//鏈接服務器,先對服務器清空
serveraddr.sin_family = AF_INET;//填充結構體成員(查一下?)
serveraddr.sin_addr.s_addr = inet_addr(argv[1]);//傳輸入的ip地址
serveraddr.sin_port = htons(atoi(argv[2]));//htons將本地數據轉換爲網絡數據 atoi是將字符串轉化爲整數 10000是字符串
//綁定函數
if(bind(sockfd,(struct sockaddr *)&serveraddr,sizeof(serveraddr))<0)
{
perror("fail to bind.\n");
return -1;
}
//將套接字設置爲監聽模式,等待客戶端鏈接請求到來
if(listen(sockfd,5)<0)
{
printf("fail to listen.\n");
return -1;
}
//處理殭屍進程
signal(SIGCHLD,SIG_IGN);
//一級菜單
while(1)
{
//accept接受連接請求 fork創建子進程通信
if((acceptfd = accept(sockfd,NULL,NULL))<0)
{
perror("fail to accept");
return -1;
}
if((pid = fork())<0)
{
perror("fail to fork");
return -1;
}
else if(pid == 0)//兒子進程
{
//處理客戶端具體消息
close(sockfd);//監聽套接字
do_client(acceptfd,db);
}
else//父親進程,用來接受客戶端的請求
{
close(acceptfd);
}
}
return 0;
}
int do_client(int acceptfd,sqlite3 *db)
{
MSG msg;
while(recv(acceptfd,&msg,sizeof(msg),0) > 0)
{
printf("type:%d\n",msg.type);
switch(msg.type)
{
case R:
do_register(acceptfd,&msg,db);
break;
case L:
do_login(acceptfd,&msg,db);
break;
case Q:
do_query(acceptfd,&msg,db);
break;
case H:
do_history(acceptfd,&msg,db);
break;
default:
printf("Invalid data msg.\n");
}
}
printf("client exit.\n");
close(acceptfd);
exit(0);
return 0;
}