在线英英词典Linux C语言网络编程

前言:

仅做记录和分享自己学习的过程,学艺不精,code新人,如有错误欢迎在评论区讨论、指正!

一、项目功能简介

        在线英英词典
   1、用户注册和登录验证
   1.1服务器:将用户信息和历史记录保存在数据库中。
   1.2客户端:输入用户名和密码
   1.3服务器:在数据库中查找、匹配,返回结果.

   2、单词在线翻译
   根据客户端输入的单词在字典文件中搜索

   3、历史记录查询显示查找记录和查找时间

二、项目演示截图

1.登录注册界面

2.用户功能界面

三、项目设计思想

因为要实现的主要是查询等一系列的工作,所以选择用数据库结合网络来做。

项目主要分为三个板块:数据库、客户端、服务器

数据库部分

数据库中有三张表,分别是:词典表,用户信息表,跟查询记录表

name(name text ,pswd text);//用户信息

dict(word text,explain text);//词典内容

record(name text,word text,explain text,restime text);//查询记录

数据导入

因为只需要导入一次词典,所以就放在服务器外面,避免每次运行都重复导入词典,

这里给出的词典参考内容是txt文件,

通过循环调用 fgets函数,读取txt文本内容,并插入到词典表中

注意对每行的末尾的换行符进行处理\r\n两个符号都要处理,

还有文本中自带的'’可能会影响sql语句,所以将全部的‘’在文本文件中替换为,再导入

观察文本发现,每一行由 单词 空格 解释 三部分组成,

#include"head.h"
int main()
{

//创建数据库,并打开
	int err;
	sqlite3* db;
	if (0 != (err = sqlite3_open("dict.db", &db)))
	{
		fprintf(stderr, "open database:%s\n", strerror(err));
		return -1;
	}
//创建词典表
	/*primary key*/
	char* errmsg1 = NULL;
	char* sql1 = "create table if not exists dict(word text ,explain text)";
	if (0 != sqlite3_exec(db, sql1, NULL, NULL, &errmsg1))
	{
		fprintf(stderr, "create table:%s\n", errmsg1);
		return -1;
	}
/*将文档中的单词导入数据库, 
用标准I/O的方法,使用fgets函数读入一行,
读入后将单词和解释分割开来,插入单词表中。*/
	char buf[500];
	char word[25];
	char explain[400];
	char sql3[500];
	char* errmsg3 = NULL;
	int  i;
	FILE* fp = fopen("./dict.txt", "r");
	if(fp==NULL)
	{
		perror("fopen:");
		return -1;
	}
	while (fgets(buf, sizeof(buf), fp))//循环读取一行
	{
		buf[strlen(buf)-1]='\0';//处理末尾换行符\r\n;
		buf[strlen(buf)-1]='\0';
	 	i=0;
		memset(word, 0, sizeof(word));//单词
		memset(explain, 0, sizeof(explain));//解释
		memset(sql3, 0, sizeof(sql3));//sql语句
		while (buf[i] != ' ')	i++;
		strncpy(word, buf, i);//拷贝单词到Word
		buf[i]='\0';
		i++;
		while (buf[i] == ' ')	 i++;//跳过空格
		strcpy(explain, buf + i);
		sprintf(sql3, "insert into dict values('%s','%s')", word, explain); //把变量按照格式存在sql中
		
		if (0 != sqlite3_exec(db,sql3,NULL,NULL,&errmsg3))
		{
			fprintf(stderr, "dict create:%s\n", errmsg3);
			return -1;
		}	
	}
	
	return 0;
}

客户端

1.连接部分:创建套接字,连接服务器

2.登录注册界面:三个选项每选择一个选项,向服务器发送相应的请求) 

1.登录login、2.注册register3.退出quit

选择登录,调用do_login函数,登录成功则跳转到功能界面

选择注册,调用do_register函数,进行注册操作

选择退出,发送"quit"命令给服务器,关闭套接字并退出程序

3.功能界面:三个选项(每选择一个选项,向服务器发送相应的请求)

1.翻译translation2.历史记录查询history3.返回上级菜单go back

选择翻译:调用do_translation函数,进行单词翻译操作

选择查询历史记录:调用do_history函数进行历史记录查询操作

选择go back 返回上级菜单()

4.关闭套接字并退出程序

服务器

这里采用信号处理方式,处理僵尸进程,

void handler(int sig)//用信号机制处理僵尸进程
{
    while (waitpid(-1, NULL, WNOHANG) > 0);
}

、、、、、、、、、、、、

signal(SIGCHLD, handler);

