服务器 > 网络 > 网络协议

基于TCP/Ip协议的网络编程在线词典

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;
}

(0)
打赏 微信扫一扫 微信扫一扫

您想发表意见!!点此发布评论

推荐阅读

【计算机网络】网络基础--协议/网络协议/网络传输流程/地址管理

08-06

HTTPS单双向认证流程详解与联想

08-06

LoRaWAN网络协议Class A/Class B/Class C三种工作模式说明

08-06

网络协议——STP协议是什么?是如何实现的?

08-06

https认证过程(TLS认证过程)

08-06

如何(关闭)断开 Websocket 连接:简单易懂的实现指南

08-06

猜你喜欢

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论