在线英语词典

目录

要求

示例代码片段

Makefile

server

client


要求

1、开启服务器后,子客户端能通过相关通道进行单词的查询
2、服务器未开启时子客户端将无法查询
3、TCP通信
4、词库选择SQLite存储

示例代码片段

Makefile

all:
	gcc server.c -o server	
	gcc client.c -o client
.PHONY:clean
clean:
	rm -f server client

server

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

#define NAME_LEN 32
#define MSGTYPE_R 11                     // register
#define MSGTYPE_L 22                     // login
#define MSGTYPE_Q 33                     // query
#define MSGTYPE_H 44                     // history


#define DATABASE "my.db"
 

typedef struct {
	int type;
	char name[NAME_LEN];
	char data[256];
}MSG_T;

struct sockaddr_in
{
    uint8_t        sin_len;
    sa_family_t    sin_family;
    in_port_t      sin_port;
    struct in_addr sin_addr;
    char           sin_zero[SIN_ZERO_LEN];
    #define SIN_ZERO_LEN 8
};

// 注册
int do_register(int sockfd, MSG_T *msg, sqlite3 *db)
{
	char sql[128] = {0};
	char *errmsg = NULL;

	sprintf(sql, "insert into user values('%s', '%s');", msg->name, msg->data);
	if (sqlite3_exec(db, sql, NULL, NULL, &errmsg) != SQLITE_OK)
	{
		printf("%s\n", errmsg);
		memset(msg->data, 0, strlen(msg->data));
		sprintf(msg->data, "user(%s) already exist.", msg->name);
	}
	else
	{
		printf("client user(%s) register success..\n", msg->name);
		memset(msg->data, 0, strlen(msg->data));
		strcpy(msg->data, "register success..\n");
	}
 
	// 返回
	if (send(sockfd, msg, sizeof(MSG_T), 0) < 0)
	{
		printf("send error..\n");
		return 0;
	}
	return 1;
}
 
// 登录, 服务器返回 "OK"表示登录成功
int do_login(int sockfd, MSG_T *msg, sqlite3 *db)
{
	int nrow, ncolumn;
    char sql[128] = {0};
	char *errmsg = NULL, **result = NULL;

	sprintf(sql, "select * from user where name='%s' and passwd='%s';", msg->name, msg->data);
	if (sqlite3_get_table(db, sql, &result, &nrow, &ncolumn, &errmsg) != SQLITE_OK)
		printf("%s\n", errmsg);
	else
		printf("sqlite3 get table ok! \n");
 
	// 查询成功当数据库中拥有此用户时
	if (nrow == 1)
	{
		printf("client user(%s) login success\n", msg->name);
		memset(msg->data, 0, strlen(msg->data));
		strcpy(msg->data, "OK");
	}
	else
	{
		printf("client user(%s) login fail! \n", msg->name);
		memset(msg->data, 0, strlen(msg->data));
		strcpy(msg->data, "user or passwd wrong! \n");
	}
 
	// 返回
	if (send(sockfd, msg, sizeof(MSG_T), 0) < 0)
	{
		printf("fail to send\n");
		return 0;
	}
	
	return 1;
 
}

// 查找单词
int do_searchword(MSG_T *msg, char *word)
{
    FILE *fp = NULL;
    int word_len = strlen(word);
    char row_data[512] = {'\0'};
    int res = 0;
    char *p;                                        // 指向注释
    
    // 打开文件
    if ((fp = fopen("dict.txt", "r")) == NULL)
    {
        perror("fopen error..\n");
        return -1;
    }
 
    // 显示查询到的单词
    word_len = strlen(word);
    printf("%s, len = %d\n", word, word_len );
 
    // 读取文件、行数据(一行一行读取),对比要查询的单词
    // 如果成功,该函数返回相同的 str 参数。
    // 如果到达文件末尾或者没有读取到任何字符,str 的内容保持不变,并返回一个空指针。
    // 如果发生错误,返回一个空指针。
    while (fgets(row_data, 512, fp) != NULL)
    {
        res = strncmp(row_data, word, word_len);    // 每行对比前word_len个字节
 
        if (res != 0)
            continue;
 
        if (row_data[word_len] != ' ')              // 单词跟注释之间没有空格
            goto _end;
 
        // 找到了单词,跳过所有的空格
        p = row_data + word_len;
        while (*p == ' ')
        {
            p++;
        }
 
        strcpy(msg->data, p);
        fclose(fp);
        return 1;
    }
 
_end:
    fclose(fp);
    return 0;                                       // 未获取到单词
}
 
