欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

网络编程实战之在线电子词典

程序员文章站 2022-05-04 14:07:34
...

   1.头文件

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <unistd.h>
#include <sqlite3.h>
#include <signal.h>
#include <time.h>

2.宏定义

#define  R  1   // user - register
#define  L  2   // user - login
#define  Q  3   // user - query
#define  H  4   // user - history
#define N 128
//char *errmsg=NULL;
typedef struct{
	char type;
	char name[N];
	char data[512];
}MSG;//规定传输的格式

3.服务器主程序

服务器创建一个套接字即所谓的sockfd用于监听,绑定,以及创建用于通信的套接字——调用accept函数,当一个进程连接上就会返回一个通信套接字,另一个进程连上会返回一个看似一样但是不一样的通讯套接字,就像是sql语言中NULL永远会不同于其他字符,哪怕是另一个NULL依然有效。

我们将父进程的通用套接字关闭,只用于监听是否有客户端连接,子进程只用于接收,避免了同一时间只能连接一个用户的囧境。

int main(int argc, const char *argv[])
{
	MSG msg;
	sqlite3 *db;
	int sockfd;
	char *errmsg=NULL;
	if(0!=sqlite3_open("test.db",&db))
	{
		fprintf(stderr,"sqlite_open failed:%s",sqlite3_errmsg(db));
		return -1;
	}
	printf("sqlite_open ok;\n");

sqlite3_exec(db,"create table if not exits usr (name char,password char);",NULL,NULL,&errmsg);
sqlite3_exec(db,"create table if not exits record_test(name char,data char,word char);",NULL,NULL,&errmsg)!=0);
	printf("the table create ok\n");
	sockfd=socket(AF_INET,SOCK_STREAM,0);
	struct sockaddr_in serveraddr;
	struct sockaddr_in clientaddr;
//填充结构体
	serveraddr.sin_family=AF_INET;
	serveraddr.sin_port=htons(8898);
	serveraddr.sin_addr.s_addr=inet_addr("127.0.0.1");

	socklen_t addrlen=sizeof(serveraddr);
	socklen_t clientlen=sizeof(clientaddr);
//绑定
	
	if(bind(sockfd,(struct sockaddr *)&serveraddr,addrlen)<0)
	{
		perror("bind");
		exit(1);
	}
	printf("bind\n");
//监听
	if(0> listen(sockfd,5))
	{
		perror("listen");
		exit(1);
	}
	printf("listen\n");

	pid_t pid;
	char buf[N]={0};
        int acceptfd;
// 处理僵尸进程
   	signal(SIGCHLD,handler);
	while(1)
	{
		acceptfd=accept(sockfd,(struct sockaddr *)&clientaddr,&clientlen);
		pid=fork();
//fork函数创建进程
//1.创建失败 2.子进程 3.父进程:父进程用于监听和连接,子进程用于通信
		if(pid<0)
		{
			perror("failed to fork");
			exit(1);
		}
		else if(pid==0)
		{ 
			close(sockfd);
			do_client(acceptfd,db);
		}
		else
			close(acceptfd);
	} 
	sqlite3_close(db);

	return 0;
}

4.客户端信号的处理

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': 
			process_register(acceptfd,&msg,db);
			break;
		case 'L': 
			process_login(acceptfd,&msg,db);
			break;
		case 'Q': 
			process_query(acceptfd,&msg,db);
			break;
		case 'H': 
			process_history(acceptfd,&msg,db);
			break;
		default:
			printf("invalid data msg.\n");
		}
	}
	printf("client exit\n");
	close(acceptfd);
	exit(0);

}

5.客户端的主函数(字符界面)

