项目:电子词典

本文描述了一个基于TCP的服务器程序,实现了登录注册、单词查询、历史记录存储等功能。服务器使用SQLite数据库存储用户信息和查询历史,支持多客户端连接。客户端通过发送特定指令与服务器交互,进行相应的操作。
摘要由CSDN通过智能技术生成

项目要求:
1.登录注册功能,不能重复登录,重复注册。用户信息也存储在数据库中
2.单词查询功能
3.历史记录功能,存储单词,意思,以及查询时间,存储在数据库
4基于TCP,支持多客户端连接
采用数据库保存用户信息与历史记录
将dict.txt的数据导入到数据库中保存
7.返回上级、按下ctrl+c退出客户端后,该客户端退出登录

服务器头文件

#ifndef __H_H__          
#define __H_H__          
#include <sqlite3.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <strings.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include <sys/wait.h>
#include <stdlib.h>    
#include <sqlite3.h>
#include <signal.h>
#include <time.h>
#define PORT 6666              
#define IP "192.168.50.10"        
typedef void (*sighandler_t)(int);
#define ERR_MSG(msg) do{\
	fprintf(stderr,"line:__%d__\n",__LINE__);\
	perror(msg);\
}while(0)    
 
struct msg{                          
    char type;             //消息类型
    char name[128];    //名字        
    char password[128];   //密码     
    char word[128];       //单词     
    char mean[128];      //翻译 
    char time[128];      //时间  
}sermsg;                             

sqlite3* sqlite3_database();  //创建数据库表
int cli_net();            //创建套接字.链接客户端.绑定信息结构体
int sqlite3_close(sqlite3* db);   //关闭数据库
int callBack(void *arg, int column, char **colunm_text, char **colunm_name);
int find_callBack(void *arg, int column, char **column_text, char **column_name);
int sqlite3_worddelete(sqlite3 *db);   //删除单词表函数
int ser_recv(int newfd, struct sockaddr_in cin,sqlite3 *db);  //接收函数
int do_login(int newfd,sqlite3 *db);   //登录函数
int do_register(int newfd, sqlite3 *db);   //注册函数
int do_find(int newfd,sqlite3 *db);       //查询单词
int do_history(int newfd, sqlite3 *db);    //查询历史记录
int do_quit(int newfd,sqlite3 *db);  //退出
void handler(int sig);    //捕获僵尸进程                               
#endif 

服务器代码