1.“打开数据库和创建:用户表(name,pswd),和记录表(name,word,explain,restime)

2. 连接准备:创建套接字,绑定本机地址和端口,监听连接

3. 循环接收客户端连接创建子进程fork,处理客服端请求

(1).登录:logindo_login:在user表中查找到相应记录,则登录成功                   
(2).注册:registerdo_register,向user表中插入一条新记录                                
(3).翻译:translation:do_translation,向record表中插入一条新记录                     
(4).历史查询:history:do_history,在record表中查找相应的记录,并发送给客户端 

四、代码实现

头文件head.h

为了方便使用,将许多头文件放入一个head.h文件中(本项目并没有使用到head.h中所有头文件)


#ifndef __HEAD_H__
#define __HEAD_H__

#include <stdio.h>
#include <string.h>
#include <strings.h>
#include <unistd.h>
#include <stdlib.h>
//file io
#include <sys/types.h>
#include <sys/stat.h>
#include<fcntl.h>
//network
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>

#include <sqlite3.h>
#include<dirent.h>
#include<time.h>
#define SIZE 64

typedef struct sockaddr SA;   //通用地址结构
typedef struct sockaddr_in IPaddr_t; //IPv4地址结构

typedef struct 
{
	char name[30];
	char word[30];
	char explain[300];
	char restime[50];
}msg_t;


#endif

客服端client.c

#include "head.h"
char username[30];
//注册
void do_register(int sockfd)
{
    char name[30];
    char pswd[30];
    char order[SIZE] = "register";//请求
    send(sockfd, order, SIZE, 0);//发送注册请求

    printf("请输入账号:");
    fgets(name, sizeof(name), stdin);
    name[strlen(name) - 1] = '\0';
    printf("请输入密码:");
    fgets(pswd, sizeof(pswd), stdin);
    pswd[strlen(pswd) - 1] = '\0';

    send(sockfd, name, sizeof(name), 0);
    send(sockfd, pswd, sizeof(pswd), 0);

    recv(sockfd, order, 2, 0);
    if (strcmp(order, "Y") == 0)
        printf("注册成功\n");
    else {
        printf("注册失败\n");

    }

}
//登录,成功返回1,失败返回0
int do_login(int sockfd)
{
    int ret = 0;
    char name[30];
    char pswd[30];
    char order[SIZE] = "login";
    send(sockfd, order, SIZE, 0);

    printf("请输入账号:");
    fgets(name, sizeof(name), stdin);
    name[strlen(name) - 1] = '\0';
    printf("请输入密码:");
    fgets(pswd, sizeof(pswd), stdin);
    pswd[strlen(pswd) - 1] = '\0';
    send(sockfd, name, sizeof(name), 0);
    send(sockfd, pswd, sizeof(pswd), 0);

    recv(sockfd, order, 2, 0);
    if (strcmp(order, "Y") == 0)
    {
        printf("登录成功!\n");
        ret = 1;
        strcpy(username, name);
    }

    else
        printf("登录失败\n");
    return ret;
}
//翻译,英英翻译,输入单词,查询得到解释
int  do_translation(int sockfd)
{
    int ret = 0;
    char order[SIZE] = "translation";
    char word[30];
    char explain[400];
    send(sockfd, order, SIZE, 0);

    printf("请输入需要查询的单词(go back 返回上级菜单):");
    fgets(word, sizeof(word), stdin);
    word[strlen(word) - 1] = '\0';//去掉换行符,避免干扰单词比较
    send(sockfd, word, sizeof(word), 0);
    //printf("word:%s\n",word);
    if (0 == strcmp(word, "go back")) {
        ret = 1;
        return ret;
    }


    recv(sockfd, order, 2, 0);

    if (0 == strcmp(order, "Y"))
    {
        recv(sockfd, explain, sizeof(explain), 0);
        printf("explain:%s\n", explain);
        send(sockfd, username, sizeof(username), 0);
    }
    else
    {
        printf("未查询到该单词\n");
        //printf("hello\n");
    }
}
//历史记录查询
int do_history(int sockfd)
{
    char order[SIZE] = "history";
    char name[30];
    char len[20];
    char record[500];
    int nrow, ncolumn;
    int ret;
    msg_t msg;
    send(sockfd, order, SIZE, 0);

   printf("请输入需要查询的账号(go back 返回上级菜单):");
   printf("(go back 返回上级菜单):");
   fgets(name, sizeof(name), stdin);
   name[strlen(name) - 1] = '\0';
    send(sockfd, username, sizeof(username), 0);
    if (0 == strcmp(name, "go back")) {
        ret = 1;
        return ret;
    }

    recv(sockfd, order, 2, 0);
    if (strcmp(order, "Y") == 0)
    {
       
       recv(sockfd, &nrow, sizeof(int), 0);
	printf("nrow:%d\n",nrow);
	
        int i;
        for (i = 0; i < nrow + 1; i++)
        {
            
                recv(sockfd, &msg, sizeof(msg), 0);
                printf("%s | %s | %s | %s\n", msg.name,msg.word,msg.explain,msg.restime);   
        }
        printf("共%d条记录\n",nrow);
    }
    else
    {
        printf("未查询到相关记录\n");
    }
  
}
int main()
{
    //1.创建套接字
    int sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd < 0) {
        perror("socket");
        return -1;
    }
    printf("socket success!\n");

    //2.主动连接服务器
    IPaddr_t srvaddr = {
        .sin_family = AF_INET,  //IPv4
        .sin_port = htons(5555), //port
        .sin_addr.s_addr = inet_addr("127.0.0.1")
    };
    if (0 > connect(sockfd, (SA*)&srvaddr, sizeof(srvaddr))) {
        perror("connect");
        return -1;
    }
    printf("connect success!\n");

    //3.通信,实现功能
    int num, num1, ret, ret1,ret2;
    char order[SIZE];
