网络通讯之在线词典
一、设计目的
本项目可以在用户没有词库的情况下,让用户利用客户端和服务器端的数据传输实现在线获取单词的翻译。
二、使用技术及主要实现途径
该项目所应用技术为tcp/ip技术,搭建网络利用局域网在同一网段下进行信息通讯完成数据传递。项目可以调用数据库和系统文件用来对数据存储和读取。
三、设计思路和各项模块的功能
设计思路:
项目主要实现四个功能:1).注册;2).登录;3).查询单词;4).查询历史记录;
本次项目采用的是多进程的tcp并发机制。多线程,多路io复用都可以应用与该项目。
数据库建表提前建好的,建表语句如下:
CREATE TABLE usr (name text PRIMARY KEY, pass TEXT);
注:用户表中需要把用户名称设置为主键避免用户名重复导致数据读取混乱
CREATE TABLE record (name TEXT, date TEXT, word TEXT);
record表是用来存储搜索记录的。
注:单词和解释保存在文件中并且每个单词和它的解释必须在同一行。一行最多三百字节,每个单词和解释都只能有一个空格。
主要思路:
服务器端:
如流程图所示,服务器与客户端建立连接后获取到对应数据进行对应功能的选择。
客户端:
客户端则以封装用户输入的信息为主。将用户需求发送给服务器端,让服务器去处理信息,客户端则负责将处理信息反馈给客户。
实现模块及功能:
注册模块:
(服务器端)
void do_register(int connectfd, MSG *msg, sqlite3 *db)
{
char sqlstr[512] = {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
{
sprintf(msg->data,"OK");
}
if(-1 == send(connectfd, msg, sizeof(MSG), 0)){
ERRLOG("send error");
}
return;
}
服务器端的注册模块可以将客户端发来的数据进行接收入库。并发送注册成功后的函数给客户端。
(客户端)
void d_reg(int sockfd, MSG *msg)
{
//指定操作码
msg->type = R;
//输入用户名
printf("input your name:");
scanf("%s", msg->name);
//输入密码
printf("input your password:");
scanf("%s", msg->data);
//发送数据
if(-1==send(sockfd,msg, sizeof(MSG), 0)){
perror("send error");
exit(-1);
}
//接收数据并输出
if(-1==recv(sockfd, msg, sizeof(MSG), 0)){
perror("recv error");
exit(-1);
}
printf("register : %s\n", msg->data);
return;
}
客户端的任务就是将客户录入的注册信息封装号后传输到服务器端
等待数据传输完成后将信息并展现给用户
登录模块:
这个模块跟注册模块流程大致相同,服务器端负责将数据进行比对如果数据库中有对应的信息则反馈给服务器登录信息。客户端接收服务器反馈的登录信息,将信息呈现给用户并进入二级菜单(翻译模块)。
(服务器端)
//登录函数
void do_login(int connectfd, MSG *msg, sqlite3 *db)
{
char sqlstr[512] = {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 wrony!!!");
}
else
{
strncpy(msg->data, "OK", 256);
}
if(-1 == send(connectfd, msg, sizeof(MSG), 0)){
ERRLOG("send error");
}
return;
}
(客户端)
int d_log(int sockfd, MSG *msg)
{
//指定操作码
msg->type = L;
//输入用户名
printf("input your name:");
scanf("%s", msg->name);
//输入密码
printf("input your password:");
scanf("%s", msg->data);
//发送数据
printf("yfs send:[%s]\n", msg->data);
if(-1==send(sockfd, msg, sizeof(MSG), 0)){
perror("send error");
return -1;
}
//接收数据并输出
if(-1==recv(sockfd, msg, sizeof(MSG), 0)){
perror("recv error");
return -1;
}
printf("yfs recv:[%s]\n", msg->data);
if(!strcmp(msg->data,"OK")){
return 1;
}else{
return -1;
}
}
翻译模块:
服务器端负责解析客户端发来的数据包,将单词从句子中提炼出来。每次只扫描库中的一行信息,将单词比对出来,并发送后面的解释。
(服务器端)
void do_query(int connectfd, MSG *msg, sqlite3 *db)
{
char sqlstr[128], *errmsg;
int found = 0;
char date[128], word[128];
strcpy(word, msg->data);//将单词复制到word中方便操作
//通过found保存查询结果
found = do_searchword(connectfd, msg);
//如果执行成功,还需要保存历史记录
if(found == 1)
{
//获取时间
getdata(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), 0)){
ERRLOG("send error");
}
return;
}
//翻译函数中截取单词以及承装翻译的函数
int do_searchword(int connectfd, MSG *msg)
{
FILE *fp;
char temp[300];
char *p;
int len, result;
//保存单词的长度
len = strlen(msg->data);
//打开保存单词的文件
if((fp = fopen("dict.txt", "r")) == NULL)
{
strcpy(msg->data, "dict can not open");
if(-1 == send(connectfd, msg, sizeof(MSG), 0)){
ERRLOG("send error");
}
}
//每次读取一行内容
int flags = 0;
while(fgets(temp, 300, fp) != NULL)
{
//比较单词
result = strncmp(msg->data, temp, len);
if(result == 0 && temp[len] == ' ')
{
//p保存单词后面第一个空格的首地址
p = temp + len;
//移动p,让p保存解释的第一个字符的首地址
while(*p == ' ')
{
p++;
}
//将解释保存在data里面
strcpy(msg->data, p);
fclose(fp);
return 1;
}
}
strcpy(msg->data, "not found");
fclose(fp);
return 0;
}
void getdata(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 d_que(int sockfd, MSG *msg)
{
msg->type = Q;
puts("---------");
while(1)
{
printf("input word : ");
scanf("%s", msg->data);
//如果输入的是#,返回上一级
if(strcmp(msg->data, "#") == 0)
break;
//发送数据
if(-1==send(sockfd, msg, sizeof(MSG), 0)){
ERRLOG("send error");
}
//接收数据并输出
if(-1==recv(sockfd, msg, sizeof(MSG), 0)){
ERRLOG("recv error");
}
printf("%s\n", msg->data);
}
return;
}
查看历史记录:
客户端发送数据包到服务器端。服务器端接收后指令后,文件中调取对应用户的搜索记录,并将用户的搜索记录发送给客户端,客户端显示给用户。
(客户端)
//查看历史记录
void do_history(int connectfd, MSG *msg, sqlite3 *db)
{
char sqlstr[128], *errmsg;
//查询历史表
//注意:这里调用的只能是当前用户的查询记录,并不是将数据从数据库中全部读出。
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), 0)){
ERRLOG("send error");
}
return;
}
//通过回调函数发送时间和单词
int history_callback(void *arg, int f_num, char **f_value, char **f_name)
{
int connectfd;
MSG msg;
connectfd = *(int *)arg;
sprintf(msg.data, "%s : %s", f_value[1], f_value[2]);//将文件读出后写入临时结构体中发送出去
if(-1 == send(connectfd, &msg, sizeof(MSG), 0)){
ERRLOG("send error");
}
return 0;
}
(客户端)
void d_his(int sockfd, MSG *msg){
msg->type = H;
send(sockfd, msg, sizeof(MSG), 0);
while(1){
if(-1==recv(sockfd, msg, sizeof(MSG), 0)){
ERRLOG("recv error");
}
if(strcmp(msg->data, "**OVER**") == 0){
break;
}//用户发送**over**表示结束翻译功能
printf("%s\n", msg->data);
}
return;
}
四、总结
``
(客户端)
void d_his(int sockfd, MSG *msg){
msg->type = H;
send(sockfd, msg, sizeof(MSG), 0);
while(1){
if(-1==recv(sockfd, msg, sizeof(MSG), 0)){
ERRLOG("recv error");
}
if(strcmp(msg->data, "**OVER**") == 0){
break;
}//用户发送**over**表示结束翻译功能
printf("%s\n", msg->data);
}
return;
}
四、总结
本项程序主要实现了通过网络传输数据包实现网络通讯中数据的封装和传输。本程序通过在线字典翻译的形式体现出一种基本的人机交互模式,通过数据库语句实现对库文件的基本操作达到数据处理的效果。今后也可以多次运用多种体系相互配合的方式将程序做的更加人性化和智能化。