#include "h.h"
int main(int argc, char const *argv[])
{
	int sfd = cli_net();
	sqlite3 *db = NULL;

	if (sqlite3_open("dict.db", &db) != SQLITE_OK)
	{
		fprintf(stderr, "sqlite3_open failed:%d %s__%d__\n", sqlite3_errcode(db), sqlite3_errmsg(db), __LINE__);
		return -1;
	}
	printf("sqlite3_open success\n");
	//注册储存表
	char sql[128] = "create table if not exists stu1(name char PRIMARY KEY,password char)";
	char *errmsg = NULL;
	if (sqlite3_exec(db, sql, NULL, NULL, &errmsg) != SQLITE_OK)
	{
		fprintf(stderr, "sqlite3_exec failed:%d %s__%d__\n", sqlite3_errcode(db), sqlite3_errmsg(db), __LINE__);
		return -1;
	}
	printf("创建stu1表格成功 __%d__\n", __LINE__);
	//登录储存表
	char sql1[128] = "create table if not exists stu2(name char PRIMARY KEY)";
	if (sqlite3_exec(db, sql1, NULL, NULL, &errmsg) != SQLITE_OK)
	{
		fprintf(stderr, "sqlite3_exec failed:%d %s__%d__\n", sqlite3_errcode(db), sqlite3_errmsg(db), __LINE__);
		return -1;
	}
	printf("创建stu2表格成功 __%d__\n", __LINE__);
	//单词查询记录表
	char sql2[128] = "create table if not exists stu(name char,word char,mean char,time char)";
	if (sqlite3_exec(db, sql2, NULL, NULL, &errmsg) != SQLITE_OK)
	{
		fprintf(stderr, "sqlite3_exec failed:%d %s__%d__\n", sqlite3_errcode(db), sqlite3_errmsg(db), __LINE__);
		return -1;
	}
	printf("创建stu表格成功 __%d__\n", __LINE__);

	struct sockaddr_in cin; //储存客户端地址信息
	socklen_t addrlen = sizeof(cin);
	//生成新的文件描述符与客户端通信
	int newfd = -1;
	pid_t cpid = 0;
	while (1)
	{

		int newfd = accept(sfd, (struct sockaddr *)&cin, &addrlen);
		if (newfd < 0)
		{
			ERR_MSG("accept");
			return -1;
		}
		printf("[%s,%d]newfd=%d 客户端链接成功 __%d__\n", inet_ntoa(cin.sin_addr), ntohs(cin.sin_port), newfd, __LINE__);
		cpid = fork();
		if (0 == cpid)
		{
			close(sfd);
			ser_recv(newfd, cin, db);
			close(newfd);
			exit(0);
		}
		else if (cpid > 0)
		{
			close(newfd);
		}
		else
		{
			ERR_MSG("fork");
			return -1;
		}
		if (signal(17, handler) == SIG_ERR)
		{
			ERR_MSG("signal");
			return -1;
		}
	}
	return 0;
}
int do_cli_msg(int newfd, struct sockaddr_in cin)
{
	char buf[128] = "";
	ssize_t res = 0;
	while (1)
	{
		bzero(buf, sizeof(buf));
		res = recv(newfd, buf, sizeof(buf), 0);
		if (res < 0)
		{
			ERR_MSG("recv");
			return -1;
		}
		else if (0 == res)
		{
			printf("[%s,%d]newfd=%d 客户端下线 __%d__\n", inet_ntoa(cin.sin_addr), ntohs(cin.sin_port), newfd, __LINE__);
			break;
		}
		printf("[%s,%d]newfd=%d :%s __%d__\n", inet_ntoa(cin.sin_addr), ntohs(cin.sin_port), newfd, buf, __LINE__);

		strcat(buf, "*_*");
		if (send(newfd, buf, sizeof(buf), 0) < 0)
		{
			ERR_MSG("send");
			return -1;
		}
		printf("send success __%d__\n", __LINE__);
	}
	return 0;
}
void handler(int sig)
{
	while (waitpid(-1, NULL, WNOHANG) > 0)
		;
	return;
}
int cli_net()
{
	//创建流式套接字
	int sfd = socket(AF_INET, SOCK_STREAM, 0);
	if (sfd < 0)
	{
		ERR_MSG("socket");
		return -1;
	}
	printf("socket success\n");
	//允许端口快速被覆盖重用。
	int reuse = 1;
	if (setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) < 0)
	{
		ERR_MSG("setsockopt");
		return -1;
	}
	printf("允许端口快速被覆盖重用成功\n");
	//填充地址信息结构体,真实的地址信息结构体AF_INEF:man 7 IP
	struct sockaddr_in sin;
	sin.sin_family = AF_INET;	//必须填AF_INEF
	sin.sin_port = htons(PORT); //端口号是网络字节序1024~49151
	sin.sin_addr.s_addr = inet_addr(IP);
	//绑定服务器的地址信息
	if (bind(sfd, (struct sockaddr *)&sin, sizeof(sin)) < 0)
	{
		ERR_MSG("bind");
		return -1;
	}
	printf("bind success __%d__\n", __LINE__);
	//将套接字设置为被动监听状态
	if (listen(sfd, 128) < 0)
	{
		ERR_MSG("listen");
		return -1;
	}
	printf("listen success __%d__\n", __LINE__);
	return sfd;
}
int ser_recv(int newfd, struct sockaddr_in cin, sqlite3 *db) //接收函数
{
	ssize_t res = 0;
	while (1)
	{
		res = recv(newfd, &sermsg, sizeof(sermsg), 0);
		struct msg sermsg1;
		strcpy(sermsg1.name, sermsg.name);
		if (res < 0)
		{
			return -1;
		}
		else if (0 == res)
		{
			fprintf(stderr, "[%s:%d]newfd = %d 客户端下线\n", inet_ntoa(cin.sin_addr), ntohs(cin.sin_port), newfd);
			return 0;
		}
		char choose = sermsg.type;
		switch (choose)
		{
		case '1': //注册
			do_register(newfd, db);
			break;
		case '2': //登录
			do_login(newfd, db);
			break;
		case '4': //查询
			do_find(newfd, db);
			break;
		case '5':
			do_history(newfd, db); //查询历史记录
			break;
		case '6':
			do_quit(newfd, db);
			break;
		}
	}
	return 0;
}
int do_register(int newfd, sqlite3 *db)
{

	char sql[128] = "";
	sprintf(sql, "insert into stu1 values (\"%s\",\"%s\")", sermsg.name, sermsg.password);
	char *errmsg = NULL;
	bzero(&sermsg.type, sizeof(sermsg.type));
	if (sqlite3_exec(db, sql, NULL, NULL, &errmsg) != SQLITE_OK)
	{
		sermsg.type = 'E';
		fprintf(stderr, "newfd = %d 注册失败\n", newfd);
		//将数据包发送给客户端
		if (send(newfd, &sermsg, sizeof(sermsg), 0) < 0)
		{
			ERR_MSG("send");
			return -1;
		}
		fprintf(stderr, "line:%d sqlite3_exec:%s\n", __LINE__, errmsg);
		return -1;
	}
	sermsg.type = 'S';
	fprintf(stdout, "newfd = %d 注册成功\n", newfd);
	//将数据包发送给客户端
	if (send(newfd, &sermsg, sizeof(sermsg), 0) < 0)
	{
		ERR_MSG("send");
		return -1;
	}
	return 0;
}
int callBack(void *arg, int column, char **column_text, char **column_name)
{
	if (strcmp(column_text[0], sermsg.name) == 0 && strcmp(column_text[1], sermsg.password) == 0)
	{
		sermsg.type = 'S';
		*(int *)arg = 1;
	}
	return 0;
}
int do_login(int newfd, sqlite3 *db) //登录函数
{
	char sql[128];
	char *errmsg = NULL;
	int flag = 0;
	sprintf(sql, "select * from stu1 where name=\"%s\";", sermsg.name);
	if (sqlite3_exec(db, sql, callBack, &flag, &errmsg) != SQLITE_OK)
	{
		fprintf(stderr, "line:%d sqlite3_exec:%s\n", __LINE__, errmsg);
		return -1;
	}
	if (flag == 0)
	{
		sermsg.type = 'E';
		fprintf(stderr, "newfd = %d 登录失败\n", newfd);
		//将数据包发送给客户端
		if (send(newfd, &sermsg, sizeof(sermsg), 0) < 0)
		{
			ERR_MSG("send");
			return -1;
		}
		return 0;
	}
	else if (flag == 1)
	{
		//将登陆信息保存到登录信息表中
		sprintf(sql, "insert into stu2 values(\"%s\");", sermsg.name);
		if (sqlite3_exec(db, sql, NULL, NULL, &errmsg) != SQLITE_OK)
		{
			sermsg.type = 'R';
			fprintf(stderr, "登录重复\n");
			bzero(sermsg.name, sizeof(sermsg.name));
			//将数据包发送给客户端
			if (send(newfd, &sermsg, sizeof(sermsg), 0) < 0)
			{
				ERR_MSG("send");
				return -1;
			}
			fprintf(stderr, "line:%d sqlite3_exec:%s\n", __LINE__, errmsg);
			return 0;
		}
	}
	fprintf(stdout, "%s 登录成功\n", sermsg.name);
	//设置为登录状态,登陆成功,将数据包发送给客户端
	if (send(newfd, &sermsg, sizeof(sermsg), 0) < 0)
	{
		ERR_MSG("send");
		return -1;
	}
	return 0;
}