AAA:
    while (1)//登陆注册界面
    {
        printf("欢迎来到英语知识海洋,这是你需要的英英词典\n");
        printf("1.login		2.register		3.quit\n");
        printf("请输入你的选择:");
        if (1 != scanf("%d", &num))
        {
            printf("输入错误,请重新输入\n");
            fgets(order, SIZE, stdin);//吃掉空格
            //continue;
        }
        fgets(order, SIZE, stdin);//吃掉回车
        switch (num)
        {
        case 1:ret = do_login(sockfd); if (1 == ret)  goto XXX;  break;
        case 2:do_register(sockfd); break;
        case 3:send(sockfd,"quit",5,0);close(sockfd); exit(0);
        default:printf("输入错误,请重新输入\n"); break;
        }
    }
XXX:
    while (1)//功能界面
    {
      
        printf("***********************************************\n");
        printf("1.translation	2.history	3.go back\n");
        printf("请输入你的选择:");
        if (1 != scanf("%d", &num1))
        {
            printf("输入错误,请重新输入\n");
            fgets(order, SIZE, stdin);//吃掉空格
            continue;
        }
        fgets(order, SIZE, stdin);//吃掉回车

        switch (num1)
        {
        case 1:
            while (1) {
                ret1 = do_translation(sockfd);
                if (ret1 == 1)
                    break;
            }		break;            
           
        case 2:
        	while(1)
        	{
        		ret2=do_history(sockfd); 
        		if (ret2== 1)
                    	break;	
                 }	break;
        	
        case 3:	goto AAA; break;
        default:	printf("输入错误,请重新输入\n"); break;
        }

    }

    //4.关闭套接字
    close(sockfd);
    return 0;

}

服务器server.c

注册成功,更新user用户表(name,pswd);

查询单词成功,更新record记录表(name,word,explain,restime);

这里用time()和localtime()获取系统时间,并记录

time_t tm = time(NULL);//返回当前的时间戳(以秒为单位)
struct tm* lt;//将时间戳转换为本地时间的结构体指针。
 lt = localtime(&tm);

其中tm结构体成员如下:

