63人参与 • 2024-08-06 • 网络协议
超详细带注释利用tcp协议构建在线词典
注:词典需要读者自己做一个.txt文本文件,以每行单词的格式存储,从未在服务器才能被调用
同时需要引入数据库,需要准备sqlite3.c相关文件
搭建客户端client:
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#define n 32
#define r 1
#define l 2
#define q 3
#define h 4
typedef struct
{
int type;
char name[n];
char data[256];
}msg;
int do_query(int sockfd, msg *msg);
int do_history(int sockfd,msg *msg);
//注册字函数
int do_register(int sockfd, msg *msg)
{
msg->type = r;
puts("请输入你的名字");
scanf("%s",msg->name);
puts("请输入你的账号");
scanf("%s",msg->data);
if(send((sockfd), msg, sizeof(msg), 0) < 0)
{
puts("发送失败");
return -1;
}
if(recv(sockfd, msg, sizeof(msg), 0) < 0)
{
puts("接收服务器端信息失败");
return -1;
}
printf("%s\n",msg->data);
return -1;
}
//登陆函数
int do_login(int sockfd, msg *msg)
{
memset(msg,0,sizeof(msg));
msg->type = l;
puts("请输入你的账号");
scanf("%s",msg->data);
getchar();
puts("请输入你的名字");
scanf("%s",msg->name);
getchar();
if(send(sockfd,msg,sizeof(msg),0) < 0)
{
puts("发送失败");
return -1;
}
if(recv(sockfd,msg,sizeof(msg),0) < 0)
{
perror("recv error");
}
if(strncmp(msg->data,"ok",3) == 0)
{
puts("登陆成功");
return 1;
}
else
{
printf("%s\n",msg->data);
return -1;
}
}
int main(int argc, const char *argv[])
{
int ret = 0;
int sockfd = socket(af_inet, sock_stream, 0);
struct sockaddr_in serveraddr;
int n;
msg msg;
//判断传参数
//参数为ip地址,端口号
if(argc != 3)
{
printf("传参错误");
return -1;
}
memset(&serveraddr, 0, sizeof(serveraddr));
serveraddr.sin_family = af_inet;
//将传递进来的点分十进制的ip地址转换为32位二进制ip地址
serveraddr.sin_addr.s_addr = inet_addr(argv[1]);
//将传进来的第二个参数作为端口
serveraddr.sin_port = htons(atoi(argv[2]));
//向服务器发送连接请求
if(connect(sockfd,(struct sockaddr *)&serveraddr, sizeof(serveraddr)) != 0)
{
perror("connect error");
return -1;
}
while(1)
{
puts("************************************************");
puts("输入1进行注册");
puts("输入2进行登陆");
puts("输入3进行退出");
scanf("%d",&n);
//清空缓冲区垃圾字符
getchar();
switch(n)
{
case 1:
do_register(sockfd,&msg);
break;
case 2:
ret = do_login(sockfd,&msg);
if(ret > 0)
{
goto next;
}
continue;
case 3:
close(sockfd);
exit(0);
break;
default:
puts("输入选项有误");
}
}
next:
while(1)
{
puts("*************************************");
puts("输入1进行查询单词");
puts("输入2进行历史记录的查询");
puts("输入3进行他退出");
puts("*************************************");
puts("请输入选项");
scanf("%d",&n);
getchar();
switch(n)
{
case 1:
do_query(sockfd, &msg);
break;
case 2:
do_history(sockfd,&msg);
break;
case 3:
close(sockfd);
exit(0);
break;
default:
puts("输入的选项有误");
}
}
return 0;
}
int do_query(int sockfd, msg *msg)
{
msg-> type = q;
puts("--------------------");
while(1)
{
printf("input word:");
scanf("%s",msg->data);
getchar();
//客户端,输入#号,返回到上一级菜单
if(strncmp(msg->data,"#",1) == 0)
{
break;
}
//将要查询的单词发送给服务器,传递回来的单词的注释信息
if(send(sockfd, msg, sizeof(msg), 0) < 0)
{
printf("fail to recv.\n");
return -1;
}
if(recv(sockfd, msg, sizeof(msg),0) < 0)
{
printf("recv error.\n");
return -1;
}
printf("%s\n",msg->data);
}
return 0;
}
int do_history(int sockfd,msg *msg)
{
msg->type = h;
send(sockfd, msg, sizeof(msg),0);
//接受服务器,传递回来的历史记录信息
while(1)
{
recv(sockfd, msg, sizeof(msg), 0);
if(msg->data[0] == '\0')
{
break;
}
//输入历史记录信息
printf("%s\n",msg->data);
}
return -1;
}
搭建服务器:
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>
#include "sqlite3.h"
#include <time.h>
#include <sys/socket.h>
#include <fcntl.h>
#define n 32
#define r 1
#define l 2
#define q 3
#define h 4
typedef struct {
int type;
char name[n];
char data[256];
}msg;
int do_client(int acceptfd, sqlite3 *db);
int do_register(int acceptfd, msg*msg, sqlite3 *db);
int do_login(int acceptfd,msg *msg, sqlite3 *db);
int do_query(int acceptfd, msg *msg, sqlite3* db);
int do_searchword(int acceptfd, msg *msg, char word[]);
int get_date(char *date);
int do_history(int acceptfd, msg *msg, sqlite3 *db);
int history_callback(void *arg, int f_num, char **f_value, char **f_name);
int main(int argc, const char *argv[])
{
int sockfd;
struct sockaddr_in serveraddr;
int n;
msg msg;
sqlite3 *db;
int acceptfd = -1;
pid_t pid;
if(argc != 3)
{
puts("传参错误");
return -1;
}
//打开数据库
if(sqlite3_open("my.db", &db) != sqlite_ok )
{
printf("%s\n",sqlite3_errmsg(db));
return -1;
}
else
{
puts("打开数据库成功");
}
sockfd = socket(af_inet,sock_stream,0);
if(sockfd <= 2)
{
perror("socket error");
return -1;
}
memset(&serveraddr,0,sizeof(serveraddr));
serveraddr.sin_family = af_inet;
serveraddr.sin_port = htons(atoi(argv[2]));
serveraddr.sin_addr.s_addr = htonl(inaddr_any);
int ret = bind(sockfd, (struct sockaddr *)&serveraddr,sizeof(serveraddr));
if(ret != 0)
{
printf("sockfd:%d\n",sockfd);
perror("绑定失败");
close(sockfd);
return -1;
}
puts("bind success.");
//将套接字设置为监听套接字
ret = listen(sockfd,5);
if(ret != 0)
{
perror("设置监听套接字失败");
close(sockfd);
return -1;
}
puts("listen success.");
while(1)
{
puts("============");
acceptfd = accept(sockfd,null,null);
printf("connfd : %d\n", acceptfd);
if(acceptfd < 0)
{
perror("accept error");
return -1;
}
puts("accept success.");
//创建子进程
if((pid = fork()) < 0)
{
perror("fork error");
return -1;
}
//子进程
else if(0 == pid)
{
//处理客户端消息
close(sockfd);
//将与客户机创建的通信套接子和数据库指针传递给子函数
do_client(acceptfd,db);
}
//父进程
else
{
//因为在子进程中已经创建了与客户机通信的套接字,所以父进程可以关闭与客户机之间的通信套接字
close(acceptfd);
}
}
return 0;
}
int do_client(int acceptfd, sqlite3 *db)
{
msg msg;
while(recv(acceptfd, &msg, sizeof(msg),0) > 0)
{
printf("type:%d\n",msg.type);
switch(msg.type)
{
case r:
do_register(acceptfd, &msg, db);
break;
case l:
do_login(acceptfd, &msg, db);
break;
case q:
do_query(acceptfd, &msg, db);
break;
case h:
do_history(acceptfd, &msg, db);
break;
default:
puts("type 的值有误");
}
}
puts("client exit");
close(acceptfd);
exit(0);
return 0;
}
//当用户提交注册申请时,用该函数处理
int do_register(int acceptfd, msg*msg, sqlite3 *db)
{
char *errmsg;
char sql[128];
sprintf(sql,"insert into usr values('%s','%s');",msg->data,msg->name);
printf("%s\n",sql);
if(sqlite3_exec(db,sql,null,null,&errmsg) != sqlite_ok)
{
printf("%s\n",errmsg);
strcpy(msg->data,"usr name error");
}
else
{
printf("数据导入成功\n");
strcpy(msg->data,"ok!");
}
if(send(acceptfd, msg, sizeof(msg), 0) < 0)
{
perror("send error");
return -1;
}
return -1;
}
//当用户端提交登陆申请时调用该函数
int do_login(int acceptfd,msg *msg, sqlite3 *db)
{
int i=0;
char sql[128]={};
char *errmsg;
int nrow;
int ncloumn;
char **resultp;
printf("%s\n",msg->data);
sprintf(sql,"select * from usr where usr_data = '%s';",msg->data);
printf("%s\n",sql);
//在数据库中进行查找
i =sqlite3_get_table(db,sql,&resultp,&nrow,&ncloumn,&errmsg);
if( i != sqlite_ok)
{
puts(errmsg);
puts("查找失败");
return -1;
}
else
{
puts("查找成功");
}
if(1 == nrow)
{
strcpy(msg->data,"ok");
send(acceptfd,msg,sizeof(msg),0);
return -1;
}
//当用户名或者账号错误时,无法进行登陆
if(0 == nrow)
{
strcpy(msg->data,"用户名或密码错误");
send(acceptfd,msg,sizeof(msg),0);
}
}
int do_query(int acceptfd, msg *msg, sqlite3* db)
{
char word[64];
int found = 0;
char date[128] = {};
char sql[128] = {};
char *errmsg;
strcpy(word,msg->data);
//拿出msg结构体,查询单词
found = do_searchword(acceptfd, msg, word);
printf("查询一个单词完毕.\n");
//如果找到了这个单词,那么将 用户名,时间,单词,插入到历史记录表中
if(1 == found)
{
//需要获取系统时间
get_date(date);
sprintf(sql,"insert into record values('%s','%s','%s')",msg->name,date,word);
if(sqlite3_exec(db,sql,null, null, &errmsg) != sqlite_ok)
{
printf("%s\n",errmsg);
return -1;
}
else
{
printf("写入数据库成功\n");
}
}
else //表示没有找到
{
strcpy(msg->data,"没找到");
}
//将查询的结果发送给客户端
printf("%s\n",msg->data);
send(acceptfd, msg, sizeof(msg), 0);
return 0;
}
//查询单词函数
int do_searchword(int acceptfd, msg *msg, char word[])
{
file *fp;
int len = 0;
char temp[512]={};
int result;
char *p;
//打开文件,读取文件,进行对比
fp =fopen("./dict.txt","r");
if(fp == null)
{
printf("%p",fp);
perror("fail to fopen\n");
strcpy(msg->data, "词典打开失败");
send(acceptfd, msg, sizeof(msg), 0);
return -1;
}
//打印出,客户端要查询的单词
len = strlen(word);
printf("%s, len = %d\n",word,len);
//读取文件,查询单词
while(fgets(temp,512,fp) != null)
{
result = strncmp(temp, word,len);
if(result < 0)
{
//说明该行不是要找的单词,继续向下遍历
continue;
}
if(result > 0 || (result == 0) && (temp[len] != ' '))
{
break;
}
//表示找到了,查询的单词
p = temp +len;
while(*p == ' ')
{
p++;
}
//找到了注释,跳过所有空格
strcpy(msg->data,p);
printf("found word:%s\n",msg->data);
//注释拷贝完毕,应该关闭文件
fclose(fp);
return 1;
}
fclose(fp);
return 0;
}
int get_date(char *date)
{
time_t t;
struct tm *tp;
time(&t);
//进行时间格式转换
tp = localtime(&t);
sprintf(date,"%d-%d-%d %d:%d:%d",tp->tm_year+1900, tp->tm_mon+1,tp->tm_mday,tp->tm_hour,tp->tm_min,tp->tm_sec);
printf("get date:%s\n",date);
return 0;
}
//调用查看历史记录函数
int do_history(int acceptfd, msg *msg, sqlite3 *db)
{
char sql[128] = {};
char * errmsg;
sprintf(sql, "select * from record where name = '%s'", msg ->name);
//查询数据库
if(sqlite3_exec(db, sql, history_callback, (void *)&acceptfd, &errmsg) != sqlite_ok)
{
printf("%s\n",errmsg);
}
else
{
puts("查询成功");
}
//所有的信息查询完毕后给客户端发出一个结束的信息
msg->data[0] = '\0';
send(acceptfd, msg, sizeof(msg), 0);
return 0;
}
//得到查询结果,并且需要将历史记录发送给客户端
int history_callback(void *arg, int f_num, char **f_value, char **f_name)
{
int acceptfd;
msg msg;
acceptfd = *((int *)arg);
sprintf(msg.data,"%s, %s", f_value[1], f_value[2]);
send(acceptfd, &msg, sizeof(msg), 0);
return 0;
}
您想发表意见!!点此发布评论
版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。
发表评论