基于tcp通信在线词典
1、功能说明
注册 用户名 (唯一编号)和密码
登录 用户名和密码 (比对数据库)
1、查询单词 (输入单词 查询)
2、查询历史记录(查看 用户查询单词情况)
2、流程图
(1)注册功能
在命令终端根据提示信息,输入用户名和密码,将所注册信息添加进数据库。根据程序提示信息,判断用户是否注册成功(添加数据库)。
代码实现思路:其实代码很简单,客户端根据提示信息输入用户名和密码发送给服务器,服务器执行写数据库操作,将插入数据库提示信息发挥给客户端,回显给用户。
服务器:
int regster(int sockfd, msg_t *msg, sqlite3 *my_db)
{
char sqlstr[512] = {0};
int ret = 0;
//注册执行写数据库操作
sprintf(sqlstr, "INSERT INTO usr VALUES('%s','%s')", msg->name, msg->txt);
printf("-----sqlstr:[%s]-----\n", sqlstr);
//执行插入操作
if (SQLITE_OK != (ret = sqlite3_exec(my_db, sqlstr, NULL, NULL, NULL)))
{
printf("errcode:[%d] errstr:[%s]\n", ret, sqlite3_errmsg(my_db));
msg->code = -1;
snprintf(msg->txt, sizeof(msg->txt), "errcode:[%d] errstr:[%s]\n", ret, sqlite3_errmsg(my_db));
send(sockfd, msg, sizeof(*msg), 0);
return -1;
}
snprintf(msg->txt, sizeof(msg->txt), "插入成功\n");
send(sockfd, msg, sizeof(msg), 0);
}
客户端:
int regster(int sockfd, msg_t *msg)
{
char name_buff[32] = {0};
char password[16] = {0};
msg->code = 1;
printf("请输入你的名字:");
fgets(name_buff, sizeof(name_buff), stdin);
name_buff[strlen(name_buff) - 1] = '\0';
printf("请输入你的密码:");
fgets(password, sizeof(password), stdin);
password[strlen(password) - 1] = '\0';
strcpy(msg->name, name_buff);
strcpy(msg->txt, password);
send(sockfd, msg, sizeof(*msg), 0);
recv(sockfd, msg, sizeof(*msg), 0);
if (msg->code == -1)
{
printf("用户名已经存在\n");
}
else
printf("注册成功\n");
system("read -p '请按任意键继续....' var");
system("clear");
}
(2)登录功能
在命令终端根据提示信息,输入用户名和密码,将所输信息与数据库信息比对。根据程序提示信息,判断用户是否登录成功(条件查找)。
代码实现思路:客户端根据提示信息输入用户名和密码发送给服务器,服务器执行数据库查询操作,并把查询结果反馈给客户端,客户端根据反馈结果进行相应操作。
服务器:
int login(int sockfd, msg_t *msg, sqlite3 *my_db)
{
char sqlstr[512] = {0};
//使用 sqlite3_get_table 查询结果
char **result = NULL;
int row = 0;
int column = 0;
int ret = 0;
sprintf(sqlstr, "SELECT * FROM usr WHERE name='%s' AND pass=%s ", msg->name, msg->txt);
printf("sqlstr=%s\n", sqlstr);
if (SQLITE_OK != (ret = sqlite3_get_table(my_db, sqlstr, &result, &row, &column, NULL)))
{
printf("errcode:[%d] errstr:[%s]\n", ret, sqlite3_errmsg(my_db));
// exit(-1);
}
if (row == 1)
{
strcpy(msg->txt, "登陆成功\n");
send(sockfd, msg, sizeof(*msg), 0);
}
else
{
msg->code = -1;
strcpy(msg->txt, "用户名或者密码有误\n");
send(sockfd, msg, sizeof(*msg), 0);
}
}
客户端:
int login(int sockfd, msg_t *msg)
{
char name_buff[32] = {0};
char password[16] = {0};
int sele = 0;
msg->code = 2;
printf("请输入你的名字:");
fgets(name_buff, sizeof(name_buff), stdin);
name_buff[strlen(name_buff) - 1] = '\0';
printf("请输入你的密码:");
fgets(password, sizeof(password), stdin);
password[strlen(password) - 1] = '\0';
strcpy(msg->name, name_buff);
strcpy(msg->txt, password);
send(sockfd, msg, sizeof(*msg), 0);
// puts("1");
recv(sockfd, msg, sizeof(*msg), 0);
if (msg->code == -1)
{
printf("%s\n", msg->txt);
}
else
{
printf("%s\n", msg->txt);
while (1)
{
showmenu2();
printf("请选择功能:");
scanf("%d", &sele);
getchar();
switch (sele)
{
case 1: //查询
quary(sockfd, msg);
break;
case 2: //查看历史记录
history(sockfd, msg);
break;
case 3:
printf("返回上一级目录\n");
goto next;
// close(fp);
break;
}
}
//调用另一个函数
}
next:
system("read -p '请按任意键继续....' var");
system("clear");
}
((1))单词查询
在命令端根据提示信息,输入要查询的单词,然后读取文件,查找与之对应的单词词条并显示提示信息或结果在终端命令行。与此同时,将查询时间 用户信息 要查询的单词,一同记录在数据库中方便后续功能调用。
代码实现思路:在客户端给服务器发出查询请求时,先将查询时间,用户名要查询单词,记录在数据库。因为单词和解释保存在文本文件中,单词和解释只占一行,一行最多300个字节,单词和解释之间至少有一个空格。所以我们用标准io中fgets()函数读取一行内容。依次比较,直到查询到单词发送信息给客户端,或者查询失败发送信息给客户端。客户端根据接受到的信息进行信息处理。
服务器:
int quary(int sockfd, msg_t *msg, sqlite3 *my_db, FILE *fp)
{
// printf("查询单词\n");
//查到了发过去 if()
int len = strlen(msg->txt);
char temp[300] = {0};
char *p = temp;
char sqlstr[512] = {0};
char timebuf[128]={0};
int ret = 0;
time_t *t;
time(t);
struct tm * tp= localtime(t);
sprintf(timebuf, "%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);
//先记录
sprintf(sqlstr, "INSERT INTO record VALUES('%s','%s','%s')",timebuf, msg->name, msg->txt);
//printf("-----sqlstr:[%s]-----\n", sqlstr);
//执行插入操作
if (SQLITE_OK != (ret = sqlite3_exec(my_db, sqlstr, NULL, NULL, NULL)))
{
printf("errcode:[%d] errstr:[%s]\n", ret, sqlite3_errmsg(my_db));
msg->code = -1;
snprintf(msg->txt, sizeof(msg->txt), "errcode:[%d] errstr:[%s]\n", ret, sqlite3_errmsg(my_db));
send(sockfd, msg, sizeof(*msg), 0);
return -1;
}
while (fgets(temp, 300, fp))
{
if (strncmp(temp, msg->txt, len) == 0 && temp[len] == ' ')
{
p = temp + len;
while (*p == ' ')
p++;
strcpy(msg->txt, p);
send(sockfd, msg, sizeof(*msg), 0);
return 0;
}
}
puts("1");
msg->code = -1;
send(sockfd, msg, sizeof(*msg), 0);
}
客户端:
int quary(int sockfd, msg_t *msg)
{
char buff[16] = {0};
printf("请输入你要查的单词:");
fgets(buff, sizeof(buff), stdin);
buff[strlen(buff) - 1] = '\0';
msg->code=4;
strcpy(msg->txt,buff);
send(sockfd, msg, sizeof(*msg), 0);
recv(sockfd,msg,sizeof(*msg),0);
if(msg->code==-1){
printf("查找失败!\n");
}else{
printf(" %s %s\n",buff,msg->txt);
}
}
((2))查询记录
在命令端选择相应功能,程序调取数据库数据,并显示数据库记录的历史用户查询记录
代码实现思路:客户端发送请求,服务器执行数据库查找操作,并将每一次的记录发送过去,客户端循环接收、打印。
服务器:
int callback(void *arg, int column, char** f_value, char** f_name){
int connectfd;
msg_t msg;
connectfd = *(int *)arg;
sprintf(msg.txt, "%s : %s : %s",f_value[0], f_value[1], f_value[2]);
send(connectfd, &msg, sizeof(msg), 0);
return 0;//该函数必须要有返回值 否则报错 query aborted
}
int history(int sockfd, msg_t *msg, sqlite3 *my_db)
{
char sqlstr[128], *errmsg;
//查询历史表
sprintf(sqlstr, "select * from record ");
if (sqlite3_exec(my_db, sqlstr, callback, (void *)&sockfd, &errmsg) != SQLITE_OK)
{
printf("error : %s\n", errmsg);
sqlite3_free(errmsg);
}
//发送结束标志
strcpy(msg->txt, "quit");
send(sockfd, msg, sizeof(*msg), 0);
return 0;
}
客户端:
int history(int sockfd, msg_t *msg)
{
memset(msg,sizeof(*msg),0);
msg->code=5;
send(sockfd, msg, sizeof(*msg), 0);
while(1){
if(recv(sockfd,msg,sizeof(*msg),0)==-1)
PRIN_ERROR("recv _history error");
if(strncmp(msg->txt,"quit",5)==0)break;
printf("%s\n",msg->txt);
}
return 0;
}
((3))返回上一级目录
goto 跳转
(3)退出
退出当前进程。exit(0)
总结
此次小试牛刀,颇有挑战,也有许多瑕疵。供各位看官参考指正。基于tcp的在线词典后续我会出个io多路复用的TCP并发服务器在线词典,比较符合实际需求。实现TCP并发 --多进程 多线程 io多路复用 均可,有兴趣的小白可以试试。
需要源码可以联系我。