基于IO多路复用的电子词典

一:实现的功能:
    1.注册账号
    2.登录电子词典
        2.1.查词功能,一直进行查词服务,当输入'#'时,退出查词
        2.2.查看历史记录
        2.3.退出登录
    3.退出电子

二:用到的知识点
    1.IO:使用到了IO多路复用实现并发,打开关闭字典文件
    2.网络编程:使用到了TCP协议,socket套接字通信
    3.数据库:将用户的信息,以及查词记录存在数据库里

dictionary.h

#ifndef __DICTIONARY_H__
#define __DICTIONARY_H__

#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/select.h>
#include <string.h>
#include <sqlite3.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <time.h>

#define LOGIN 1
#define REGISTER 2
#define QUIT 3
#define FIND 1
#define HISTORY 2

//用于客户端和服务器之间发送信息的结构体,即数据包
typedef struct client_information{
    char name[20];
    char password[10];
    unsigned int choice;
    char answer[301];
}client_information_t;

//功能:客户端登陆操作,登录失败会让重新输入,直到成功为止
//参数:@choice:选项
//      @socketfd:用于通信的套接字
//返回值:成功返回0,失败返回-1
int client_login_dictionary(unsigned int choice,int socketfd);

//功能:服务器接收到客户端的登录请求,在数据库中查看是否有该信息,如果有登录成功,没有登陆失败
//参数:@dirctionary:字典的句柄
//      @service_recv:客户端接收到的客户的信息
//      @chatfd:用于通信的文件描述符
//返回值:登录成功返回0,登录失败返回-1
int service_login_dictionary(sqlite3 *dircetory_db,client_information_t service_recv,int chatfd);

//功能:客户端注册新用户
//参数: @choice:选项 
//       @socketfd:套接字文件描述符
//返回值:成功返回0,失败返回-1
int client_register_dictionary(unsigned int choice,int socketfd);

//功能:服务器端接收到客户端申请注册的处理函数,将用户信息放入数据库用户表和历史记录表中,并把成功或者失败的信息发送给客户端
//参数:@dictionary_db:句柄
//      @service_recv:服务器接收到的客户端的信息
//      @chatfd:用于通信的文件描述符
//返回值:添加成功返回0,添加失败返回-1
int service_register_dictionary(sqlite3* dictionary_db,client_information_t service_recv,int chatfd);

//功能:用户退出服务,并向服务器发送退出信号
//参数:@choice:选项
//      @socketfd:用于通信的文件描述符
//返回值:成功返回0,失败返回-1
int client_quit(unsigned int choice,int socketfd);

//功能:客户端发送单词信息给服务器,接受服务器发送过来的解释或者不存在信息
//参数:@login_choice:登录界面的选项
//      @chatfd:用于通信的文件描述符
//返回值:成功返回0,失败返回-1
int client_login_find(int login_choice,int socketfd);

//功能:接受客户端发送来的单词信息,从字典文件中查找到单词的解释信息,将解释信息发送给客户端,并保存客户的查询记录到history数据库中
//参数:@chatfd:用于通信的文件描述符
//      @choice_service_recv:初始界面接受的客户端信息
//      @login_service_recv:登录界面接受的客户端信息
//      @dictionary_db:字典数据库文件的句柄
//返回值:成功返回0,失败返回-1
int service_login_find(int chatfd,client_information_t choice_service_recv,client_information_t login_service_recv,sqlite3* dictionary_db);

//功能:客户端向服务器发送历史记录申请
//参数:@login_choice:登录界面的选择
//      @socketfd:用于通信的文件描述符
//返回值:成功返回0,失败返回-1
int client_login_history(int login_choice,int socketfd);

//功能:服务器向客户端发送历史记录信息
//参数:@dictionary:字典文件的句柄
//      @service_recv:接收到的客户端的信息结构体
//      @chatfd:用于通信的文件描述符
//返回值:成功返回0,失败返回-1
int service_login_history(sqlite3* dictionary,client_information_t login_recv,client_information_t service_recv,int chatfd);

//功能:客户端向服务器发送退出登录界面申请
//参数:@login_choice:登录界面的选项
//      @socketfd:用于通信的套接字
//返回值:成功返回0,失败返回-1
int client_login_quit(int login_choice,int socketfd);

//功能:服务器受到退出登录界面请求,服务器退出登录界面
//参数:@chatfd:用于通信的套接字
//      @login_service_recv:登录界面的选项
//返回值:成功返回0,失败返回-1
int service_login_quit(int chatfd,client_information_t choice_service_recv);

