目录

一、前言

二、项目介绍

三、功能实现

3.1. 用户注册

3.1.1 功能演示

3.1.2 功能函数实现

3.2. 用户登录

3.2.1 功能演示

3.2.2 功能函数实现

3.3. 查询单词

3.3.1 功能演示

3.3.2 功能函数实现

3.4. 历史查询

3.4.1 功能演示

3.4.2 功能函数实现

四、代码实现

4.1 服务器程序 server.c

4.2 客户端程序 client.c

五、总结


一、前言

今天是培训的第62天,这两个月的时间里,先后学习了C语言、shell编程、makefile编程、数据结构、IO进程和网络编程,时间紧任务重,但只学习知识不去运用肯定是不行的,所以通过今天这个项目,对所学知识进行回顾和融会贯通,同样也是一个短期的成果展示。

二、项目介绍

主要利用数据库和TCP网络编程,制作一个服务器和客户端程序,实现:一用户注册,二用户登录,三用户在线查询单词,四查询用户历史记录功能。

服务器程序等待客户端连接,接收客户端发来的数据,根据不同的数据包类型,决定是执行注册操作、还是登录操作,如果是注册程序还需遍历数据库用户表,判断用户名是否重复注册,如果是登录操作还需与数据库用户表对比,判断用户名和密码是否匹配;接着如果用户登录成功,再根据数据包类型,决定执行单词查询操作,或者查询历史操作,并将期间该用户的查询到的单词数据库入表,以提供后续的查询历史操作。

客户端程序提供可视化界面,提示用户按需求操作,根据用户选择组装相应的数据包发送给服务器,根据数据的应答数据包,显示对应的提示,用户登录成功后,跳转到单词查询的二级界面,以提供接下来的服务。

三、功能实现

3.1. 用户注册

3.1.1 功能演示

3.1.2 功能函数实现

        server.c

//用户注册函数
void do_register(int connectfd, msg_t *msg, sqlite3 *db)
{char sqlstr[STR_NUM] = {0};char *errmsg;//使用sqlite3_exec函数调用插入函数判断是否能够插入成功//由于用户名设置为主键,所以如果用户名已经存在就会报错sprintf(sqlstr, "INSERT INTO usr VALUES('%s', '%s')", msg->name, msg->data);if (sqlite3_exec(db, sqlstr, NULL, NULL, &errmsg) != SQLITE_OK){sprintf(msg->data, "User %s already exist!!!", msg->name);}else{strcpy(msg->data, "Register was successful!!!");}if (-1 == send(connectfd, msg, sizeof(msg_t), 0)){perror("fail to send");exit(-1);}return;
}

    client.c


//用户注册函数
void do_register(int socketfd, msg_t *msg)
{//指定操作码msg->type = REGISTER;//输入用户名printf("input your name:");scanf("%s", msg->name);//输入密码printf("input your password:");scanf("%s", msg->data);//发送数据if (-1 == send(socketfd, msg, sizeof(msg_t), 0)){perror("fail to send");exit(-1);}//接收数据并输出if (-1 == recv(socketfd, msg, sizeof(msg_t), 0)){perror("fail to recv");exit(-1);}printf("register : %s\n", msg->data);return;
}

3.2. 用户登录

3.2.1 功能演示

3.2.2 功能函数实现

server.c

//用户登录函数
void do_login(int connectfd, msg_t *msg, sqlite3 *db)
{char sqlstr[STR_NUM] = {0};char *errmsg, **result;int nrow, ncolumn;//通过sqlite3_get_table函数查询记录是否存在sprintf(sqlstr, "select * from usr where name = '%s' and pass = '%s'", msg->name, msg->data);if (sqlite3_get_table(db, sqlstr, &result, &nrow, &ncolumn, &errmsg) != SQLITE_OK){printf("error : %s\n", errmsg);}//通过nrow参数判断是否能够查询到疾记录,如果值为0,则查询不到,如果值为非0,则查询到if (nrow == 0){strcpy(msg->data, "name or password is wrong!!!");}else{strncpy(msg->data, "Login was successful", sizeof(msg->data));}if (-1 == send(connectfd, msg, sizeof(msg_t), 0)){perror("fail to send");exit(-1);}return;
}

        client.c

