【网络编程】

项目:基于TCP的在线词典

功能演示

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

功能说明

大方向一共四个功能:
注册
登录
查询单词
查询历史记录

单词和解释保存在文件中,单词和解释只占一行,
一行最多300个字节,单词和解释之间至少有一个空格

也可以先写个程序,将文件中的内容先都插入到数据库中。

fgets--->  sqlite3_exec("INSERT INTO dict VALUES('word','解释')");

实现TCP并发 --多进程 多线程 io多路复用 均可

建表语句:–在sqlite3终端执行即可

CREATE TABLE usr (name TEXT PRIMARY KEY, pass TEXT);
CREATE TABLE record (name TEXT, date TEXT, word TEXT);

流程图

在这里插入图片描述
在这里插入图片描述

功能实现

1、搭建程序框架
2、实现注册和登录功能
3、 查单词
4、 查历史记录

代码实现

服务器:

#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>
#include <sys/wait.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_register(int connectfd, MSG *msg, sqlite3 *db);
void do_login(int connectfd, MSG *msg, sqlite3 *db);
void do_query(int connectfd, MSG *msg, sqlite3 *db);
void do_history(int connectfd, MSG *msg, sqlite3 *db);
void do_client(int connectfd, sqlite3 *db);
int  do_searchword(int connectfd, MSG *msg);
void getdata(char data[]);
int history_callback(void *arg, int f_num, char **f_value, char **f_name);

void handler(int sig)
{
    wait(NULL);
}

int main(int argc, char *argv[])
{
	int listenfd, connectfd;
	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 ((listenfd = socket(PF_INET, SOCK_STREAM, 0)) < 0)
	{
		perror("fail to socket");
		exit(-1);
	}
	
    bzero(&server_addr, sizeof(server_addr));
	server_addr.sin_family = PF_INET;
	server_addr.sin_addr.s_addr = inet_addr(argv[1]);
	server_addr.sin_port = htons(atoi(argv[2]));
	
    if (bind(listenfd, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0)
	{
		perror("fail to bind");
		exit(-1);
	}
	
    if (listen(listenfd, 5) < 0)
	{
		perror("fail to listen");
		exit(-1);
	}
	
    signal(SIGCHLD, handler);//处理僵尸进程
	
    while ( 1 )
	{
		if ((connectfd = accept(listenfd, NULL, NULL)) < 0)
		{
			perror("fail to accept");
			exit(-1);
		}
		
        if ((pid = fork()) < 0)
		{
			perror("fail to fork");
			exit(-1);
		}
		else if(pid == 0) //子进程执行处理代码
		{
			do_client(connectfd, db);
		}
        else  //父进程负责连接
        {
		    close(connectfd);
        }
	}
	return 0;
}

void do_client(int connectfd, sqlite3 *db)
{
	MSG msg;
	while (recv(connectfd, &msg, sizeof(MSG), 0) > 0)  // receive request
	{
		printf("type = %d\n", msg.type);
		printf("type = %s\n", msg.data);
		switch ( msg.type )
		{
		case R :
			do_register(connectfd, &msg, db);
			break;
		case L :
			do_login(connectfd, &msg, db);
			break;
		case Q :
			do_query(connectfd, &msg, db);
			break;
		case H :
			do_history(connectfd, &msg, db);
			break;
		}
	}
	printf("client quit\n");
	exit(0);
	return;
}

void do_register(int connectfd, MSG *msg, sqlite3 *db)
{
	char sqlstr[512] = {0};
	char *errmsg;
    
    //使用sqlite3_exec函数调用插入函数判断是否能够插入成功
    //由于用户名设置为主键,所以如果用户名已经存在就会报错
	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(connectfd, msg, sizeof(MSG), 0);

	return;
}

void do_login(int connectfd, MSG *msg, sqlite3 *db)
{
	char sqlstr[512] = {0};
	char *errmsg, **result;
	int nrow, ncolumn;

    //通过sqlite3_get_table函数查询记录是否存在
	sprintf(sqlstr, "select * from usr where name = '%s' and pass = '%s'", msg->name, msg->data);
	if(sqlite3_get_table(db, sqlstr, &result, &nrow, &ncolumn, &errmsg) != SQLITE_OK)
	{
		printf("error : %s\n", errmsg);
	}
    //通过nrow参数判断是否能够查询到疾记录,如果值为0,则查询不到,如果值为非0,则查询到
	if(nrow == 0)
	{
		strcpy(msg->data, "name or password is wrony!!!");
	}
	else
	{
		strncpy(msg->data, "OK", 256);
	}

	send(connectfd, msg, sizeof(MSG), 0);

	return;
}

void do_query(int connectfd, MSG *msg, sqlite3 *db)
{
	char sqlstr[128], *errmsg;
	int found = 0;
	char date[128], word[128];

	strcpy(word, msg->data);

	//通过found保存查询结果
	found = do_searchword(connectfd, msg);

	//如果执行成功,还需要保存历史记录
	if(found == 1)
	{
		//获取时间
		getdata(date);
		//通过sqlite3_exec函数插入数据
		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);
		}
	}

	send(connectfd, msg, sizeof(MSG), 0);

	return;
}

