Linux多进程编程之在线词典

       在线词典是基于Linux 多进程并发服务器编程,由服务器端和客户端构成,客户端可以运行在多个不同的主机上连接服务器,服务器对员工信息的操作结果通过数据库sqlite来保存。当用户登录后,根据用户名判断用户是否已注册。如果未注册,则进行注册用户名和密码,如果已经存在该用户名,输入正确密码,则进行查询单词和查询历史记录操作。

服务器端

服务器端功能描述:


服务器端操作流程:


客户端

客户端功能描述:


客户端操作流程:


源代码

服务器端

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

#define  N  16
#define  R  1   //  user register
#define  L  2   //  user login
#define  Q  3   //  query word
#define  H  4   //  history record

//定义数据库
#define DATABASE "my.db"

typedef struct 
{
	int type;
	char name[N];
	char data[256];   // password or word
} MSG;

void do_client(int acceptfd, sqlite3 *db);
void do_register(int acceptfd, MSG *msg, sqlite3 *db);
void do_login(int acceptfd, MSG *msg, sqlite3 *db);
void do_query(int acceptfd, MSG *msg, sqlite3 *db);
int  do_searchword(int acceptfd, MSG *msg);
void getdata(char data[]);
void 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, char *argv[])
{
	int sockfd, acceptfd;
	struct sockaddr_in server_addr;
	pid_t pid;
	sqlite3 *db;

	if (argc < 3)
	{
		printf("Usage : %s <ip> <port>\n", argv[0]);
		exit(-1);
	}
	
	//打开数据库(如果数据库已经创建好了,调用函数后,之后利用指针操作数据库)
	//数据库里面有两个表,一个负责存放用户名和密码,用户名唯一,另一个负责存放历史记录
	if (sqlite3_open(DATABASE, &db) != SQLITE_OK)
	{
		printf("error : %s\n", sqlite3_errmsg(db));
		exit(-1);
	}
	
	if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
	{
		perror("fail to socket");
		exit(-1);
	}

	bzero(&server_addr, sizeof(server_addr));	
	server_addr.sin_family = AF_INET;
	server_addr.sin_addr.s_addr = inet_addr(argv[1]);
	server_addr.sin_port = htons(atoi(argv[2]));
	
	if (bind(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0){
		perror("fail to bind");
		exit(-1);
	}
	
	if (listen(sockfd, 5) < 0){
		perror("fail to listen");
		exit(-1);
	}
	
	signal(SIGCHLD, SIG_IGN);// 避免僵尸进程
	
	while(1)
	{
		//不关心客户端的网络信息结构体
		if((acceptfd = accept(sockfd, NULL, NULL)) < 0){
			perror("fail to accept");
			exit(-1);
		}
	
		//父子进程实现并发
		if((pid = fork()) < 0){
			perror("fail to fork");
			exit(-1);
		}
		if(pid == 0)  //子进程负责接收数据并处理
		{
			close(sockfd);
			do_client(acceptfd, db);
		}
		else  //父进程负责连接
		{
			close(acceptfd);
		}
	}
	return 0;
}

void do_client(int acceptfd, sqlite3 *db)
{
	MSG msg;

	//根据接收到的type判断对应执行的代码
	while (recv(acceptfd, &msg, sizeof(MSG), 0) > 0)  // receive request
	{
		printf("type : %d, name : %s, data : %s\n", msg.type, msg.name, msg.data);

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

	//对方退出或者异常关闭
	printf("client quit\n");
	exit(0);

	return;
}

//注册
void do_register(int acceptfd, MSG *msg, sqlite3 *db)
{
	char sqlstr[128] = {0};
	char *errmsg;

	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 {
		//插入成功
		strcpy(msg->data, "OK");
	}
	//将信息发送给客户端
	send(acceptfd, msg, sizeof(MSG), 0);
	return;
}

//登录
void do_login(int acceptfd, MSG *msg, sqlite3 *db)
{
	char sqlstr[128] = {0};
	char *errmsg, **result;
	int nrow, ncolumn;
	
	sprintf(sqlstr, "select * from usr where name = '%s' and pass = '%s'", msg->name, msg->data);
	
	//调用sqlite3_get_table查询数据是否在数据库的表里面
	if(sqlite3_get_table(db, sqlstr, &result, &nrow, &ncolumn, &errmsg) != SQLITE_OK){
		printf("error : %s\n", errmsg);
	}
	
	//nrow为0表示数据不存在或者不匹配
	if(nrow == 0){
		strcpy(msg->data, "name or password is wrony!!!");
	}
	else  //数据匹配
	{
		strncpy(msg->data, "OK", 256);
	}

	//发送指令
	send(acceptfd, msg, sizeof(MSG), 0);
	
	return;
}

//查询
void do_query(int acceptfd, MSG *msg, sqlite3 *db)
{
	char sqlstr[128], *errmsg;
	int found = 0;
	char date[128], word[128];
	
	strcpy(word, msg->data);
	
	//查询成功返回1
	found = do_searchword(acceptfd, msg);

	//如果找到单词的解释,需要将单词以及时间保存在历史记录的数据库表里
	if(found == 1)
	{
		//时间
		getdata(date);		
		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);
		}
	} else {
		strcpy(msg->data, "not found");
	}
	send(acceptfd, msg, sizeof(MSG), 0);	
	return;
}