//用户登录函数
int do_login(int socketfd, msg_t *msg)
{//设置操作码msg->type = LOGIN;//输入用户名printf("input your name:");scanf("%s", msg->name);//输入密码printf("input your password:");scanf("%s", msg->data);//发送数据给服务器if (-1 == send(socketfd, msg, sizeof(msg_t), 0)){perror("fail to send");exit(-1);}//接收服务器发送的数据if (-1 == recv(socketfd, msg, sizeof(msg_t), 0)){perror("fail to recv");exit(-1);}//判断是否登录成功//用21  可以防止“xx”后面有其他垃圾字符//登录成功返回1if (0 == strncmp(msg->data, "Login was successful", 21)){printf("Login was successful!!!\n");return 1;}//登录失败返回0printf("login : %s\n", msg->data);return 0;
}

3.3. 查询单词

3.3.1 功能演示

3.3.2 功能函数实现

将存放单词释义的文件存入数据库的dict表中

#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <sqlite3.h>
#include <unistd.h>#define DATABASE "my.db"int main(int argc, char const *argv[])
{//把文件导入数据库中sqlite3 *db;FILE *fp;if(SQLITE_OK !=sqlite3_open(DATABASE,&db)){perror("sqlite err");exit(1);}fp = fopen("dict.txt","r");if(fp==NULL){perror("err");exit(1);}char str[300]={0};char word[50]={0};char introduct[250]={0};char *errmsg;char sql[500]={0};sprintf(sql,"drop table dict");if(SQLITE_OK !=sqlite3_exec(db,sql,NULL,NULL,&errmsg)){printf("drop table dict error!!!\n");}else{printf("drop table dict yes!!!\n");}sprintf(sql,"create table if not exists dict(word text,translation text)");if(SQLITE_OK !=sqlite3_exec(db,sql,NULL,NULL,&errmsg)){printf("表已经存在!!!\n");}else{printf("表创建成功!!!\n");}int count=0;while ((fgets(str,300,fp))!=NULL){//usleep(10000);memset(word, 0, 300);int i=0;char *p=str;str[strlen(str)-1] = '\0';//printf("str = [%s]\n",str);while (*p!=' '){word[i]=*p;p++;i++;}word[i]='\0';p++;while(*p==' ' && *p != '\0'){p++;}//处理 两个字段值中的 单引号  sqlite3 数据库  text 字段//不能插入带有单引号的字符串   如  one's  转换成   one.s 再插入char *temp = p;while(*temp != '\0'){if(*temp == '\''){*temp = '.';}temp++;}temp = word;while(*temp != '\0'){if(*temp == '\''){*temp = '.';}temp++;}strcpy(introduct, p);introduct[strlen(introduct)-1]='\0';printf("insert count = [%d]\tword:[%s]\t\ttranslation:[%s]\n", count, word, introduct);//插入数据sprintf(sql,"INSERT INTO dict (word,translation) VALUES('%s','%s')",word,introduct);int ret =-1;if(SQLITE_OK !=(ret=sqlite3_exec(db,sql,NULL,NULL,&errmsg))){printf("sql err : %s\n", errmsg); exit(1); }count++;memset(str, 0, 300);}printf("sum count = %d , process end......................\n", count);return 0;
}

        server.c

