(五)linux下c語言實現在線詞典

一.客戶端代碼:

#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;
}










































發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章