int do_searchword(int connectfd, 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(connectfd, msg, sizeof(MSG), 0);
	}
	//printf("query word is %s len = %d\n", msg->data, len);
    
    //每次读取一行内容
	int flags = 0;
	while(fgets(temp, 300, fp) != NULL)
	{
		//比较单词
		result = strncmp(msg->data, temp, len);
		
		if(result == 0 && temp[len] == ' ')
        {
			//p保存单词后面第一个空格的首地址
			p = temp + len;
			
			//移动p,让p保存解释的第一个字符的首地址
			while(*p == ' ')
			{
				p++;
			}

			//将解释保存在data里面
			strcpy(msg->data, p);
			
			fclose(fp);
			return 1;
        }
	}

	strcpy(msg->data, "not found");
	fclose(fp);
	return 0;
}

void getdata(char *data)
{
	time_t t;
	struct tm *tp;
	time(&t);
	tp = localtime(&t);
	sprintf(data, "%d-%d-%d %d:%d:%d",  1900+tp->tm_year,  1+tp->tm_mon,  tp->tm_mday, \
					tp->tm_hour, tp->tm_min, tp->tm_sec);
}

void do_history(int connectfd, 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 *)&connectfd, &errmsg) != SQLITE_OK)
	{
		printf("error : %s\n", errmsg);
		sqlite3_free(errmsg);
	}

	//发送结束标志
	strcpy(msg->data, "**OVER**");
	send(connectfd, msg, sizeof(MSG), 0);
	
	return;
}