//单词查询函数
void do_query(int connectfd, msg_t *msg, sqlite3 *db)
{char sqlstr[TXT_NUM], *errmsg;int found = 0;char date[TXT_NUM], word[TXT_NUM];strcpy(word, msg->data); //备份一份,历史记录时要用//通过found保存查询结果,成功返回1,失败返回0found = do_searchword(connectfd, msg, db);//如果执行成功,还需要保存历史记录到数据库的record表中if (found == 1){//获取当前系统日期及时间get_date(date);//通过sqlite3_exec函数插入数据sprintf(sqlstr, "insert into record values('%s', '%s', '%s')", msg->name, date, word);if (sqlite3_exec(db, sqlstr, NULL, NULL, &errmsg) != SQLITE_OK){printf("error : %s\n", errmsg);}}if (-1 == send(connectfd, msg, sizeof(msg_t), 0)){perror("fail to send");exit(-1);}return;
}int do_searchword(int connectfd, msg_t *msg, sqlite3 *db)
{char sqlstr[STR_NUM] = {0};char *errmsg, **result;int nrow, ncolumn;//通过sqlite3_get_table函数查询记录是否存在sprintf(sqlstr, "select explanation from dict where word = '%s'", msg->data);if (sqlite3_get_table(db, sqlstr, &result, &nrow, &ncolumn, &errmsg) != SQLITE_OK){printf("error : %s\n", errmsg);}//通过nrow参数判断是否能够查询到疾记录,如果值为0,则查询不到,如果值为非0,则查询到if (nrow == 0){strcpy(msg->data, "not found!!!");return 0;}else{//此时msg->data里存的是单词的解释strcpy(msg->data, result[ncolumn]);}return 1;
}//获取当前系统日期和时间的函数
void get_date(char *data)
{time_t t;struct tm *tp;time(&t);tp = localtime(&t);sprintf(data, "%d-%d-%d %d:%d:%d", 1900 + tp->tm_year, 1 + tp->tm_mon, tp->tm_mday,tp->tm_hour, tp->tm_min, tp->tm_sec);
}

        client.c