int find_callBack(void *arg, int column, char **column_text, char **column_name)
{

	strcat(sermsg.mean, column_text[1]);
	strcat(sermsg.mean, "\n");
	sermsg.type = 'S';
	fprintf(stderr, "查询单词成功\n");
	(*(int *)arg)++;
	return 0;
}
int do_find(int newfd, sqlite3 *db) //查询单词
{

	bzero(sermsg.mean, sizeof(sermsg.mean));
	char sql[128];
	char *errmsg = NULL;
	int flag = 0;
	sprintf(sql, "select * from dict where word=\"%s\";", sermsg.word);
	if (sqlite3_exec(db, sql, find_callBack, &flag, &errmsg) != SQLITE_OK)
	{
		fprintf(stderr, "line:%d sqlite3_exec:%s\n", __LINE__, errmsg);
		return -1;
	}
	if (flag == 0)
	{
		sermsg.type = 'E';
		fprintf(stderr, "newfd = %d 查询失败\n", newfd);
		if (send(newfd, &sermsg, sizeof(sermsg), 0) < 0)
		{
			ERR_MSG("send");
			return -1;
		}
	}
	else
	{
		fprintf(stdout, "%s 查询成功\n", sermsg.word);
		if (send(newfd, &sermsg, sizeof(sermsg), 0) < 0)
		{
			ERR_MSG("send");
			return -1;
		}
	}
	time_t t;
	time(&t);
	struct tm *tm_t;
	tm_t = localtime(&t);
	sprintf(sermsg.time, "%d-%d-%d %d:%d:%d\n",tm_t->tm_year + 1900, tm_t->tm_mon + 1, tm_t->tm_mday, tm_t->tm_hour, tm_t->tm_min, tm_t->tm_sec);
	sprintf(sql, "insert into stu values (\"%s\",\"%s\",\"%s\",\"%s\")", sermsg.name, sermsg.word, sermsg.mean, sermsg.time);
	if (sqlite3_exec(db, sql, NULL, NULL, &errmsg) != SQLITE_OK)
	{
		fprintf(stderr, "sqlite3_exec failed:%d %s__%d__\n", sqlite3_errcode(db), sqlite3_errmsg(db), __LINE__);
		return -1;
	}
	return 0;
}
int do_history(int newfd, sqlite3 *db) //查看历史记录
{
	char sql[256] = "";
	sprintf(sql, "select *from stu where name=\"%s\";", sermsg.name);
	char **pres = NULL;
	int row, column;
	char *errmsg = NULL;
	if (sqlite3_get_table(db, sql, &pres, &row, &column, &errmsg) != SQLITE_OK)
	{
		fprintf(stderr, "line:%d sqlite3_get_table:%s\n", __LINE__, errmsg);
		return -1;
	}
	printf("row=%d column=%d\n", row, column);
	if (row == 0)
	{
		sermsg.type = 'F';
		if (send(newfd, &sermsg, sizeof(sermsg), 0) < 0)
		{
			ERR_MSG("send");
			return -1;
		}
		return 0;
	}
	bzero(sermsg.name, sizeof(sermsg.name));
	bzero(sermsg.word, sizeof(sermsg.word));
	bzero(sermsg.mean, sizeof(sermsg.mean));
	bzero(sermsg.time, sizeof(sermsg.time));
	int i = 0;
	for (i = 0; i < (row + 1) * column; i++)
	{
		if (i % column == column - 4)
			sprintf(sermsg.name, "%s", pres[i]);
		else if (i % column == column - 3)
			sprintf(sermsg.word, "%s", pres[i]);
		else if (i % column == column - 2)
			sprintf(sermsg.mean, "%s", pres[i]);
		else if (i % column == column - 1)
		{
			sprintf(sermsg.time, "%s", pres[i]);
			if (send(newfd, &sermsg, sizeof(sermsg), 0) < 0)
			{
				ERR_MSG("send");
				return -1;
			}
		}
	}
	if (i == (row + 1) * column)
	{
		printf("%s", sermsg.name);
		sermsg.type = 'S';
		if (send(newfd, &sermsg, sizeof(sermsg), 0) < 0)
		{
			ERR_MSG("send");
			return -1;
		}
	}
	//释放内存空间
	sqlite3_free_table(pres);
	return 0;
}
int do_quit(int newfd, sqlite3 *db)
{
	char sql[256];
	char *errmsg = NULL;
	sprintf(sql, "delete from stu2 where name=\"%s\";", sermsg.name);
	if (sqlite3_exec(db, sql, NULL, NULL, &errmsg) != SQLITE_OK)
	{
		fprintf(stderr, "line:%d sqlite3_exec:%s\n", __LINE__, errmsg);
		return 0;
	}
	printf("退出成功\n");
	return 0;
}