// 获取系统时间
void get_date(char *data)
{
    time_t rowtime;
    struct tm *info;
 
    // 时间格式转换
    info = localtime(&rowtime);
    sprintf(data, "%d-%d-%d %d:%d:%d", info->tm_year + 1900, info->tm_mon + 1, info->tm_mday,info->tm_hour, info->tm_min, info->tm_sec);
    printf("get date is : %s\n", data);                                   
}
 
// 单词查询
int do_query(int sockfd, MSG_T *msg, sqlite3 *db)
{
	char sql[128] = {0};
	char word[64] = {0};
	int found = 0;
    char date[128] = {0};
    char *errmsg;
    printf("\n");
    
    // 单词查找
	strcpy(word, msg->data);
	found = do_searchword(msg, word);
 
	if (found == 1)                                 // 插入到历史记录表
	{
	    get_date(date);                             // 获取系统时间
 
        //sprintf(sql, "insert into user values('%s', '%s');", msg->name, msg->data);
	    sprintf(sql, "insert into record values('%s', '%s', '%s')", msg->name, date, word);
	    if (sqlite3_exec(db, sql, NULL, NULL, &errmsg) != SQLITE_OK)
	    {
	        printf("%s\n", errmsg);
	        return -1;
	    }
	    else
	    {
	        printf("sqlite3 insert record done.\n");
	    }
	}
    else if (found == 0)//没有找到
    {
        memset(msg->data, 0, strlen(msg->data));
        strcpy(msg->data, "Not found!\n");
    }
    else if (found == -1)//dict.txt代开失败
    {
        memset(msg->data, 0, strlen(msg->data));
        strcpy(msg->data, "fail to open dict.txt.");
    }
 
	//将查询的结果发送给客户端
	send(sockfd, msg, sizeof(MSG_T), 0);
    
    return 0;
 
}
 
 
// 获取查询结果并将历史记录发送客户端
int history_callback(void* arg,int colCount,char** colValue,char** colName)
{
	int acceptfd;
	MSG_T msg;
 
	acceptfd = *((int *)arg);
	sprintf(msg.data, "%s , %s", colValue[1], colValue[2]);
	send(acceptfd, &msg, sizeof(MSG_T), 0);
 
	return 0;
}
 
// 历史记录查询
int do_history(int sockfd, MSG_T *msg, sqlite3 *db)
{
	char sql[128] = {0};
	char *errmsg;
 
	// 查询数据库
	// 会先执行*sql对应的功能命令,然后将结果传递给回调函数,回调函数根据结果再进一步执行
    sprintf(sql, "select * from record where name = '%s'", msg->name);
	if(sqlite3_exec(db, sql, history_callback,(void *)&sockfd, &errmsg)!= SQLITE_OK)
		printf("%s\n", errmsg);
	else
		printf("sqlite3 query record done.\n");
 
	// 给客户端回复完成发送消息
	msg->data[0] = '\0';
	send(sockfd, msg, sizeof(MSG_T), 0);
	
	return 0;
}
 
int do_client(int acceptfd, sqlite3 *db)
{
	MSG_T recv_msg;
 
	// 默认是阻塞式接收
	// 如果客户端断开连接或是主动发送close关闭连接 , recv会返回0
	while (recv(acceptfd, &recv_msg, sizeof(MSG_T), 0) > 0)
	{
		switch(recv_msg.type)
		{
			case MSGTYPE_R:
				do_register(acceptfd, &recv_msg, db);
				break;
 
			case MSGTYPE_L:
				do_login(acceptfd, &recv_msg, db);
				break;
 
			case MSGTYPE_Q:
				do_query(acceptfd, &recv_msg, db);
				break;
 
			case MSGTYPE_H:
				do_history(acceptfd, &recv_msg, db);
				break;
 
			default:
				printf("invalid data msg.\n");
		}
	}
 
	printf("client exit..\n");
	close(acceptfd);
	exit(0);
 
	return 0;
}