//单词查询函数
void do_query(int socketfd, msg_t *msg)
{msg->type = QUERY;puts("-----------------------------");while (1){printf("input word (if # is end): ");scanf("%s", msg->data);//如果输入的是#,返回上一级if (0 == strcmp(msg->data, "#")){break;}if (-1 == send(socketfd, msg, sizeof(msg_t), 0)){perror("fail to send");exit(-1);}if (-1 == recv(socketfd, msg, sizeof(msg_t), 0)){perror("fail to recv");exit(-1);}printf(">>> %s\n", msg->data);}return;
}

3.4. 历史查询

3.4.1 功能演示

3.4.2 功能函数实现

        server.c

//查询历史记录的函数
void do_history(int connectfd, msg_t *msg, sqlite3 *db)
{char sqlstr[TXT_NUM], *errmsg;//查询历史表,sqlite3_exec回调函数实现表的遍历sprintf(sqlstr, "select * from record where name = '%s'", msg->name);if (sqlite3_exec(db, sqlstr, history_callback, (void *)&connectfd, &errmsg) != SQLITE_OK){printf("error : %s\n", errmsg);sqlite3_free(errmsg);}//发送结束标志,客户端接收到这条说明单词的解释发送完毕strcpy(msg->data, "**OVER**");if (-1 == send(connectfd, msg, sizeof(msg_t), 0)){perror("fail to send");exit(-1);}return;
}//通过回调函数发送时间和单词
int history_callback(void *arg, int f_num, char **f_value, char **f_name)
{int connectfd;msg_t msg;connectfd = *(int *)arg;sprintf(msg.data, "%s : %s", f_value[1], f_value[2]);if (-1 == send(connectfd, &msg, sizeof(msg_t), 0)){perror("fail to send");exit(-1);}return 0;
}

        client.c

//查询历史记录的函数
void do_history(int socketfd, msg_t *msg)
{msg->type = HISTORY;if (-1 == send(socketfd, msg, sizeof(msg_t), 0)){perror("fail to send");exit(-1);}while (1){if (-1 == recv(socketfd, msg, sizeof(msg_t), 0)){perror("fail to recv");exit(-1);}if (0 == strcmp(msg->data, "**OVER**")){break;}printf("%s\n", msg->data);}return;
}

四、代码实现

4.1 服务器程序 server.c

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sqlite3.h>
#include <signal.h>
#include <time.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/wait.h>//防止魔鬼数字
#define NAME_NUM 16
#define DATA_NUM 256
#define TXT_NUM 128
#define STR_NUM 512//数据包类型宏定义
#define REGISTER 1 //注册
#define LOGIN 2    //登录
#define QUERY 3    //查询
#define HISTORY 4  //历史#define DATABASE "my.db"//数据包结构体
typedef struct _MSG
{int type;           //存放数据包的类型char name[NAME_NUM]; //存放用户姓名char data[DATA_NUM]; //根据类型存放其他文本信息
} msg_t;void do_register(int connectfd, msg_t *msg, sqlite3 *db);
void do_login(int connectfd, msg_t *msg, sqlite3 *db);
void do_query(int connectfd, msg_t *msg, sqlite3 *db);
void do_history(int connectfd, msg_t *msg, sqlite3 *db);
void do_client(int connectfd, sqlite3 *db);
int do_searchword(int connectfd, msg_t *msg, sqlite3 *db);
void get_date(char data[]);
int history_callback(void *arg, int f_num, char **f_value, char **f_name);//子进程结束时,发送信号,父进程收到信号后为子进程回收资源
void handler(int sig)
{wait(NULL); //阻塞或非阻塞都可以,因为时间很短,不影响程序执行
}int main(int argc, char *argv[])
{int listenfd, connectfd;pid_t pid;sqlite3 *db;// 1. 入参合理性检查if (3 != argc){printf("Usage : %s <IP> <PORT>\n", argv[0]);exit(-1);}// 2. 打开数据库if (SQLITE_OK != (sqlite3_open(DATABASE, &db))){printf("error : %s\n", sqlite3_errmsg(db));exit(-1);}// 2.1 在数据库中创建 usr 和 record 表char sqlstr[STR_NUM] = {0};char *errmsg;// IF NOT EXISTS: 有则打开 , 无则创建sprintf(sqlstr, "CREATE TABLE IF NOT EXISTS usr (name text PRIMARY KEY, pass TEXT);");if (SQLITE_OK != sqlite3_exec(db, sqlstr, NULL, NULL, &errmsg)){printf("create table usr error!!!\n");}sprintf(sqlstr, "CREATE TABLE IF NOT EXISTS record (name TEXT, date TEXT, word TEXT);");if (SQLITE_OK != sqlite3_exec(db, sqlstr, NULL, NULL, &errmsg)){printf("create table record error!!!\n");}// 3. 创建套接字if ((-1 == (listenfd = socket(PF_INET, SOCK_STREAM, 0)))){perror("fail to socket");exit(-1);}// 4. 创建网络信息结构体struct sockaddr_in server_addr;memset(&server_addr, 0, sizeof(server_addr));server_addr.sin_family = PF_INET;server_addr.sin_addr.s_addr = inet_addr(argv[1]);server_addr.sin_port = htons(atoi(argv[2]));socklen_t server_addr_len = sizeof(server_addr);// 5. 绑定网络信息结构体if (-1 == bind(listenfd, (struct sockaddr *)&server_addr, server_addr_len)){perror("fail to bind");exit(-1);}// 6. 将套接字设置为被动监听状态if (-1 == listen(listenfd, 5)){perror("fail to listen");exit(-1);}signal(SIGCHLD, handler); //声明信号处理函数,处理僵尸进程while (1){// 7. 父进程循环接收客户端连接if (-1 == (connectfd = accept(listenfd, NULL, NULL))){perror("fail to accept");exit(-1);}// 7.1 当有客户端连接时,父进程创建子进程if (-1 == (pid = fork())){perror("fail to fork");exit(-1);}else if (0 == pid){// 7.2 子进程负责与客户端交换do_client(connectfd, db);}else{// 7.3 父进程关闭当前套接字,等待下一个客户端连接close(connectfd);}}// 8. 关闭套接字// 此服务器程序执行不到这里,但有开有关,养成好习惯close(listenfd);return 0;
}//客户端分支处理函数(交通枢纽)
void do_client(int connectfd, sqlite3 *db)
{msg_t msg;//注意:如果对端关闭,此处的recv会立即返回0//如果仅设置recv返回-1为客户端退出的话,客户端实际退出时,会疯狂返回type的随机值while (0 < recv(connectfd, &msg, sizeof(msg_t), 0)) //接收客户端信息{printf("type = %d\n", msg.type);printf("type = %s\n", msg.data);switch (msg.type){case REGISTER:do_register(connectfd, &msg, db);break;case LOGIN:do_login(connectfd, &msg, db);break;case QUERY:do_query(connectfd, &msg, db);break;case HISTORY:do_history(connectfd, &msg, db);break;}}printf("Client quit\n");//子进程结束,发送SIGCHLD信号,父进程收到信号,回收资源exit(0);return;
}//用户注册函数
void do_register(int connectfd, msg_t *msg, sqlite3 *db)
{char sqlstr[STR_NUM] = {0};char *errmsg;//使用sqlite3_exec函数调用插入函数判断是否能够插入成功//由于用户名设置为主键,所以如果用户名已经存在就会报错sprintf(sqlstr, "INSERT INTO usr VALUES('%s', '%s')", msg->name, msg->data);if (sqlite3_exec(db, sqlstr, NULL, NULL, &errmsg) != SQLITE_OK){sprintf(msg->data, "User %s already exist!!!", msg->name);}else{strcpy(msg->data, "Register was successful!!!");}if (-1 == send(connectfd, msg, sizeof(msg_t), 0)){perror("fail to send");exit(-1);}return;
}//用户登录函数
void do_login(int connectfd, msg_t *msg, sqlite3 *db)
{char sqlstr[STR_NUM] = {0};char *errmsg, **result;int nrow, ncolumn;//通过sqlite3_get_table函数查询记录是否存在sprintf(sqlstr, "select * from usr where name = '%s' and pass = '%s'", msg->name, msg->data);if (sqlite3_get_table(db, sqlstr, &result, &nrow, &ncolumn, &errmsg) != SQLITE_OK){printf("error : %s\n", errmsg);}//通过nrow参数判断是否能够查询到疾记录,如果值为0,则查询不到,如果值为非0,则查询到if (nrow == 0){strcpy(msg->data, "name or password is wrong!!!");}else{strncpy(msg->data, "Login was successful", sizeof(msg->data));}if (-1 == send(connectfd, msg, sizeof(msg_t), 0)){perror("fail to send");exit(-1);}return;
}//单词查询函数
void do_query(int connectfd, msg_t *msg, sqlite3 *db)
{char sqlstr[TXT_NUM], *errmsg;int found = 0;char date[TXT_NUM], word[TXT_NUM];strcpy(word, msg->data); //备份一份,历史记录时要用//通过found保存查询结果,成功返回1,失败返回0found = do_searchword(connectfd, msg, db);//如果执行成功,还需要保存历史记录到数据库的record表中if (found == 1){//获取当前系统日期及时间get_date(date);//通过sqlite3_exec函数插入数据sprintf(sqlstr, "insert into record values('%s', '%s', '%s')", msg->name, date, word);if (sqlite3_exec(db, sqlstr, NULL, NULL, &errmsg) != SQLITE_OK){printf("error : %s\n", errmsg);}}if (-1 == send(connectfd, msg, sizeof(msg_t), 0)){perror("fail to send");exit(-1);}return;
}int do_searchword(int connectfd, msg_t *msg, sqlite3 *db)
{char sqlstr[STR_NUM] = {0};char *errmsg, **result;int nrow, ncolumn;//通过sqlite3_get_table函数查询记录是否存在sprintf(sqlstr, "select explanation from dict where word = '%s'", msg->data);if (sqlite3_get_table(db, sqlstr, &result, &nrow, &ncolumn, &errmsg) != SQLITE_OK){printf("error : %s\n", errmsg);}//通过nrow参数判断是否能够查询到疾记录,如果值为0,则查询不到,如果值为非0,则查询到if (nrow == 0){strcpy(msg->data, "not found!!!");return 0;}else{//此时msg->data里存的是单词的解释strcpy(msg->data, result[ncolumn]);}return 1;
}//获取当前系统日期和时间的函数
void get_date(char *data)
{time_t t;struct tm *tp;time(&t);tp = localtime(&t);sprintf(data, "%d-%d-%d %d:%d:%d", 1900 + tp->tm_year, 1 + tp->tm_mon, tp->tm_mday,tp->tm_hour, tp->tm_min, tp->tm_sec);
}//查询历史记录的函数
void do_history(int connectfd, msg_t *msg, sqlite3 *db)
{char sqlstr[TXT_NUM], *errmsg;//查询历史表,sqlite3_exec回调函数实现表的遍历sprintf(sqlstr, "select * from record where name = '%s'", msg->name);if (sqlite3_exec(db, sqlstr, history_callback, (void *)&connectfd, &errmsg) != SQLITE_OK){printf("error : %s\n", errmsg);sqlite3_free(errmsg);}//发送结束标志,客户端接收到这条说明单词的解释发送完毕strcpy(msg->data, "**OVER**");if (-1 == send(connectfd, msg, sizeof(msg_t), 0)){perror("fail to send");exit(-1);}return;
}//通过回调函数发送时间和单词
int history_callback(void *arg, int f_num, char **f_value, char **f_name)
{int connectfd;msg_t msg;connectfd = *(int *)arg;sprintf(msg.data, "%s : %s", f_value[1], f_value[2]);if (-1 == send(connectfd, &msg, sizeof(msg_t), 0)){perror("fail to send");exit(-1);}return 0;
}

4.2 客户端程序 client.c

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sqlite3.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>//防止魔鬼数字
#define NAME_NUM 16
#define DATA_NUM 256
#define TXT_NUM 128
#define STR_NUM 512//数据包类型宏定义
#define REGISTER 1 //注册
#define LOGIN 2    //登录
#define QUERY 3    //查询
#define HISTORY 4  //历史//数据包结构体
typedef struct _MSG
{int type;char name[NAME_NUM];char data[DATA_NUM];
} msg_t;void do_register(int socketfd, msg_t *msg);
int do_login(int socketfd, msg_t *msg);
void do_query(int socketfd, msg_t *msg);
void do_history(int socketfd, msg_t *msg);int main(int argc, char *argv[])
{int socketfd;msg_t msg;if (3 != argc){printf("Usage : %s <serv_ip> <serv_port>\n", argv[0]);exit(-1);}if (-1 == (socketfd = socket(PF_INET, SOCK_STREAM, 0))){perror("fail to socket");exit(-1);}struct sockaddr_in server_addr;bzero(&server_addr, sizeof(server_addr)); // bzero等价于memsetserver_addr.sin_family = PF_INET;server_addr.sin_addr.s_addr = inet_addr(argv[1]);server_addr.sin_port = htons(atoi(argv[2]));if (-1 == connect(socketfd, (struct sockaddr *)&server_addr, sizeof(server_addr))){perror("fail to connect");exit(-1);}int choose = 0;while (1){printf("************************************\n");printf("* 1: register   2: login   3: quit *\n");printf("************************************\n");printf("please choose --> ");if (scanf("%d", &choose) <= 0){perror("scanf error");exit(-1);}switch (choose){case 1:do_register(socketfd, &msg);break;case 2://执行登录函数,执行完毕后通过返回值决定是否要跳转到下一个菜单if (do_login(socketfd, &msg) == 1){goto next;}break;case 3:printf("Look forward to using it next time!!");close(socketfd);exit(0);}}
next:while (1){printf("************************************\n");printf("* 1: query   2: history   3: quit  *\n");printf("************************************\n");printf("please choose --> ");if (scanf("%d", &choose) <= 0){perror("scanf error");exit(-1);}switch (choose){case 1:do_query(socketfd, &msg);break;case 2:do_history(socketfd, &msg);break;case 3:printf("Look forward to using it next time!!");close(socketfd);exit(0);}}return 0;
}//用户注册函数
void do_register(int socketfd, msg_t *msg)
{//指定操作码msg->type = REGISTER;//输入用户名printf("input your name:");scanf("%s", msg->name);//输入密码printf("input your password:");scanf("%s", msg->data);//发送数据if (-1 == send(socketfd, msg, sizeof(msg_t), 0)){perror("fail to send");exit(-1);}//接收数据并输出if (-1 == recv(socketfd, msg, sizeof(msg_t), 0)){perror("fail to recv");exit(-1);}printf("register : %s\n", msg->data);return;
}//用户登录函数
int do_login(int socketfd, msg_t *msg)
{//设置操作码msg->type = LOGIN;//输入用户名printf("input your name:");scanf("%s", msg->name);//输入密码printf("input your password:");scanf("%s", msg->data);//发送数据给服务器if (-1 == send(socketfd, msg, sizeof(msg_t), 0)){perror("fail to send");exit(-1);}//接收服务器发送的数据if (-1 == recv(socketfd, msg, sizeof(msg_t), 0)){perror("fail to recv");exit(-1);}//判断是否登录成功//用21  可以防止“xx”后面有其他垃圾字符//登录成功返回1if (0 == strncmp(msg->data, "Login was successful", 21)){printf("Login was successful!!!\n");return 1;}//登录失败返回0printf("login : %s\n", msg->data);return 0;
}void do_query(int socketfd, msg_t *msg)
{msg->type = QUERY;puts("-----------------------------");while (1){printf("input word (if # is end): ");scanf("%s", msg->data);//如果输入的是#,返回上一级if (0 == strcmp(msg->data, "#")){break;}if (-1 == send(socketfd, msg, sizeof(msg_t), 0)){perror("fail to send");exit(-1);}if (-1 == recv(socketfd, msg, sizeof(msg_t), 0)){perror("fail to recv");exit(-1);}printf(">>> %s\n", msg->data);}return;
}void do_history(int socketfd, msg_t *msg)
{msg->type = HISTORY;if (-1 == send(socketfd, msg, sizeof(msg_t), 0)){perror("fail to send");exit(-1);}while (1){if (-1 == recv(socketfd, msg, sizeof(msg_t), 0)){perror("fail to recv");exit(-1);}if (0 == strcmp(msg->data, "**OVER**")){break;}printf("%s\n", msg->data);}return;
}

五、总结

这个项目我们花了差不多一天的时间,期间也出现了各种各样的问题,比如某个函数的返回值记混了、知识点忘了、不会用等等,究其原因无非就是用的少,练得少;而通过这个项目正好把这两个月的知识串了一遍,忘记的知识即时回头复习,总的来说还算顺利,时间较紧,代码可能还不太成熟,等有时间把代码优化一下,用更常用的io多路复用方法重新实现,欢迎各位讨论,共同进步!

基于数据库及TCP网络编程实现的电子词典相关推荐

  1. 基于Linux的socket网络编程项目——游侠手机商城

    基于Linux的socket网络编程项目--游侠手机商城 一.项目说明 二.项目使用的技术 三.客户端搭建 四.服务器端搭建 一.项目说明 本项目是一个仿真手机商城类系统,基本功能: 登录界面功能:用 ...

  2. 迈入JavaWeb第一步,Java网络编程基础,TCP网络编程URL网络编程等

    文章目录 网络编程概述 网络通信要素 要素一IP和端口号 要素二网络协议 TCP网络编程 UDP网络编程 URL网络编程 Java网络编程基础 网络编程概述 Java是Internet上的语言,它从语 ...

  3. TCP网络编程的基本流程

    TCP网络编程的基本流程 对于服务端,通常为以下流程: 调用socket函数创建socket 调用bind函数将socket绑定到某个IP和端口上 调用listen开始监听 当有客户端请求连接上来时, ...

  4. 【计算机网络】Linux环境中的TCP网络编程

    文章目录 前言 一.TCP Socket API 1. socket 2. bind 3. listen 4. accept 5. connect 二.封装TCPSocket 三.服务端的实现 1. ...

  5. muduo学习笔记:net部分之实现TCP网络编程库-Buffer

    文章目录 为什么采用non-blocking网络编程中应用层buffer是必需的? Buffer 设计 Buffer::readFd() 线程安全 Muduo Buffer 的数据结构 Muduo B ...

  6. 套接字编程-TCP网络编程

    文章目录 套接字地址结构 通用套接字地址数据结构 以太网协议的套接字地址数据结构 Netlink协议套接字地址结构 TCP网络编程 套接字初始化socket() domain type protoco ...

  7. tcp网络编程客户端和服务端及listen和tcp允许最大连接数

    tcp网络编程 tcp网络编程步骤: 由于tcp传输特点是可靠有连接,那么就有 1.客户端向服务端发送连接请求(SYN), 2.服务端接受请求并向客户端发送(SYN+ACK); 3.客户端向服务端回复 ...

  8. TCP网络编程 [Java]

    TCP网络编程 这里我们通过一个例子来了解什么是TCP网络编程: eg: 问题: -> 从客户端发送文件到服务端,服务端将接受到的客户端发来的文件存储到本地之后并返回一个发送成功给客户端,并关闭 ...

  9. 粤嵌GEC6818板子TCP网络编程发送命令控制音视频

    TCP网络编程 (1)gec6818网络编程前期准备工作 (1).开发板和Ubuntu系统都处于教室局域网内 1.开发板和电脑(Ubuntu)都需要连接上教室的网线 2.开发板设置ip地址 在开发板终 ...

最新文章

  1. kuayu react_react跨域解决方案
  2. POJ - 3613 Cow Relays(Floyd思想+矩阵快速幂+动态规划)
  3. 浅聊一下各类以太网媒体接口MII/RMII/SMII/GMII/RGMII/SGMII
  4. stylus之选择器(Selectors)
  5. [deviceone开发]-do_RichLabel的简单示例
  6. Idea插件——Translation 翻译插件安装与使用
  7. 数据隐私和数据分享_对数据隐私持开放态度
  8. Python 分析二手房源信息,揭晓土地交易现状
  9. Zabbix之Python发送邮件
  10. Chrome历史版本和Chrome webDriver历史版本【多测师】
  11. 视频教程-学透JavaScript-JavaScript
  12. CenterOs git安装
  13. python 拓扑排序_python拓扑排序算法实现
  14. 麒麟处理器排行天梯图2022 麒麟处理器各型号排行2022
  15. XCP协议和A2L文件–A2L(ASAP2)解析
  16. AWS解决方案架构师薪资平均159,033 美元
  17. maven surefire的executions配置
  18. 传统企业想要实现数字化转型,主要包含以下几大趋势?
  19. Win7系统文件上添加Sublime Text 3的打开方式
  20. python基础——类型转换

热门文章

  1. 毕业设计 医学数据分析 中医乳腺癌数据分析与可视化
  2. 利用函数公式进行贷款的计算
  3. java API1.6 中文帮助文档(chm格式)
  4. iOS开发知识点总结(三)
  5. 列表推导式_Python教程
  6. springBoot实现http代理ip
  7. a byte of python电子书_免费好书:A Byte of Python
  8. 164、关于POE交换机和8芯网线的秘密,本文来给你揭秘
  9. WordPress主题 阿里百秀XIU主题V6.0下载
  10. 【HDOJ 4970】 Killing Monsters