客户端头文件

#ifndef __H2_H__
#define __H2_H__
#include <stdio.h>
#include <sys/types.h>       
#include <sys/socket.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <stdlib.h>
#include <string.h>
 
#define PORT 6666              
#define IP "192.168.50.10" 
 
#define ERR_MSG(msg) do{\
	fprintf(stderr,"line:__%d__\n",__LINE__);\
	perror(msg);\
}while(0)
 
struct msg{
	char type;             //消息类型
	char name[128];    //名字
	char password[128];   //密码
	char word[128];       //单词
	char mean[128];      //翻译
	char time[128];      //时间  
}climsg;
 
void holp1(); //主菜单
void holp2();    //查询菜单
int cli_login(int cfd);   //登录
int cli_register(int cfd);    //注册
int cli_find(int cfd);       //查找单词
int cli_history(int cfd);     //查询历史记录	
int cli_quit(int cfd);   //退出函数
#endif

客户端代码

#include "h2.h"
int main(int argc, char const *argv[])
{
    //创建流式套接字
    int cfd = socket(AF_INET, SOCK_STREAM, 0);
    if (cfd < 0)
    {
        ERR_MSG("socket");
        return -1;
    }
    //填充服务器的地址信息,给connect使用
    struct sockaddr_in sin;
    sin.sin_family = AF_INET;   //必须填AF_INEF
    sin.sin_port = htons(6666); //端口号是网络字节序1024~49151
    sin.sin_addr.s_addr = inet_addr(IP);
    //链接服务器
    if (connect(cfd, (struct sockaddr *)&sin, sizeof(sin)) < 0)
    {
        ERR_MSG("connect");
        return -1;
    }
    char n;
    char a;
    while (1)
    {
AND:        
        holp1();
        printf("请输入指令:");
        scanf("%c", &n);
        while (getchar() != 10)
            ;
        switch (n)
        {
        case '1':
            cli_register(cfd);
            break;
        case '2':
            if (cli_login(cfd) == 0)
            {
                while (1)
                {
                    holp2();
                    printf("请输入指令:");
                    scanf("%c", &a);
                    while (getchar() != 10)
                        ;
                    switch (a)
                    {
                    case '4':
                        cli_find(cfd);
                        break;
                    case '5':
                        cli_history(cfd);
                        break;
                    case '6':
                        cli_quit(cfd);
                        goto AND;
                    default:
                        printf("输入错误,请重新输入\n");
                        break;
                    }
                }
            };
            break;
        case '3':
            goto END;
            break;
        default:
            printf("输入错误,请重新输入\n");
            break;
        }
    }
END:
    if (close(cfd) < 0)
    {
        ERR_MSG("close");
        return -1;
    }
    printf("退出词典\n");
    return 0;
}
void holp1()
{
    printf("****************************\n");
    printf("**********1.注册************\n");
    printf("**********2.登录************\n");
    printf("**********3.退出************\n");
    printf("****************************\n");
}
void holp2()
{
    printf("*******************************\n");
    printf("**********4.查询***************\n");
    printf("**********5.历史记录***********\n");
    printf("**********6.返回上一级菜单*****\n");
    printf("*******************************\n");
}
int cli_register(int cfd)
{
    climsg.type = '1';
    bzero(climsg.name, sizeof(climsg.name));
    bzero(climsg.password, sizeof(climsg.password));
    printf("请输入用户名:\n");
    fgets(climsg.name, sizeof(climsg.name), stdin);
    climsg.name[strlen(climsg.name) - 1] = 0;
    printf("请输入用户密码:\n");
    fgets(climsg.password, sizeof(climsg.password), stdin);
    climsg.password[strlen(climsg.password) - 1] = 0;
    //发送
    if (send(cfd, &climsg, sizeof(climsg), 0) < 0)
    {
        ERR_MSG("send");
        return -1;
    }
    ssize_t res = 0;
    bzero(&climsg.type, sizeof(climsg.type));
    res = recv(cfd, &climsg, sizeof(climsg), 0);
    if (res < 0)
    {
        ERR_MSG("recv");
        return -1;
    }
    printf("%c",climsg.type);
    if ('E' == climsg.type)
    {
        printf("注册失败\n");
    }
    else if ('S' == climsg.type)
    {
        printf("注册成功\n");
    }
}
int cli_login(int cfd)
{
    climsg.type = '2';
    bzero(climsg.name, sizeof(climsg.name));
    bzero(climsg.password, sizeof(climsg.password));
    printf("请输入用户名:\n");
    fgets(climsg.name, sizeof(climsg.name), stdin);
    climsg.name[strlen(climsg.name) - 1] = 0;
    printf("请输入用户密码:\n");
    fgets(climsg.password, sizeof(climsg.password), stdin);
    climsg.password[strlen(climsg.password) - 1] = 0;
    //发送
    if (send(cfd, &climsg, sizeof(climsg), 0) < 0)
    {
        ERR_MSG("send");
        return -1;
    }
    ssize_t res = 0;
    res = recv(cfd, &climsg, sizeof(climsg), 0);
    if (res < 0)
    {
        ERR_MSG("recv");
        return -1;
    }
    if ('E' == climsg.type)
    {
        printf("登录失败\n");
        return -1;
    }
    else if ('R' == climsg.type)
    {
        printf("登录重复\n");
        return -1;
    }
    else if ('S' == climsg.type)
    {
        printf("登录成功\n");
    }
    return 0;
}
int cli_find(int cfd)
{
    climsg.type = '4';
    bzero(climsg.word, sizeof(climsg.word));
    printf("请输入查询单词:\n");
    fgets(climsg.word, sizeof(climsg.word), stdin);
    climsg.word[strlen(climsg.word) - 1] = 0;
    //发送
    if (send(cfd, &climsg, sizeof(climsg), 0) < 0)
    {
        ERR_MSG("send");
        return -1;
    }
    ssize_t res = 0;
    bzero(climsg.mean, sizeof(climsg.mean));
    res = recv(cfd, &climsg, sizeof(climsg), 0);
    if (res < 0)
    {
        ERR_MSG("recv");
        return -1;
    }
    if ('E' == climsg.type)
    {
        printf("查询失败\n");
        return -1;
    }
    else if ('S' == climsg.type)
    {
        printf("查询成功\n");
        printf("%s\n", climsg.mean);
    }
    return 0;
}
int cli_history(int cfd)
{
    climsg.type = '5';
    if (send(cfd, &climsg, sizeof(climsg), 0) < 0)
    {
        ERR_MSG("send");
        return -1;
    }
    ssize_t res = 0;
    while (1)
    {
        res = recv(cfd, &climsg, sizeof(climsg), 0);
        if (res < 0)
        {
            ERR_MSG("recv");
            return -1;
        }
        if (climsg.type == 'S')
            break;
        if (climsg.type == 'F')
        {
            fprintf(stdout, "没有查询记录!\n");
            break;
        }
        printf("%s %s %s %s\n", climsg.name, climsg.word, climsg.mean,climsg.time);
    }
    return 0;
}
int cli_quit(int cfd)
{
    climsg.type = '6';
    if (send(cfd, &climsg, sizeof(climsg), 0) < 0)
    {
        ERR_MSG("send");
        return -1;
    }
    printf("返回上一级\n");
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值