int main(int argc, const char *argv[])
{
	int sockfd;
	MSG msg;
	MSG *msgp = NULL;
	sockfd=socket(AF_INET,SOCK_STREAM,0);
	if(sockfd<0)
	{
		perror("sockfd.");
		exit(1);
	}
	struct sockaddr_in serveraddr;
	bzero(&serveraddr,sizeof(serveraddr));//清零 
	serveraddr.sin_family=AF_INET;
	serveraddr.sin_port=htons(8898);
	serveraddr.sin_addr.s_addr=inet_addr("127.0.0.1");
	socklen_t addrlen=sizeof(serveraddr);
	connect(sockfd,(struct sockaddr *)&serveraddr,addrlen);
	char buf[N]={0};
	int n;
	while(1)
	{
		printf("***********************\n");
		printf("******1:register****\n");
		printf("******2:login*******\n");
		printf("******3.quit********\n");
		printf("***********************\n");
		printf("please choose\n");
		fgets(buf,sizeof(buf),stdin);
		printf("%s\n",buf);
		switch(buf[0])
		{
		case '1':
			do_register(sockfd,&msg);
			break;
		case '2':
			if(do_login(sockfd,&msg)==1)
			{
				goto next;
			}
			break;
		case '3':
			close(sockfd);
			exit(0);
			break;

		default:
			printf("input again\n");
			continue;
		}
	}
	while(1)
	{
next:
		printf("************************************\n");
		printf("**1:querry_word**2:history**3:quit**\n");
		printf("************************************\n");
		printf("please choose:\n");
		fgets(buf,sizeof(buf),stdin);
		switch(buf[0])
		{
		case '1':
			do_query(sockfd,&msg);
			break;
		case '2':
			do_history(sockfd,&msg);
			break;
		case '3':
			close(sockfd);
			exit(0);
		default:printf("invalid data\n");
		}


	}
	return 0;
}

6.注册功能实现

(1)客户端

int do_register(int sockfd,MSG *msg)
{ 
	msg->type='R';
	printf("your usrname:\n");
	scanf("%s",msg->name);
	printf("your password:\n");
	scanf("%s",msg->data);
	
	printf("%c,%s,%s\n",msg->type,msg->name,msg->data);
	pid_t pid;


	if(send(sockfd,msg,512,0)<0)
	{
		printf("failed to send\n");
		return 1;
	}

	if(recv(sockfd,msg,sizeof(MSG),0)<0)
	{
		printf("failed to recv\n");
		return 1;
	}
	printf("%s\n",msg->data);
	return 0;
}

(2)服务器端

void process_register(int acceptfd,MSG *msg, sqlite3 * db)
{
	char sql[64]={0};
	char *errmsg=NULL;
	sprintf(sql,"insert into usr values('%s','%s');",msg->name,msg->data);
	printf("%s\n",sql);
	if(0!= sqlite3_exec(db,sql,NULL,NULL,&errmsg))
	{
		printf("%s\n",errmsg);
		strcpy(msg->data,"usrname already exit.");
	}
	else
	{
		printf("client register ok.\n");
		strcpy(msg->data,"OK!");
	}
	if(send(acceptfd,msg,sizeof(MSG),0)<0)
	{
		perror("failed to send");
		return;
	}
	return;

}

7.登录功能的实现

(1)客户端

int do_login(int sockfd,MSG *msg)
{
	pid_t pid;
	msg->type='L';
	printf("input your name\n");
	scanf("%s",msg->name);
	getchar();
	printf("input your password\n");
	scanf("%s",msg->data);
	getchar();
		send(sockfd,msg,sizeof(MSG),0);

		printf("%s\n",msg->data);
		recv(sockfd,msg,sizeof(MSG),0);

		printf("%s\n",msg->data);
	if(strncmp(msg->data,"OK",3)==0)
	{

		printf("Login ok\n");
		return 1;
	}
	else 
	{
		printf("%s",msg->data);
		return 0;
	}
	

}

(2)服务器端

int  process_login(int acceptfd,MSG *msg,sqlite3 *db)
{	char **resultp=NULL;
	int nrow,ncolumn;
	char *errmsg=NULL;
	char sql[512]={0};
	printf("a2\n");
	sprintf(sql,"select * from usr where name = '%s' and password = '%s';",msg->name,msg->data);
	if(0!= sqlite3_get_table(db,sql,&resultp,&nrow,&ncolumn,&errmsg))
	{

		printf("%s\n",errmsg);
		return -1;
	}else
	{
		printf("get_table ok!\n");
	}
	if(nrow==0)
	{
		strcpy(msg->data,"usr/pwd wrony");
		send(acceptfd,msg,sizeof(MSG),0);	
	}else if(nrow==1)
	{
		strcpy(msg->data,"OK");
		send(acceptfd,msg,sizeof(MSG),0);
		return 1;
	}
	return 0;
}

8.查询功能

(1)客户端