#endif

dictionary.c

#include "dictionary.h"

//功能:客户端登陆操作,登录失败会让重新输入,直到成功为止
//参数:@choice:选项
//      @socketfd:用于通信的套接字
//返回值:成功返回0,失败返回-1
int client_login_dictionary(unsigned int choice,int socketfd){
    //定义给服务器传输信息的结构体,登录操作,主要传输姓名,密码,还有选项
    client_information_t client_login_information;
    //初始化结构体
    memset(&client_login_information,0,sizeof(client_login_information));
    //循环输入姓名和密码,成功退出循环,失败将一直输入
    while(1){
        //清空结构体中的姓名,密码
        memset(client_login_information.name,0,sizeof(client_login_information.name));
        memset(client_login_information.answer,0,sizeof(client_login_information.answer));
        //将用户的选择赋值给结构体
        client_login_information.choice=choice;
        //用户输入姓名,以及密码信息
        printf("please input your name:");
        scanf("%s", client_login_information.name);
        printf("please input your password:");
        scanf("%s", client_login_information.password);
        //将姓名,密码,选项信息发送给服务器
        if(EOF==send(socketfd,&client_login_information,sizeof(client_login_information),0)){
            perror("client send login information error");
            return -1;
        }
        //接受服务器发送的登录成功与否的信息,成功退出循环,失败重新输入
        if(EOF==recv(socketfd,&client_login_information,sizeof(client_login_information),0)){
            perror("client receive service answer error");
            return -1;
        }
        //打印登录成功与否的信息
        printf("%s\n", client_login_information.answer);
        //登录不成功重新输入,登录成功退出循环
        if(0!=strcmp("login sucessful",client_login_information.answer)){
            printf("please input again\n");
            continue;
        }else{
            break;
        }
    }
    return 0;
}

//功能:服务器接收到客户端的登录请求,在数据库中查看是否有该信息,如果有登录成功,没有登陆失败
//参数:@dirctionary:字典的句柄
//      @service_recv:客户端接收到的客户的信息
//      @chatfd:用于通信的文件描述符
//返回值:登录成功返回0,登录失败返回-1
int service_login_dictionary(sqlite3 *dictionary_db,client_information_t service_recv,int chatfd){
    char **result=NULL;
    int nRow=0;
    int nColumn=0;
    char *errmsg=NULL;
    //定义从数据库中匹配姓名和密码的命令
    char login_information[128]={0};
    snprintf(login_information,sizeof(login_information),"SELECT * FROM user WHERE name='%s' AND password='%s';", service_recv.name,service_recv.password);
    if(SQLITE_OK!=sqlite3_get_table(dictionary_db,login_information,&result,&nRow,&nColumn,&errmsg)){
        printf("select user error,error message [%s]\n", errmsg);
        return -1;
    }
    //如果在数据库中匹配到相应的信息,nRow返回非0;如果没有匹配到,nRow返回0,以此判断用户是否登陆成功
    memset(service_recv.answer,0,sizeof(service_recv.answer));
    if(0==nRow){
        snprintf(service_recv.answer,sizeof(service_recv.answer),"input name or password error");
        printf("input name or password error\n");
    }else if(0!=nRow){
        snprintf(service_recv.answer,sizeof(service_recv.answer),"login sucessful");
        printf("[%d] %s login sucessful\n", chatfd,service_recv.name);
    }
    //将登录成功与否的信息发送给客户端
    if(EOF==send(chatfd,&service_recv,sizeof(service_recv),0)){
        perror("service send login answer error");
        return -1;
    }
    //用来给服务器判断是否登录失败
    if(0==strcmp("input name or password error",service_recv.answer)){
        return -1;
    }
    //释放result的空间
    sqlite3_free_table(result);
    return 0;
}