int main(int argc, const char *argv[])
{
	int sockfd, acceptfd;
	struct sockaddr_in server_addr;
	struct sockaddr_in client_addr;
	socklen_t addrlen = sizeof(client_addr);
	char ipv4_addr[16];
	sqlite3 *db = NULL;
	char *errmsg = NULL;
	char sql[128] = {0};
	pid_t pid;
	
	if (argc != 3)
	{
		printf("usage: %s serverip port\n", argv[0]);
		return -1;
	}
	
	if (sqlite3_open(DATABASE, &db) != SQLITE_OK)                  // 打开数据库
	{
		printf("%s\n", sqlite3_errmsg(db));
		return -1;
	}
	printf("sqlite3 open %s success.\n", DATABASE);
	sprintf(sql, "create table if not exists user(name text primary key, passwd text);");
	if (sqlite3_exec(db, sql, NULL, NULL, &errmsg) != SQLITE_OK)   // 创建用户表
	{
		printf("%s\n", errmsg);
		return -1;
	}
	memset(sql, 0, sizeof(sql));
	sprintf(sql, "create table if not exists record(name text, date text, word text);");
	if (sqlite3_exec(db, sql, NULL, NULL, &errmsg) != SQLITE_OK)//创建记录表
	{
		printf("%s\n", errmsg);
		return -1;
	}
	

	// 创建流式套接字
	if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
	{
		perror("socket create error..\n");
		return -1;
	}	
	
	server_addr.sin_family = AF_INET;
	server_addr.sin_addr.s_addr = inet_addr(argv[1]);
	server_addr.sin_port = htons(atoi(argv[2]));
    bzero(&(server_addr.sin_zero), sizeof(server_addr.sin_zero));
    
    // 绑定
    if (bind(sockfd, (struct sockaddr*)&server_addr, sizeof(struct sockaddr)) < 0)
	{
		perror("bind error..\n");
		return -1;
	}
	
	// 监听
	if (listen(sockfd, 5) < 0)
	{
		perror("listen error..\n");
		exit(1);
	}
	printf("listen success..\n");
	
	// 僵尸进程处理
	signal(SIGCHLD, SIG_IGN);
	
	while (1)
	{
		if ((acceptfd = accept(sockfd, (struct sockaddr *)&client_addr, &addrlen)) < 0)
		{
			perror("acceptfd error..\n");
			return -1;
		}
		if (inet_ntop(AF_INET, &client_addr.sin_addr, ipv4_addr, addrlen) < 0)
		{
			perror("inet_ntop error..\n");
			return -1;
		}
		printf("client(%s:%d) is connected..\n", ipv4_addr, htons(client_addr.sin_port));
 
		// 创建子进程
		if ((pid = fork()) < 0)
		{
			perror("fork create error..\n");
			return -1;
		}
        printf("fork create success..\n");
		else if (pid == 0)                         // 子进程处理客户端消息
		{
			close(sockfd);
			do_client(acceptfd, db);
		}
		else                                       // 父进程接收客户端连接请求
			close(acceptfd);
	}
	
	return 0;
}

client

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
 
 
#define NAME_LEN 32


#define MSGTYPE_R 11                            // register
#define MSGTYPE_L 22                            // login
#define MSGTYPE_Q 33                            // query
#define MSGTYPE_H 44                            // history


typedef struct {
	int type;
	char name[NAME_LEN];
	char data[256];
}MSG_T;
 
/*
struct sockaddr
{
    uint8_t        sa_len;
    sa_family_t    sa_family;
    char           sa_data[14];
};
/* members are in network byte order */
/*
struct sockaddr_in
{
    uint8_t        sin_len;
    sa_family_t    sin_family;
    in_port_t      sin_port;
    struct in_addr sin_addr;
    char            sin_zero[SIN_ZERO_LEN];
    #define SIN_ZERO_LEN 8
};
struct in_addr
{
    in_addr_t s_addr;
};
*/
 
// 注册
int do_register(int sockfd, MSG_T *msg)
{
	printf("register..\n");
	
	memset(msg, 0, sizeof(MSG_T));
	msg->type = MSGTYPE_R;
	printf("input name:"); scanf("%s", msg->name); getchar();
	printf("input passwd:"); scanf("%s", msg->data); getchar();
	
	if (send(sockfd, msg, sizeof(MSG_T), 0) < 0)
	{
		printf("fail to send register msg.\n");
		return -1;
	}
	
	if (recv(sockfd, msg, sizeof(MSG_T), 0) < 0)
	{
		printf("register error..\n");
		return -1;
	}
	
	printf("%s\n", msg->data);
	
	return 1;
}
 