void do_query(int sockfd,MSG *msg)
{
	
	msg->type='Q';
	puts("-----------------------------");
	while(1)
	{
		printf("input word:");
		fgets(msg->data,sizeof(msg->data),stdin);
		msg->data[strlen(msg->data)-1]='\0';
		if(strncmp(msg->data,"#",1)==0)
			break;
		if(send(sockfd,msg,sizeof(MSG),0)<0)
		{
			printf("failed to send\n");
			return ;
		}
		printf("%c,%s",msg->type,msg->data);
		printf("send ok.\n");
		if(recv(sockfd,msg,1024,0)<0)
		{
			printf("failed to recv\n");
			return ;
		}
		printf("%s\n",msg->data);
	}
	return ;
}

(2)服务器端

int do_searchword(int acceptfd,MSG *msg,char word[])
{

	FILE *fp=NULL;
	char buffer[1024]={0};
	int len;

	if((fp=fopen("./dict.txt","r"))==NULL)
	{
		perror("fail to fopen.\n");
		strcpy(msg->data, "Failed to open dict.txt");
		send(acceptfd,(MSG*)&msg, sizeof(MSG), 0);
		return 1;
	}
	len=strlen(word);
	printf("%s,len=%d\n",word,len);
		int result;
		char *p=NULL;
	while(fgets(buffer,1024,fp)!=NULL)
	{	
		result=strncmp(buffer,word,len);
		if(result<0)
		{
			continue;
		}
		if(result>0||((result == 0)&&(buffer[len]!=' ')))
			break;
		p=buffer + len;
		while(*p== ' ')
		{
			p++;
		}
		strcpy(msg->data,p);
	printf("%s\n",msg->data);
		fclose(fp);
		return 1;
	}
	fclose(fp);
	return 0;
}
int get_time(char *data)
{

	struct tm *tm_p=NULL;
	time_t dtime;

	time(&dtime);
	tm_p=localtime(&dtime);
	sprintf(data,"%d-%d-%d %d:%d:%d\n",tm_p->tm_year + 1900,tm_p->tm_mon + 1,\
			tm_p->tm_mday,tm_p->tm_hour,tm_p->tm_min,tm_p->tm_sec);
	printf("get data:%s\n",data);
	return 0;
}
void process_query(int acceptfd,MSG *msg,sqlite3 *db)
{
	int found;
	char word[64]={0};
	char data[128]={0};
	char sql[128]={0};
	char *errmsg =NULL;
	strcpy(word,msg->data);
	found=do_searchword(acceptfd,msg,word);
	if(found==1)
	{
		get_time(data);

		sprintf(sql,"insert into record_test values('%s','%s','%s')",msg->name,data,word);
		if(0!=sqlite3_exec(db,sql,NULL,NULL,&errmsg))
		{
			printf("%s\n",errmsg);
			return ;
		}
		else
		{
			printf("insert record done.\n");
		}
	}
	else
	{
		strcpy(msg->data,"NO found");
		printf("aaaa\n");
	}
	if(send(acceptfd,msg,sizeof(MSG),0)<0)
	{
	perror("failed to send");
	}
}

9 历史查询

(1)客户端

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

}

(2)服务器端

int history_callback(void* arg,int f_num,char** f_value,char** f_name)
{
	int accptfd;
	MSG msg;

	accptfd=*((int *)arg);
	sprintf(msg.data,"%s ,%s",f_value[1],f_value[2]);
	send(accptfd,&msg,sizeof(msg),0);
	return 0;
}

void process_history(int acceptfd,MSG *msg,sqlite3 * db)
{
	char sql[128]={0};
	char *errmsg=NULL;
	sprintf(sql,"select * from record where name ='%s';",msg->name);
	if(0!=sqlite3_exec(db,sql,history_callback,(void *)&acceptfd,&errmsg))
	{
		printf("%s\n",errmsg);
	}else
	{
		printf("record done.\n");
	}
	msg->data[0]='\0';
	send(acceptfd,msg,sizeof(MSG),0);
}

10 僵尸进程处理函数

void handler(int signum)
{
	if(SIGCHLD==signum)
	waitpid(-1,NULL,WNOHANG);
}

本文纯属个人笔记,如有错误,还望不吝指点,如有雷同,你来打我呀