//功能:客户端注册新用户
//参数: @choice:选项 
//       @socketfd:套接字文件描述符
//返回值:成功返回0,失败返回-1
int client_register_dictionary(unsigned int choice,int socketfd){
    //定义客户端新用户的信息结构体,包括注册选项
    client_information_t new_user;
    memset(&new_user,0,sizeof(new_user));
    char password[10]={0};
    char name[20]={0};
    //输入新用户的姓名以及密码
    printf("please input your name:");
    scanf("%s", name);
    while(getchar()!='\n');
    printf("please input the passport:");
    scanf("%s", password);
    while(getchar()!='\n');
    new_user.choice=choice;
    //将姓名信息以及密码信息拷贝给结构体
    strcpy(new_user.name,name);
    strcpy(new_user.password,password);
    //发送新用户的信息给服务器,判断是否创建成功
    if(EOF==send(socketfd,&new_user,sizeof(new_user),0)){
        perror("client send new_user information error");
        return -1;
    }
    if(EOF==recv(socketfd,&new_user,sizeof(new_user),0)){
        perror("client receive answer error");
        return -1;
    }
    printf("%s\n", new_user.answer);
    return 0;
}

//功能:服务器端接收到客户端申请注册的处理函数,将用户信息放入数据库用户表和历史记录表中,并把成功或者失败的信息发送给客户端
//参数:@dictionary_db:句柄
//      @service_recv:服务器接收到的客户端的信息
//      @chatfd:用于通信的文件描述符
//返回值:添加成功返回0,添加失败返回-1
int service_register_dictionary(sqlite3* dictionary_db,client_information_t service_recv,int chatfd){ 
    char add_user[128]={0};
    char *errmsg=NULL;
    //组装数据库语句
    snprintf(add_user,sizeof(add_user),"INSERT INTO user VALUES('%s','%s');", service_recv.name,service_recv.password);
    if(SQLITE_OK!=sqlite3_exec(dictionary_db,add_user,NULL,NULL,&errmsg)){
        //判断注册是否失败,失败的话,把错误信息组装起来,并在服务器端打印错误信息
        snprintf(service_recv.answer,sizeof(service_recv.answer),"register error,error message [%s]", errmsg);
        printf("[%d] %s\n", chatfd,service_recv.answer);
    }else{
        //判断注册是否成功,成功的话把成功信息组装起来,并在服务器端打印注册成功信息
        snprintf(service_recv.answer,sizeof(service_recv.answer),"register sucessful");
        printf("[%d] %s %s\n", chatfd,service_recv.name,service_recv.answer);
    }
    //将注册信息成功与否的信息发送给服务器
    if(EOF==send(chatfd,&service_recv,sizeof(service_recv),0)){
        perror("service send answer message error");
        return -1;
    }
    //清理errmsg在sqlite3_exec函数中申请的空间
    sqlite3_free(errmsg);
    return 0;
}

//功能:用户退出服务,并向服务器发送退出信号
//参数:@choice:选项
//      @socketfd:用于通信的文件描述符
//返回值:成功返回0,失败返回-1
int client_quit(unsigned int choice,int socketfd){
    //定义客户端退出词典的结构体,主要就是一个选项
    client_information_t client_quit;
    client_quit.choice=choice;
    //给服务器发送该请求是为了让服务器端退出,让服务器端擦除该用户的文件描述符
    if(EOF==send(socketfd,&client_quit,sizeof(client_quit),0)){
        perror("client send quit information error");
        return -1;
    }
    //打印退出成功的信息
    printf("thanks for your using\n");
    return 0;
}

//功能:客户端发送单词信息给服务器,接受服务器发送过来的解释或者不存在信息
//参数:@login_choice:登录界面的选项
//      @chatfd:用于通信的文件描述符
//返回值:成功返回0,失败返回-1
int client_login_find(int login_choice,int socketfd){
    //定义发送单词的结构体
    client_information_t word;
    memset(&word,0,sizeof(word));
    //循环查找单词
    while(1){
        //输入要查找的单词
        memset(word.answer,0,sizeof(word.answer));
        printf("please input the word:");
        scanf("%s", word.answer);
        while(getchar()!='\n');
        //保留客户端进入登录界面以后的选项
        word.choice=login_choice;
        //发送单词信息
        if(EOF==send(socketfd,&word,sizeof(word),0)){
            perror("client send find word error");
            return -1;
        }
        //如果发送的单词信息是"#"将退出查找单词功能
        if(0==strcmp(word.answer,"#")){
            break;
        }
        //接受服务器发送来的单词解释信息
        if(EOF==recv(socketfd,&word,sizeof(word),0)){
            perror("client receive word error");
            return -1;
        }
        //打印解释信息
        printf("%s\n", word.answer);
    }
}