#include"head.h"
#include <signal.h>
#include <sys/wait.h>
//注册
void do_register(int connfd, sqlite3* db)
{
	char sql[128];
	char name[30];
	char pswd[30];

	char* errmsg;
	recv(connfd, name, sizeof(name), 0);
	recv(connfd, pswd, sizeof(pswd), 0);
	sprintf(sql, "insert into user values('%s','%s')", name, pswd);//插入user表
	if (0 != sqlite3_exec(db, sql, NULL, NULL, &errmsg))//注册失败,发送失败信号给客户端
	{
		fprintf(stderr, "register:%s\n", errmsg);
		send(connfd, "N", 2, 0);
		return;
	}
	send(connfd, "Y", 2, 0);

}
//登录
void do_login(int connfd, sqlite3* db)
{
	char name[30];
	char pswd[30];
	char order[SIZE];
	char sql[128];
	char** resultp = NULL;
	char* errmsg = NULL;
	int nrow, ncolumn;
	recv(connfd, name, sizeof(name), 0);
	recv(connfd, pswd, sizeof(pswd), 0);


	sprintf(sql, "select * from user where name='%s' and pswd='%s'", name, pswd);

	if (0 != sqlite3_get_table(db, sql, &resultp, &nrow, &ncolumn, &errmsg))
	{
		fprintf(stderr, "lodin:%s\n", errmsg);
		send(connfd, "N", 2, 0);
		return;
	}
	if (nrow > 0)//查找到记录
		send(connfd, "Y", 2, 0);
	else
		send(connfd, "N", 2, 0);

}
//单词在线翻译
void do_translation(int connfd, sqlite3* db)
{
	char word[30];
	char name[30];
	char restime[100];
	char explain[400];
	char sql[128];
	char sql1[600];
	char* errmsg1 = NULL;
	char** resultp = NULL;
	char* errmsg = NULL;
	char buf[400];
	int nrow, ncolumn;

	time_t tm = time(NULL);//返回当前的时间戳(以秒为单位)
	struct tm* lt;//将时间戳转换为本地时间的结构体指针。
	lt = localtime(&tm);
	sprintf(restime, "%d-%d-%d  %d:%d:%d", lt->tm_year + 1900, lt->tm_mon + 1, lt->tm_mday, lt->tm_hour, \
		lt->tm_min, lt->tm_sec);

	recv(connfd, word, sizeof(word), 0);
	if (strcmp(word, "go back") == 0)
		return;

	sprintf(sql, "select explain from dict  where word='%s'", word);
	if (0 != sqlite3_get_table(db, sql, &resultp, &nrow, &ncolumn, &errmsg))
	{
		fprintf(stderr, "translation:%s", errmsg);
		return;
	}


	if (nrow > 0)
	{
		send(connfd, "Y", 2, 0);
		strcpy(explain, resultp[1]);
		//printf("explain:%s\n", explain);
		send(connfd, explain, sizeof(explain), 0);

		recv(connfd, name, sizeof(name), 0);//name

		//查找单词成功,在查询表中添加一条记录,name,word,explain,restime
		sprintf(sql1, "insert into record values('%s','%s','%s','%s')", name, word, explain, restime);
		//sprintf(sql, "insert into user values('%s','%s')", name, pswd);//插入user表
		//printf("%s\n", sql1);
		if (0 != sqlite3_exec(db, sql1, NULL, NULL, &errmsg1))
		{
			fprintf(stderr, "record:%s", errmsg);
			return;
		}
		//printf("本次查询记录成功\n");

	}
	else
		send(connfd, "N", 2, 0);
}
//历史记录查询
void do_history(int connfd, sqlite3* db)
{
	msg_t msg;
	memset(&msg, 0, sizeof(msg));
	char name[30];
	char sql[128];


	char** resultp = NULL;
	char* errmsg = NULL;
	int nrow, ncolumn;
	recv(connfd, name, sizeof(name), 0);


	sprintf(sql, "select * from record where name='%s'", name);
	if (sqlite3_get_table(db, sql, &resultp, &nrow, &ncolumn, &errmsg))
	{
		fprintf(stderr, "history:%s", errmsg);
		return;
	}
	//printf("sql:%s nrow:%d\n", sql, nrow);
	strcpy(msg.name, name);

	if (nrow > 0)
	{

		send(connfd, "Y", 2, 0);

		send(connfd, &nrow, sizeof(int), 0);
		int i, j, count = 0;
		for (i = 0; i < nrow + 1; i++)
		{
			memset(&msg, 0, sizeof(msg));
			for (j = 0; j < ncolumn; j++)
			{
				if (j == 0)	strcpy(msg.name, resultp[count++]);
				else if (j == 1)	strcpy(msg.word, resultp[count++]);
				else if (j == 2)	strcpy(msg.explain, resultp[count++]);
				else strcpy(msg.restime, resultp[count++]);
			}

			send(connfd, &msg, sizeof(msg), 0);

		}

	}
	else
	{
		send(connfd, "N", 2, 0);
	}

}
void handler(int sig)//用信号机制处理僵尸进程
{
	while (waitpid(-1, NULL, WNOHANG) > 0);
}