//通过回调函数发送时间和单词
int history_callback(void *arg, int f_num, char **f_value, char **f_name)
{
	int connectfd;
	MSG msg;
	connectfd = *(int *)arg;
	sprintf(msg.data, "%s : %s", f_value[1], f_value[2]);
	send(connectfd, &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   //  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 or remark
} MSG;

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

int main(int argc, char *argv[])
{
	int socketfd ;
	struct sockaddr_in server_addr;
	MSG msg;
	if(argc < 3){
		printf("Usage : %s <serv_ip> <serv_port>\n", argv[0]);
		exit(-1);
	}
	if(-1 == (socketfd = socket(PF_INET, SOCK_STREAM, 0))){
		perror("fail to socket");
		exit(-1);
	}
	
	bzero(&server_addr, sizeof(server_addr));
	server_addr.sin_family = PF_INET;
	server_addr.sin_addr.s_addr = inet_addr(argv[1]);
	server_addr.sin_port = htons(atoi(argv[2]));
	
	if(-1 == connect(socketfd, (struct sockaddr *)&server_addr, sizeof(server_addr))){
		perror("fail to connect");
		exit(-1);
	}
	
	int choose = 0;
	while(1)
	{
		printf("************************************\n");
		printf("* 1: register   2: login   3: quit *\n");
		printf("************************************\n");
		printf("please choose : ");

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

		switch(choose){
			case 1:
				do_register(socketfd, &msg);
				break;
			case 2:
                //执行登录函数,执行完毕后通过返回值决定是否要跳转到下一个菜单
				if(do_login(socketfd, &msg) == 1){
					goto next;
				}
				break;
			case 3:
				close(socketfd);
				exit(0);
		}
	}
next:
	while(1){
		printf("************************************\n");
		printf("* 1: query   2: history   3: quit  *\n");
		printf("************************************\n");
		printf("please choose : ");

		if(scanf("%d", &choose) <= 0){
			perror("scanf");
			exit(-1);
		}
		switch(choose){
			case 1:
				do_query(socketfd, &msg);
				break;
			case 2:
				do_history(socketfd, &msg);
				break;
			case 3:
				close(socketfd);
				exit(0);
		}
	}
	return 0;
}

void do_register(int socketfd, MSG *msg){
    //指定操作码
	msg->type = R;
    //输入用户名
	printf("input your name:");
	scanf("%s", msg->name);
	//输入密码
    printf("input your password:");
	scanf("%s", msg->data);
	//发送数据
    send(socketfd, msg, sizeof(MSG), 0);
	//接收数据并输出
    recv(socketfd, msg, sizeof(MSG), 0);
	printf("register : %s\n", msg->data);
	
    return;
}

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

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

void do_query(int socketfd, MSG *msg){
	msg->type = Q;
	puts("-----------------------------");

	while(1){
		printf("input word (if # is end): ");
		scanf("%s", msg->data);

        //如果输入的是#,返回上一级
		if(strcmp(msg->data, "#") == 0){
			break;
		}

		send(socketfd, msg, sizeof(MSG), 0);
		
		recv(socketfd, msg, sizeof(MSG), 0);
		printf(">>> %s\n", msg->data);
	}
	return;
}

void do_history(int socketfd, MSG *msg){
	msg->type = H;
	send(socketfd, msg, sizeof(MSG), 0);

	while(1){
		recv(socketfd, msg, sizeof(MSG), 0);

		if(strcmp(msg->data, "**OVER**") == 0){
            break;
        }

		printf("%s\n", msg->data);
	}

	return;
}

将字典文件导入数据库的程序:

#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <sqlite3.h>
#include <unistd.h>

#define DATABASE "my.db"

int main(int argc, char const *argv[])
{
    //把文件导入数据库中
    sqlite3 *db;
    FILE *fp;
    if(SQLITE_OK !=sqlite3_open(DATABASE,&db)){
        perror("sqlite err");
        exit(1);
    }
    fp = fopen("dict.txt","r");
    if(fp==NULL){
        perror("err");
        exit(1);
    }
    char str[300]={0};
    char word[50]={0};
    char introduct[250]={0};
    char *errmsg;
    char sql[500]={0};
    sprintf(sql,"drop table dict");
    if(SQLITE_OK !=sqlite3_exec(db,sql,NULL,NULL,&errmsg)){
        printf("drop table dict error!!!\n");
    }else{
        printf("drop table dict yes!!!\n");
    }
    sprintf(sql,"create table if not exists dict(word text,translation text)");
    if(SQLITE_OK !=sqlite3_exec(db,sql,NULL,NULL,&errmsg)){
        printf("表已经存在!!!\n");
    }else{
        printf("表创建成功!!!\n");
    }
    int count=0;
    while ((fgets(str,300,fp))!=NULL)
    {
		//usleep(10000);
		memset(word, 0, 300);
        int i=0;
        char *p=str;
		str[strlen(str)-1] = '\0';
        //printf("str = [%s]\n",str);
        while (*p!=' ')
        {
            word[i]=*p;
            p++;
            i++;
        }
        word[i]='\0';
        p++;
        while(*p==' ' && *p != '\0')
        {
            p++;
        }


		//处理 两个字段值中的 单引号  sqlite3 数据库  text 字段
		//不能插入带有单引号的字符串   如   one's  转换成   one.s 再插入
		char *temp = p;
		while(*temp != '\0'){
			if(*temp == '\''){
				*temp = '.';
			}
			temp++;
		}
		temp = word;
		while(*temp != '\0'){
			if(*temp == '\''){
				*temp = '.';
			}
			temp++;
		}


        strcpy(introduct, p);
        introduct[strlen(introduct)-1]='\0';
        printf("insert count = [%d]\tword:[%s]\t\ttranslation:[%s]\n", count, word, introduct);
       
        //插入数据
        sprintf(sql,"INSERT INTO dict (word,translation) VALUES('%s','%s')",word,introduct);
        int ret =-1;
        if(SQLITE_OK !=(ret=sqlite3_exec(db,sql,NULL,NULL,&errmsg))){
            printf("sql err : %s\n", errmsg); 
            exit(1); 
        }
        count++;
		memset(str, 0, 300);
    }

    printf("sum count = %d , process end......................\n", count);
    return 0;
}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值