//功能:接受客户端发送来的单词信息,从字典文件中查找到单词的解释信息,将解释信息发送给客户端,并保存客户的查询记录到history数据库中
//参数:@chatfd:用于通信的文件描述符
//      @choice_service_recv:初始界面接受的客户端信息
//      @login_service_recv:登录界面接受的客户端信息
//      @dictionary_db:字典数据库文件的句柄
//返回值:成功返回0,失败返回-1
int service_login_find(int chatfd,client_information_t choice_service_recv,client_information_t login_service_recv,sqlite3* dictionary_db){
    //定义字典的文件指针,用来打开字典文件
    FILE* dictionary;
    if(NULL==(dictionary=fopen("./dict.txt","r"))){
        perror("open direction error");
        return -1;
    }
    char data[301]={0};
    char word[30]={0};
    int i=0;
    //定义flag标志,如果flag=1表示在字典文件中找到了这个单词,如果flag=0表示在字典中没有找到这个单词
    int flag=0;
    char find_set_history[128]={0};
    char *errmsg;
    //获取时间
    time_t secs;
    secs=time(&secs);
    struct tm *times;
    char local_time[20]={0};
    //循环查找单词
    while(1){
        //如果接收到的单词是"#"表示服务器端退出查词功能
        if(0==strcmp(login_service_recv.answer,"#")){
            break;
        }
        //将服务器发送过来的单词单独保存下来
        memset(word,0,sizeof(word));
        //在文件中循环寻找单词
        while(NULL!=fgets(data,sizeof(data),dictionary)){
            //截取字典中的单词信息
            memset(word,0,sizeof(word));
            for(i=0;data[i]!=' ';i++){
                word[i]=data[i];
            }
            //把截取到的单词和客户端发送过来的单词做比较,看是否是客户要查找的单词,如果是,打印解释信息
            if(0==strcmp(word,login_service_recv.answer)){
                //将解释信息拷贝到要发送给服务器的结构体中
                data[strlen(data)-1]='\0';
                strcpy(login_service_recv.answer,data);
                //设置标志位,退出循环
                flag=1;
                break;
            }
        }
        //如果把这个字典文件读完都没有找到该单词,打印未找到的信息,发送个客户端
        if(0==flag){
            strcpy(word,login_service_recv.answer);
            strcpy(login_service_recv.answer,"this word is not find");
        }
        //将解释信息发送给客户端
        if(EOF==send(chatfd,&login_service_recv,sizeof(login_service_recv),0)){
            perror("service send word information error");
        }
        //将客户的查找信息放到到历史记录的数据库文件中
        times=localtime(&secs);
        snprintf(local_time,sizeof(local_time),"%04d-%02d-%02d %02d:%02d:%02d", times->tm_year+1900,times->tm_mon+1,times->tm_mday,times->tm_hour,times->tm_min,times->tm_sec);
        snprintf(find_set_history,sizeof(find_set_history),"INSERT INTO history VALUES('%s','%s','%s');", choice_service_recv.name,local_time,word);
        if(SQLITE_OK!=sqlite3_exec(dictionary_db,find_set_history,NULL,NULL,&errmsg)){
            printf("[%d] insert history error,error message [%s]\n", chatfd,errmsg);
            return -1;
        }
        //打印查找结果的信息
        printf("[%d] %s\n", chatfd,login_service_recv.answer);
        break;
    }
    return 0;
}

//功能:客户端向服务器发送历史记录申请
//参数:@login_choice:登录界面的选择
//      @socketfd:用于通信的文件描述符
//返回值:成功返回0,失败返回-1
int client_login_history(int login_choice,int socketfd){
    //创建客户端申请历史纪录的信息结构体
    client_information_t history;
    memset(&history,0,sizeof(history));
    history.choice=login_choice;
    //把申请历史记录的选项发送给服务器
    if(EOF==send(socketfd,&history,sizeof(history),0)){
        perror("client send history error");
        return -1;
    }
    //循环接受历史记录
    while(1){
        memset(&history,0,sizeof(history));
        if(EOF==recv(socketfd,&history,sizeof(history),0)){
            perror("client recv history information error");
        }
        printf("%s\n", history.answer);
        //当接收到的choice是0时表示接受完,退出循环
        if(0==history.choice){
            break;
        }
    }
    return 0;
}