int main()
{
	signal(SIGCHLD, handler);
	//创建数据库,并打开
	int err;
	sqlite3* db;
	if (0 != (err = sqlite3_open("dict.db", &db)))
	{
		fprintf(stderr, "open database:%s\n", strerror(err));
		return -1;
	}
	//创建用户表
	char* errmsg = NULL;
	char* sql = "create table if not exists user(name text primary key,pswd text)";
	if (0 != sqlite3_exec(db, sql, NULL, NULL, &errmsg))
	{
		fprintf(stderr, "create table:%s\n", errmsg);
		return -1;
	}

	// 创建查询记录表
	char* errmsg2 = NULL;
	char* sql2 = "create table if not exists record(name text,word text,explain text,sertime text)";
	if (0 != sqlite3_exec(db, sql2, NULL, NULL, &errmsg2))
	{
		fprintf(stderr, "create table:%s\n", errmsg2);
		return -1;
	}

	//1.创建套接字
	int sockfd = socket(AF_INET, SOCK_STREAM, 0);
	if (sockfd < 0) {
		perror("socket");
		return -1;
	}
	printf("socket success!\n");

	//2.设置重用本机地址和端口
	int on = 1;
	if (0 > setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on))) {
		perror("setsocket");
		return -1;
	}

	//3.绑定本机通信地址和端口
	IPaddr_t srvaddr = {
		.sin_family = AF_INET,  //IPv4
		.sin_port = htons(5555), //port
		.sin_addr.s_addr = htonl(INADDR_ANY),
		//.sin_addr.s_addr=inet_addr("192.168.7.199")
	};
	if (0 > bind(sockfd, (SA*)&srvaddr, sizeof(srvaddr))) {
		perror("bind");
		return -1;
	}
	printf("bind success!\n");

	//4.设置监听套接字
	if (0 > listen(sockfd, 5)) {
		perror("listen");
		return -1;
	}
	printf("listen success\n");


	
	pid_t pid;
	int ret;
	char order[SIZE];
	while (1)
	{
		//5.接收客户端的连接
		IPaddr_t cliaddr;
		socklen_t addrlen = sizeof(cliaddr);  //需要初始化
		int connfd = accept(sockfd, (SA*)&cliaddr, &addrlen);
		if (connfd < 0) {
			perror("accept");
			return -1;
		}
		printf("accept: IP-%s	 Port_%hu\n", inet_ntoa(cliaddr.sin_addr), ntohs(cliaddr.sin_port));
		
		//6.通信,实现功能
		if (0 > (pid = fork()))
		{
			perror("fork:");
			break;
		}
		else if (0 == pid)//子进程
		{
			close(sockfd);
			while (1)
			{
					ret = recv(connfd, order, SIZE, 0);
					if (ret < 0)
					{
						perror("recv:");
						break;
					}
					if (strcmp(order, "login") == 0)
						do_login(connfd, db);


					else if (strcmp(order, "register") == 0)
						do_register(connfd, db);

					else if (strcmp(order, "translation") == 0)
						do_translation(connfd, db);

					else
						do_history(connfd, db);

			}
			close(connfd);
			exit(0);
		}
		close(connfd);

	}
	//7.关闭套接字
	close(sockfd);
	sqlite3_close(db);
	return 0;
}

sprintf( )和sscanf( ):

sprintf()sscanf() 是C语言中的两个函数,用于格式化字符串的输入输出操作

sprintf() 函数:

功能:将格式化的数据写入字符串中。

用法:sprintf() 的原型是 int sprintf(char *str, const char *format, ...);
... 是可变参数列表,包含要格式化输出的数据。
format 是格式控制字符串,类似于 printf() 函数中的格式字符串,指定输出的格式和数据类型。
str 是目标字符串的指针,用来接收格式化后的输出;
实例:
char buf[100];
int num = 123;
sprintf(buf, "The number is: %d", num);
printf("string: %s\n", buf);
输出:string: The number is: 123
sscanf() 函数:

功能:根据指定的格式从字符串中读取格式化的输入

用法:sscanf() 的原型是 int sscanf(const char *str, const char *format, ...);

str :       输入的字符串,从这个字符串中读取数据。
format :    格式控制字符串,指定如何解析输入字符串中的数据。
... :       可变参数列表,用来接收解析出来的数据。
示例:

char input[] = "John 25 1.75";
char name[20];
int age;
float height;
sscanf(input, "%s %d %f", name, &age, &height);
printf("Name: %s, Age: %d, Height: %.2f\n", name, age, height);
输出:Name: John, Age: 25, Height: 1.75


sprintf() 用于将格式化的数据输出到字符串中,类似于 printf() 将输出写入到控制台。
sscanf() 用于从字符串中读取格式化的输入数据,类似于 scanf() 从控制台读取输入数据。

五、实验总结

这是第一次实打实的自己实现项目,个人感觉还蛮不容易的,从搭建框架到接口函数的实现再代码的调整,在不断地调整中终于实现了这些基础功能,希望在接下来的学习和生活中再接再厉,取得进步。

  • 10
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值