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

注:读者需要自己引入sqlite3.h文件

服务器端:

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

客户端:

#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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值