//功能:服务器向客户端发送历史记录信息
//参数:@dictionary:字典文件的句柄
//      @service_recv:接收到的客户端的信息结构体
//      @chatfd:用于通信的文件描述符
//返回值:成功返回0,失败返回-1
int service_login_history(sqlite3* dictionary,client_information_t login_recv,client_information_t service_recv,int chatfd){
    char history_get[128]={0};
    char **result=NULL;
    int nRow=0;
    int nColumn=0;
    char *errmsg=NULL;
    //组装查询历史记录的数据库语句
    snprintf(history_get,sizeof(history_get),"SELECT time,data FROM history WHERE name='%s';",service_recv.name);
    if(SQLITE_OK!=sqlite3_get_table(dictionary,history_get,&result,&nRow,&nColumn,&errmsg)){
        printf("select history error,error message [%s]\n", errmsg);
        return -1;
    }
    //遍历result,将result里面的内容组装起来循环发送给服务器,每一次发送的是一行数据,
    //由nRow判断有多少行,当最后一次传输时,将choice赋值0,告诉客户端传输结束
    int i=0;
    int index=0;
    //循环打印数据发送给客户端
    for(i=0;i<=nRow;i++){
        memset(login_recv.answer,0,sizeof(login_recv.answer));
        snprintf(login_recv.answer,sizeof(login_recv.answer),"%s %s", result[index],result[index+1]);
        printf("[%d] %s %s\n", chatfd,result[index],result[index+1]);
        index=index+2;
        if(i==nRow){
            login_recv.choice=0;
        }
        if(EOF==send(chatfd,&login_recv,sizeof(login_recv),0)){
            perror("send history data error");
            return -1;
        }
    }
    return 0;
}

//功能:客户端向服务器发送退出登录界面申请
//参数:@login_choice:登录界面的选项
//      @socketfd:用于通信的套接字
//返回值:成功返回0,失败返回-1
int client_login_quit(int login_choice,int socketfd){
    //定义退出登录界面的信息结构体,主要是用来传输退出选项
    client_information_t quit;
    quit.choice=login_choice;
    //发送退出信息给服务器
    if(EOF==send(socketfd,&quit,sizeof(quit),0)){
        perror("client send quit choice error");
        return -1;
    }
    //该步骤是为了防止客户端连续两次发送信息给服务器,造成的接受数据的延迟
    //接受到"quit",表示服务器侧接收到退出信息,客户端也退出
    memset(quit.answer,0,sizeof(quit.answer));
    if(EOF==recv(socketfd,&quit,sizeof(quit),0)){
        perror("client receive quit information error");
        return -1;
    }
    if(0==strcmp(quit.answer,"quit")){
        printf("I'm quiting login\n");
    }
    return 0;
}

//功能:服务器受到退出登录界面请求,服务器退出登录界面
//参数:@chatfd:用于通信的套接字
//      @login_service_recv:登录界面的选项
//返回值:成功返回0,失败返回-1
int service_login_quit(int chatfd,client_information_t login_service_recv){
    //该步骤是防止客户端连续发送两次数据造成的接受数据延迟
    memset(login_service_recv.answer,0,sizeof(login_service_recv.answer));
    strcpy(login_service_recv.answer,"quit");
    if(EOF==send(chatfd,&login_service_recv,sizeof(login_service_recv),0)){
        perror("service send quit informationerror");
        return -1;
    }
    printf("[%d] quiting login\n", chatfd);
    return 0;
}

service.c

#include "dictionary.h"