//查找单词
int  do_searchword(int acceptfd, 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");
		send(acceptfd, msg, sizeof(MSG), 0);
	}

	//printf("query word is %s len = %d\n", msg->data, len);
	
	//fgets每次只可以读一行,规定文档里面最后一行有300个字节
	while(fgets(temp, 300, fp) != NULL)
	{
		result = strncmp(msg->data, temp, len);

		//输入的单词必须保证一样,并且temp的下一个字符是空格
		if(result == 0 && temp[len] == ' ')
		{
			//指针指向单词后面的位置
			p = temp + len;

			//从第一个有数据的位置开始读,排除单词与解释之间的空格
			while(*p == ' ')
			{
				p++;
			}
			strcpy(msg->data, p);
			fclose(fp);

			return 1;
		}
	}
	//如果没找到
	fclose(fp);
	return 0;
}

//获取时间
void getdata(char data[])
{
	time_t t;
	struct tm *tp;
	time(&t);
	tp = localtime(&t);
	
	//将数据保存在data里面
	sprintf(data, "%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);

	return ;
}

//查询历史记录
void do_history(int acceptfd, 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 *)&acceptfd, &errmsg) != SQLITE_OK)
	{
		printf("error : %s\n", errmsg);
		sqlite3_free(errmsg);
	}
	msg->data[0] = '\0';
	send(acceptfd, msg, sizeof(MSG), 0);
	
	return;
}

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 <unistd.h>
#include <string.h>
#include <sqlite3.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#define  N  16
#define  R  1   //  注册
#define  L  2   //  登录
#define  Q  3   //  查询单词
#define  H  4   //  查询历史记录

#define DATABASE "my.db"

typedef struct 
{
	int type;
	char name[N];
	char data[256];   // password or word or remark
} MSG;

void do_register(int sockfd, MSG *msg);
int do_login(int sockfd, MSG *msg);
void do_query(int sockfd, MSG *msg);
void do_history(int sockfd, MSG *msg);

int main(int argc, char *argv[])
{
	int sockfd ;
	int n;
	struct sockaddr_in server_addr;
	MSG msg;
	if (argc < 3)
	{
		printf("Usage : %s <serv_ip> <serv_port>\n", argv[0]);
		exit(-1);
	}

	if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
	{
		perror("fail to socket");
		exit(-1);
	}

	bzero(&server_addr, sizeof(server_addr));	
	server_addr.sin_family = AF_INET;
	server_addr.sin_addr.s_addr = inet_addr(argv[1]);
	server_addr.sin_port = htons(atoi(argv[2]));
	
	if (connect(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0)
	{
		perror("fail to connect");
		exit(-1);
	}
	
	while(1)
	{
		printf("************************************\n");
		printf("* 1: register   2: login   3: quit *\n");
		printf("************************************\n");
		printf("please choose : ");

		if(scanf("%d", &n) <= 0){
			perror("scanf");
			exit(-1);
		}

		switch(n)
		{
			case 1:
				do_register(sockfd, &msg);
				break;
			case 2:
				if(do_login(sockfd, &msg) == 1)
				{
					goto next;
				}
				break;
			case 3:
				close(sockfd);
				exit(0);
		}
	}
next:
	while(1)
	{
		printf("************************************\n");
		printf("* 1: query   2: history   3: quit *\n");
		printf("************************************\n");
		printf("please choose : ");

		if(scanf("%d", &n) <= 0){
			perror("scanf");
			exit(-1);
		}

		switch(n)
		{
			case 1:
				do_query(sockfd, &msg);
				break;
			case 2:
				do_history(sockfd, &msg);
				break;
			case 3:
				close(sockfd);
				exit(0);
		}
	}
	return 0;
}

//注册
void do_register(int sockfd, MSG *msg)
{
	//输入指令以及用户名和密码,发送给服务器
	msg->type = R;
	
	printf("input your name:");
	scanf("%s", msg->name);	
	printf("input your password:");
	scanf("%s", msg->data);
	
	send(sockfd, msg, sizeof(MSG), 0);	
	//接收数据,判断是否注册成功
	recv(sockfd, msg, sizeof(MSG), 0);
	
	printf("register : %s\n", msg->data);	
	return;
}

//登录
int do_login(int sockfd, MSG *msg)
{
	//输入指令以及用户名和密码,发送给服务器
	msg->type = L;
	
	printf("input your name:");
	scanf("%s", msg->name);	
	printf("input your password:");
	scanf("%s", msg->data);
	
	send(sockfd, msg, sizeof(MSG), 0);	
	//接收数据,判断是否登录成功
	recv(sockfd, msg, sizeof(MSG), 0);
	
	//如果登录成功,返回1
	if(strncmp(msg->data, "OK", 3) == 0){
		printf("login : OK\n");
		return 1;
	}

	//登录失败返回0
	printf("login : %s\n", msg->data);	
	return 0;
}

//查询单词
void do_query(int sockfd, MSG *msg)
{
	//告知服务器查询单词
	msg->type = Q;
	puts("---------");

	while(1)
	{
		printf("input word : ");
		scanf("%s", msg->data);
	
		//如果输入#,回退到上一层
		if(strcmp(msg->data, "#") == 0){
			break;
		}

		//发送指令和单词
		send(sockfd, msg, sizeof(MSG), 0);	
		//接收数据
		recv(sockfd, msg, sizeof(MSG), 0);
		
		putchar(10);
		printf("********************************************************************************\n");
		printf("%s\n", msg->data);
		printf("********************************************************************************\n");
		putchar(10);
	}
	
	return;
}
//查询历史记录
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);
	}
	return;
}
Makefile
CC=gcc
CFGLASS=-Wall -g -c -O2

