目录
一、前言
今天是培训的第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”后面有其他垃圾字符
//登录成功返回1
if (0 == strncmp(msg->data, "Login was successful", 21))
{
printf("Login was successful!!!\n");
return 1;
}
//登录失败返回0
printf("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,失败返回0
found = 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,失败返回0
found = 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等价于memset
server_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”后面有其他垃圾字符
//登录成功返回1
if (0 == strncmp(msg->data, "Login was successful", 21))
{
printf("Login was successful!!!\n");
return 1;
}
//登录失败返回0
printf("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多路复用方法重新实现,欢迎各位讨论,共同进步!