int main(int argc,const char * argv[]){
    //入参合理性判断
    if(argc!=3){
        printf("input error\n");
        printf("Uasge:./%s ip port\n", argv[0]);
        return -1;
    }
    //创建套接字
    int socketfd;
    if(EOF==(socketfd=socket(AF_INET,SOCK_STREAM,0))){
        perror("create socket error");
        return -1;
    }
    //创建服务器网络信息结构体
    struct sockaddr_in servicedata;
    servicedata.sin_family=AF_INET;
    servicedata.sin_addr.s_addr=inet_addr(argv[1]);
    servicedata.sin_port=htons(atoi(argv[2]));
    socklen_t service_len=sizeof(servicedata);
    //设置端口复用
    int on=1;
    socklen_t on_size=sizeof(on);
    if(EOF==setsockopt(socketfd,SOL_SOCKET,SO_REUSEADDR,&on,on_size)){
        perror("set port reuse error");
        return -1;
    }
    //将套接字和服务器网络信息结构体绑定
    if(EOF==bind(socketfd,(struct sockaddr *)&servicedata,service_len)){
        perror("servicedata bind error");
        return -1;
    }
    //将套接字设置为被动监听状态
    if(EOF==listen(socketfd,10)){
        perror("socket listen error");
        return -1;
    }
    //创建客户端网络信息结构体
    struct sockaddr_in clientdata;
    socklen_t client_len=sizeof(clientdata);
    //创建文件描述符集合
    fd_set readfds;
    fd_set readfds_temp;
    FD_ZERO(&readfds);
    FD_ZERO(&readfds_temp);
    //定义最大文件描述符
    int fd_max=0;
    //将套接字文件描述符放入集合中,并更新最大文件描述符
    FD_SET(socketfd,&readfds);
    fd_max = fd_max > socketfd ? fd_max : socketfd;
    //打开数据库文件
    sqlite3 *dictionary_db;
    if(SQLITE_OK!=sqlite3_open("./dictionary.db", &dictionary_db)){
        printf("open sqlite3 file error,error message [%s]\n", sqlite3_errmsg(dictionary_db));
        return -1;
    }
    //创建用户信息的表
    if(SQLITE_OK!=sqlite3_exec(dictionary_db,"CREATE TABLE IF NOT EXISTS user(name CHAR PRIMARY KEY,password CHAR);",NULL,NULL,NULL)){
        printf("create user information table error,error message [%s]\n", sqlite3_errmsg(dictionary_db));
        return -1;
    }
    //创建用户使用历史记录的表
    if(SQLITE_OK!=sqlite3_exec(dictionary_db,"CREATE TABLE IF NOT EXISTS history(name CHAR, time CHAR, data CHAR);",NULL,NULL,NULL)){
        printf("create user information table error,error message [%s]\n", sqlite3_errmsg(dictionary_db));
        return -1;
    }
    //判断是否有文件描述符就绪
    int select_ret=0;
    int i=0;
    int chatfd;
    int recv_ret=0;
    client_information_t choice_service_recv;
    client_information_t login_service_recv;
    memset(&choice_service_recv,0,sizeof(choice_service_recv));
    memset(&login_service_recv,0,sizeof(login_service_recv));
    while(1){
        //将文件描述符集合的数据传输给副本,擦除使用
        readfds_temp=readfds;
        if(EOF==(select_ret=select(fd_max+1,&readfds_temp,NULL,NULL,NULL))){
            perror("service select error");
            return -1;
        }
        //循环遍历哪些文件描述符就绪了
        for(i=0;i<fd_max+1 && select_ret!=0;i++){
            if(0!=FD_ISSET(i,&readfds_temp)){
                select_ret--;
                //套接字文件描述符就绪
                if(i==socketfd){
                    if(EOF==(chatfd=accept(socketfd,(struct sockaddr *)&clientdata,&client_len))){
                        perror("service linked error");
                        return -1;
                    }
                    printf("[%d] is linking\n", chatfd);
                    FD_SET(chatfd,&readfds);
                    fd_max = fd_max > chatfd ? fd_max : chatfd;
                }else{
                    //有客户端发送消息,接受客户端发送过来的信息
                    if(EOF==(recv_ret=recv(i,&choice_service_recv,sizeof(choice_service_recv),0))){
                        perror("service receive information error");
                        return -1;
                    }
                    //判断客户端是否是断开连接,断开连接的话要将客户端的文件描述符关闭并将它从文件描述符集合中擦除
                    if(0==recv_ret){
                        printf("[%d] is breaking\n", i);
                        if(EOF==close(i)){
                            perror("close chatfd error");
                            return -1;
                        }
                        FD_CLR(i,&readfds);
                        break;
                    }
                    //通过客户端发送过来的选项判断执行什么操作
                    switch(choice_service_recv.choice){
                        //登录操作
                        case LOGIN:
                            //判断用户是否登录成功,如果登录失败,直接break,因为循环里面的东西是用户登陆成功时的操作
                            memset(choice_service_recv.answer,0,sizeof(choice_service_recv.answer));
                            if(EOF==service_login_dictionary(dictionary_db,choice_service_recv,i)){
                                break;
                            }
                            //登录成功进入登录成功界面
                            while(1){
                                //主要接收登录界面的选项,还有相关操作的数据包
                                if(EOF==recv(i,&login_service_recv,sizeof(login_service_recv),0)){
                                    perror("service receive login information error");
                                    return -1;
                                }
                                //根据接受到的选项判断执行什么操作
                                switch(login_service_recv.choice){
                                    //查词操作
                                    case FIND:
                                        service_login_find(i,choice_service_recv,login_service_recv,dictionary_db);
                                        break;
                                    //查看历史纪录操作
                                    case HISTORY:
                                        service_login_history(dictionary_db,login_service_recv,choice_service_recv,i);
                                        break;
                                    //退出操作
                                    case QUIT:
                                        service_login_quit(i,login_service_recv);
                                        break;
                                }
                                //判断是否时退出选项,是的话退出循环
                                if(QUIT==login_service_recv.choice){
                                    break;
                                }
                            }
                            break;
                        //注册操作
                        case REGISTER:
                            service_register_dictionary(dictionary_db,choice_service_recv,i);
                            break;
                        //退出操作
                        case QUIT:
                            //打印退出客户端退出信息,并将该文件描述符从集合中擦除
                            printf("[%d] is quiting\n", i);
                            if(EOF==close(i)){
                                perror("close chatfd error");
                                return -1;
                            }
                            FD_CLR(i,&readfds);
                            break;
                    }
                }
            }
        }
    }
    //关闭套接字
    if(EOF==close(socketfd)){
        perror("close sockefd error");
        return -1;
    }
    return 0;
}