all:server client

server:server.o
	$(CC) $< -o $@ -lsqlite3

client:client.o
	$(CC) $< -o $@

%.o:%.c
	$(CC) $(CFGLASS) $< -o $@

.PHONY:
	clean

clean:
	$(RM) -rf *.o a.out server client

运行结果

操作步骤

(1)执行命令make,生成可执行文件server和client。

(2)执行命令./server 127.0.0.1 10001,运行服务器端。

(3)执行命令./client 127.0.0.1 10001,模拟客户1。

(4)执行命令./client 127.0.0.1 10001,模拟客户2。

(5)在客户端按下Ctrl+C,关闭客户连接。


服务器


客户端1


客户端2


下载

基于Linux在线英语词典


  • 3
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 在Linux下实现电子词典代码可以采用如下的步骤: 1. 安装相关的开发工具和库:在Linux系统中,可以安装GCC编译器、make工具和ncurses库等开发工具和库,它们能够辅助实现电子词典的代码。 2. 创建一个数据库:使用mysql或sqlite等数据库软件,在Linux系统上创建一个用于存储词典数据的数据库。 3. 下载词典数据:从网上下载词典数据文件,可以选择免费的开源词典数据或商业的付费词典数据。 4. 解析词典数据:编写程序,读取并解析词典数据文件,将其存储到之前创建的数据库中。 5. 实现查询功能:编写查询程序,接收用户输入的查询词,将其与数据库中的词汇进行匹配,并显示查询结果。 6. 添加用户界面:借助ncurses库等,设计一个用户友好的界面,使用户能够方便地输入查询词,并查看查询结果。 7. 完善功能:在基本的查询功能基础上,可以增加一些额外的功能,如单词朗读、查看详细释义等,提高用户体验。 8. 测试和调试:对代码进行测试和调试,确保电子词典Linux系统上正常运行。 9. 部署和发布:将代码部署到Linux系统上,并按需求进行发布,使其他用户能够使用这个电子词典。 总结:通过上述步骤,我们可以在Linux系统上实现一个电子词典代码,用户可以通过输入查询词,使用查询功能来查找词汇的释义等信息。 ### 回答2: 在Linux下实现电子词典代码可以通过使用Python编程语言来实现。以下是一个使用Python编写的简单电子词典代码示例: ```python import json def load_data(): # 从文件中加载词典数据 with open('dictionary.json', 'r') as file: data = json.load(file) return data def search_word(word): # 在词典数据中搜索指定的单词并返回解释 data = load_data() if word in data: return data[word] else: return '该单词不存在' def main(): while True: print('请输入要查询的单词:') word = input() definition = search_word(word) print('解释:', definition) print('-------------------------') if __name__ == '__main__': main() ``` 以上代码中的`load_data`函数用于加载存储词典数据的`dictionary.json`文件。`search_word`函数用于在加载的词典数据中搜索指定的单词并返回解释。`main`函数是程序的入口,通过循环接收用户输入的单词并调用`search_word`函数进行查询,最后将查询结果打印出来。 为了使代码能够正常运行,需要准备一个存储词典数据的`dictionary.json`文件,该文件应具有以下格式: ```json { "apple": "苹果", "banana": "香蕉", "cat": "猫", ... } ``` 注意,以上只是一个简单的示例代码,实际的电子词典代码可能需要更复杂的功能,例如模糊搜索、数据的更新与维护等。这需要根据具体的需求来进行代码设计与功能实现。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值