// 服务器返回 "OK" 表示登录成功
int do_login(int sockfd, MSG_T *msg)
{
	printf("login..\n");
	
	memset(msg, 0, sizeof(MSG_T));
	msg->type = MSGTYPE_L;
	printf("input name:"); scanf("%s", msg->name); getchar();
	printf("input passwd:"); scanf("%s", msg->data); getchar();
	
	if (send(sockfd, msg, sizeof(MSG_T), 0) < 0)
	{
		printf("send register msg error..\n");
		return -1;
	}
	
	if (recv(sockfd, msg, sizeof(MSG_T), 0) < 0)
	{
		printf("login error..\n");
		return -1;
	}
	
	if (strncmp(msg->data, "OK", 3) == 0)
	{
		printf("login success..\n");
		return 1;
	}
	else
		printf("%s\n", msg->data);
	
	return 0;
}
 
// 单词查询
int do_query(int sockfd, MSG_T *msg)
{
	printf("query..\n");
	
	msg->type = MSGTYPE_Q;
	while(1)
	{
		printf("input word:"); scanf("%s", msg->data); getchar();
		
		// 输入"#"退出本次查询
		if (strncmp(msg->data, "#", 1) == 0)
			break;
		
		// 将查询单词发送给服务器
		if (send(sockfd, msg, sizeof(MSG_T), 0) < 0)
		{
			printf("send error..\n");
			return -1;
		}
		
		// 将查询到的结果返回
		if (recv(sockfd, msg, sizeof(MSG_T), 0) < 0)
		{
			printf("recv error..\n");
			return -1;
		}
		printf("%s\n", msg->data);
	}
	
	return 1;
}
 
// 历史记录查询
int do_history(int sockfd, MSG_T *msg)
{
	printf("history..\n");
	
	msg->type = MSGTYPE_H;
 
	// 消息发送至服务器
	if (send(sockfd, msg, sizeof(MSG_T), 0) < 0)
	{
		printf("send error..\n");
		return -1;
	}
    printf("send success..\n");
		
	while(1)
	{
		// 将查询到的结果返回
		if (recv(sockfd, msg, sizeof(MSG_T), 0) < 0)
		{
			printf("recv error..\n");
			return -1;
		}
 
		if (msg->data[0] == '\0')
		    break;
 
		// 回显历史记录    
		printf("%s\n", msg->data);
	}
	
	return 1;
 
}
 
int main(int argc, const char *argv[])
{
	int sockfd;
	struct sockaddr_in server_addr;
	int input_nbr;
	MSG_T send_msg;
	
	if (argc != 3)
	{
		printf("usage: %s serverip port\n", argv[0]);
		return -1;
	}
	
	// 创建流式套接字
	if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
	{
		perror("socket create error..\n");
		return -1;
	}	
	
	server_addr.sin_family  = AF_INET;
	server_addr.sin_addr.s_addr = inet_addr(argv[1]);
	server_addr.sin_port = htons(atoi(argv[2]));
    bzero(&(server_addr.sin_zero), sizeof(server_addr.sin_zero));    // 清零函数
    
    // 连接
    if (connect(sockfd, (struct sockaddr*)&server_addr, sizeof(struct sockaddr)) < 0)
	{
		perror("fail to connect\n");
		return -1;
	}
	
    // 阻塞等待
	while (1)
	{
		printf("***********************************************\n");
		printf("*******  1.register   2.login   3.quit  *******\n");
		printf("***********************************************\n");
		printf("please choose:");
		
		scanf("%d", &input_nbr);
		getchar();
		
		switch (input_nbr)
		{
			case 1:
				do_register(sockfd, &send_msg);
				break;
				
			case 2:
				if (do_login(sockfd, &send_msg) == 1)
					goto _login;
				break;
				
			case 3:
				close(sockfd);
				exit(0);
				break;
			
			default:
				printf("Invalid data cmd..\n");
				break;
		}
	}

_login:
	while(1)
	{
		printf("***********************************************\n");
		printf("*****1.query_word  2.history_record  3.quit****\n");
		printf("***********************************************\n");
		printf("please choose:");
		
		input_nbr = 0;
		scanf("%d", &input_nbr);
		getchar();
		
		switch (input_nbr)
		{
			case 1:
				do_query(sockfd, &send_msg);
				break;
				
			case 2:
				do_history(sockfd, &send_msg);
				break;
				
			case 3:
				close(sockfd);
				exit(0);
				break;
			
			default:
				printf("Invalid data cmd. \n");
				break;
		}
	}
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值