client.c

#include "dictionary.h"

int main(int argc,const char * argv[]){
    //入参合理性判断
    if(argc!=3){
        printf("input error\n");
        printf("Usage:./%s ip port\n", argv[0]);
        return -1;
    }
    //创建套接字
    int socketfd;
    if(EOF==(socketfd=socket(AF_INET,SOCK_STREAM,0))){
        perror("create socket error");
        return -1;
    }
    //创建服务器网络信息结构体
    struct sockaddr_in servicedata;
    servicedata.sin_family=AF_INET;
    servicedata.sin_addr.s_addr=inet_addr(argv[1]);
    servicedata.sin_port=htons(atoi(argv[2]));
    socklen_t service_len=sizeof(servicedata);
    //与服务器创建连接
    if(EOF==connect(socketfd,(struct sockaddr *)&servicedata,service_len)){
        perror("client connect error");
        return -1;
    }
    //字典操作
    unsigned int choice=0;
    unsigned int login_choice=0;
    int ret=0;
    while(1){
        //打印界面,并让用户输入自己的选项
        printf("***********************************************\n");
        printf("**********1.login 2.register 3.quit************\n");
        printf("***********************************************\n");
        printf("please input your choice:");
        ret=scanf("%d", &choice);
        while(getchar()!='\n');//处理垃圾字符
        if(ret!=1){
            printf("input choice number error\n");
            printf("please input again\n");
            continue;
        }
        //根据用户的选择来执行相对应的操作
        switch(choice){
            //登录操作
            case LOGIN:
                //登录函数,登录不成功将一直输入姓名,密码,登录成功,执行下面的代码
                client_login_dictionary(choice,socketfd);
                //登录成功进入该界面
                while(1){
                    printf("***********************************************\n");
                    printf("*************1.find 2.history 3.quit***********\n");
                    printf("***********************************************\n");
                    //输入登录界面的选项
                    printf("please input yout login_choice:");
                    ret=scanf("%d", &login_choice);
                    while(getchar()!='\n');
                    if(1!=ret){
                        printf("input login_choice number error\n");
                        printf("please input again\n");
                        continue;
                    }
                    if(login_choice<1 || login_choice >3){
                        printf("input login_choice error\n");
                        printf("please input again\n");
                        continue;
                    }
                    //通过选项判断执行什么操作
                    switch(login_choice){
                        //查词操作
                        case FIND:
                            client_login_find(login_choice,socketfd);
                            break;
                        //查询历史记录操作
                        case HISTORY:
                            client_login_history(login_choice,socketfd);
                            break;
                        //退出登录界面操作
                        case QUIT:
                            client_login_quit(login_choice,socketfd);
                            break;
                    }
                    //判断选项是否是退出选项,是的话,退出循环
                    if(QUIT==login_choice){
                        break;
                    }
                }
                break;
            case REGISTER:
                //注册操作
                client_register_dictionary(choice,socketfd);
                break;
                //退出操作
            case QUIT:
                client_quit(choice,socketfd);
                break;
            default:
                printf("input choice error\n");
                printf("please input again(1<=choice<=3))\n");
                continue;
        }
        if(choice==QUIT){
            break;
        }
    }
    //退出后关闭套接字
    if(EOF==close(socketfd)){
        perror("close socketfd error");
        return -1;
    }
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值