项目:电子词典

该系统包括服务器和客户端两部分,通过TCP/IP进行通信。服务器端负责数据库操作,如创建、删除、查询单词,用户注册、登录、查询历史记录等功能。客户端则提供用户交互界面,支持注册、登录、查询单词和查看历史记录。系统使用SQLite数据库存储用户信息和单词数据,并处理TCP粘包问题。

项目要求

 服务器:

        所需头文件:linklist.h   SerOperation.h

        所需源文件:linklist.c   SerOperation.c

        主函数源文件:Ser.c

        linklist.h:

#ifndef __LINKLIST_H__
#define __LINKLIST_H__

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#define N 128

typedef struct Node{
	char msg[N];
	struct Node *next;
}linkList;
 
linkList *list_creat();
 
linkList *node_buy(char *str);
 
int list_empty(linkList *h);
 
int list_insert_head(linkList *h,char *str);
 
int list_delete(linkList *h,char *str);

int list_delete_head(linkList *h);
 
int list_destroy(linkList *h);
 
#endif

        SerOperation.h:

#ifndef __SEROPERATION_H__
#define __SEROPERATION_H__

#include <stdio.h>
#include <sqlite3.h>
#include <string.h>
#include "./linklist.h"
#include <time.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <sys/ipc.h>
#include <sys/shm.h>

#define ERR_MSG(msg1,msg2) do{\
	fprintf(stderr,"__%s__ __%d__\n",__func__,__LINE__);\
	fprintf(stderr,"%s : %s\n",msg1,msg2);\
}while(0)

//创建数据库
sqlite3 *create_database();

//删除表
int drop_table();

//关闭数据库
int close_database(sqlite3 *db);

//单词导入数据库--数据库操作
int import_words(sqlite3 *db);

//用户注册--数据库操作
int user_register(sqlite3 *db,char *username,char *password,char *dest);

//用户登录--数据库操作
int user_login(sqlite3 *db,char *username,char *password,char *dest);

//查询单词--数据库操作
linkList *select_word(sqlite3 *db,char *word,char *username);

//记录历史查询信息--数据库操作
int insert_history(sqlite3 *db,char *word,char *username,linkList *head);

//查看历史查询信息--数据库操作
linkList *select_history(sqlite3 *db,char *username);

//处理接收到的客户端消息
int deal_rcvmsg(char *rcvmsg);

//处理客户端的注册请求
int do_register(int newfd,sqlite3 *db,char *rcvmsg);

//修改用户状态为在线
int update_state_online(sqlite3 *db,char *username);

//修改用户状态为离线
int update_state_offline(sqlite3 *db,char *username);

//处理客户端的登录请求
int do_login(int newfd,sqlite3 *db,char *rcvmsg);

//处理客户端的查询单词请求
int do_select_world(int newfd,sqlite3 *db,char *username,char *rcvmsg);

//处理客户端的查看历史信息请求
int do_select_history(int newfd,sqlite3 *db,char *username);

//减少TCP的粘包问题
int writen(int fd,char *msg,int size);
int mysendMsg(int fd,char *msg,int size);
int readn(int fd,char *msg,int size);
int myrecvMsg(int fd,char **msg);

#endif

        linklist.c:

#include "linklist.h"

//头节点的创建
linkList *list_creat()
{
	linkList *h=(linkList *)malloc(sizeof(linkList));
	if(NULL==h){
		return NULL;
	}
	h->next=NULL;
	return h;
}

//申请节点
linkList *node_buy(char *str)
{
	linkList *p=(linkList *)malloc(sizeof(linkList));
	if(NULL==p){
		return NULL;
	}
	strcpy(p->msg,str);
	p->next=NULL;
	return p;
}

//判空
int list_empty(linkList *h)
{
	if(NULL==h){
		return -1;
	}
	return h->next==NULL ? 1 : 0;
}

//头插法
int list_insert_head(linkList *h,char *str)
{
	if(NULL==h){
		return -1;
	}
	linkList *l=node_buy(str);
	if(NULL==l){
		return -2;
	}
	l->next=h->next;
	h->next=l;
	return 0;
}

//删除目标
int list_delete(linkList *h,char *str)
{
	if(NULL==h){
		return -1;
	}
	if(list_empty(h)){
		return -2;
	}

	linkList *p=h->next;
	while(p->next!=NULL){
		if(strcmp(p->msg,str)==0)
			break;
		else
			p=p->next;
	}
	while(h->next!=p){
		h=h->next;
	}
	h->next=p->next;
	free(p);
	return 0;
}

//头删
int list_delete_head(linkList *h)
{
	if(NULL==h){
		return -1;
	}
	if(!list_empty(h)){
		linkList *p=h->next;
		h->next=p->next;
		free(p);
	}else{
		return -1;
	}
	return 0;
}

//销毁链表
int list_destroy(linkList *h)
{
	while(list_delete_head(h)==0);
	free(h);
	h=NULL;
	return 0;
}

        SerOperation.c:

#include "./SerOperation.h"

//创建数据库
sqlite3 *create_database()
{
	sqlite3 *db=NULL;
	char *msg=NULL;
	char buf[128]="";
	if(sqlite3_open("./SerDB.db",&db)!=SQLITE_OK){
		ERR_MSG("create database fail",sqlite3_errmsg(db));
		return NULL;
	}
	
	//创建存储用户信息表格及在线状态
	bzero(buf,sizeof(buf));
	sprintf(buf,"create table if not exists usermsg (username char primary key,password char,state char)");
	if(sqlite3_exec(db,buf,NULL,NULL,&msg)!=SQLITE_OK){
		ERR_MSG("create usermsg table fail",msg);
		return NULL;
	}

	//创建四级单词存储表格
	bzero(buf,sizeof(buf));
	sprintf(buf,"create table if not exists wordsmsg (word char,annotation char)");
	if(sqlite3_exec(db,buf,NULL,NULL,&msg)!=SQLITE_OK){
		ERR_MSG("create wordsmsg table fail",msg);
		return NULL;
	}

	fprintf(stdout,"create database success\n");

	return db;
}

//删除单词的表,防止服务器重启时重复导入单词
int drop_table()
{
	sqlite3 *db=NULL;
	char *msg=NULL;
	char buf[128]="";
	if(sqlite3_open("./SerDB.db",&db)!=SQLITE_OK){
		ERR_MSG("open database fail",sqlite3_errmsg(db));
		return -1;
	}

	bzero(buf,sizeof(buf));
	sprintf(buf,"drop table wordsmsg");
	if(sqlite3_exec(db,buf,NULL,NULL,&msg)!=SQLITE_OK){
		ERR_MSG("drop table fail",msg);
		return -1;
	}

	close_database(db);
	db=NULL;

	return 0;
}

//关闭数据库
int close_database(sqlite3 *db)
{
	if(sqlite3_close(db)!=SQLITE_OK){
		ERR_MSG("close database fail",sqlite3_errmsg(db));
		return -1;
	}
	fprintf(stdout,"close database success\n");
	return 0;
}

//单词导入数据库--数据库操作
int import_words(sqlite3 *db)
{
	char *msg=NULL;
	char *ch;
	int i=0;
	char buf[128]="";
	char str[64]="";
	char word[32]="";
	char mean[32]="";
	FILE *fd=fopen("./dict.txt","r");
	if(fd==NULL){
		perror("fopen fail");
		return -1;
	}

	while(1){
		bzero(word,sizeof(word));
		bzero(mean,sizeof(mean));
		bzero(str,sizeof(str));
		i=0;
		if(fgets(str,sizeof(str),fd)==NULL){
			break;
		}
		str[strlen(str)-1]='\0';
		ch=str+i;
		while(*ch!='\0'){
			if(*ch==' ' && *(ch+1)==' '){
				break;
			}
			i++;
			ch=str+i;
		}
		str[i]='\0';
		strcpy(word,str);
		strcpy(mean,str+i+3);
		sprintf(buf,"insert into wordsmsg values (\"%s\",\"%s\")",word,mean);
		if(sqlite3_exec(db,buf,NULL,NULL,&msg)!=SQLITE_OK){
			ERR_MSG("import word fail",msg);
			return -1;
		}
	}
	fclose(fd);
	fprintf(stdout,"import words success\n");
}

//用户注册--数据库操作
int user_register(sqlite3 *db,char *username,char *password,char *dest)
{
	char *msg=NULL;
	char buf[128]="";
	char src[64]="";
	char state[]="offline";
	int len=0;
	sprintf(buf,"insert into usermsg values (\"%s\",\"%s\",\"%s\")",username,password,state);
	if(sqlite3_exec(db,buf,NULL,NULL,&msg)!=SQLITE_OK){
		sprintf(src,"register fail : username is exists!");
		len=strlen(src);
		strcpy(dest,src);
		return len;
	}
	sprintf(src,"register success!");
	len=strlen(src);
	strcpy(dest,src);
	return len;
}

//用户登录--数据库操作
int user_login(sqlite3 *db,char *username,char *password,char *dest)
{
	char buf[128]="";
	char src[64]="";
	char *msg=NULL;
	char **restr=NULL;
	int len=0;
	int row=0;
	int column=0;
	sprintf(buf,"select * from usermsg where username=\"%s\"",username);
	if(sqlite3_get_table(db,buf,&restr,&row,&column,&msg)!=SQLITE_OK){
		ERR_MSG("get usermsg fail",msg);
		return -1;
	}
	if(row==0){
		sprintf(src,"login fail : username inexistence!");
		len=strlen(src);
		strcpy(dest,src);
	}else{
		if(strcmp(restr[4],password)==0){
			if(strcmp(restr[5],"offline")==0){
				sprintf(src,"login success!");
				len=strlen(src);
				strcpy(dest,src);
				bzero(buf,sizeof(buf));
				//登录成功,创建一个以用户名命名的表,用来存储用户历史查询信息
				sprintf(buf,"create table if not exists %s (word char,annotation char,time char)",username);
				if(sqlite3_exec(db,buf,NULL,NULL,&msg)!=SQLITE_OK){
					ERR_MSG("create historymsg table fail",msg);
					return -1;
				}
				//设置为在线状态
				update_state_online(db,username);
			}else{
				sprintf(src,"The user is online!");
				len=strlen(src);
				strcpy(dest,src);
			}
		}else{
			sprintf(src,"login fail : password error!");
			len=strlen(src);
			strcpy(dest,src);
		}
	}
	sqlite3_free_table(restr);

	return len;
}

//查询单词,用链表存储查询到的单词信息,防止一词多义
linkList *select_word(sqlite3 *db,char *word,char *username)
{
	char buf[128]="";
	char *msg=NULL;
	char **restr=NULL;
	int row=0;
	int column=0;
	linkList *head=list_creat();
	sprintf(buf,"select annotation from wordsmsg where word=\"%s\"",word);
	if(sqlite3_get_table(db,buf,&restr,&row,&column,&msg)!=SQLITE_OK){
		ERR_MSG("get words fail",msg);
		return NULL;
	}
	if(row==0){
		list_insert_head(head,"Not found!");	
	}else{
		for(int i=row;i>0;i--){
		list_insert_head(head,restr[i]);
		}
	}

	//记录历史信息
	insert_history(db,word,username,head);
	sqlite3_free_table(restr);

	return head;
}

//记录历史信息--数据库操作
int insert_history(sqlite3 *db,char *word,char *username,linkList *head)
{
	char buf[256]="";
	char tminfo[64]="";
	char *msg=NULL;
	time_t tocl=time(NULL);
	struct tm *info=localtime(&tocl);
	sprintf(tminfo,"%d-%02d-%02d %02d:%02d:%02d",\
			info->tm_year+1900,info->tm_mon+1,info->tm_mday,\
			info->tm_hour,info->tm_min,info->tm_sec);
	while(head->next!=NULL){
		head=head->next;
		sprintf(buf,"insert into %s values (\"%s\",\"%s\",\"%s\")",\
				username,word,head->msg,tminfo);
		if(sqlite3_exec(db,buf,NULL,NULL,&msg)!=SQLITE_OK){
			ERR_MSG("insert historymsg fail",msg);
			return -1;
		}
	}
	return 0;
}

//查看历史查询信息,用链表存储查询到的历史信息
linkList *select_history(sqlite3 *db,char *username)
{
	char buf[128]="";
	char *msg=NULL;
	char **restr=NULL;
	int row=0;
	int column=0;
	linkList *head=list_creat();
	sprintf(buf,"select * from %s",username);
	if(sqlite3_get_table(db,buf,&restr,&row,&column,&msg)!=SQLITE_OK){
		ERR_MSG("get historymsg fail",msg);
		return NULL;
	}
	if(row==0){
		list_insert_head(head,"当前历史信息为空!");	
	}else{
		for(int i=row*column-1;i>=0;i--){
			list_insert_head(head,restr[i+3]);
		}
	}

	sqlite3_free_table(restr);

	return head;
}

//处理接收到的客户端消息
int deal_rcvmsg(char *rcvmsg)
{
	int i=0;
	char *ch=rcvmsg+i;
	while(1){
		if(*ch=='#' && *(ch+1)=='#'){
			return i;
		}
		i++;
		ch=rcvmsg+i;
	}
	return 0;
}

//处理客户端的注册请求
int do_register(int newfd,sqlite3 *db,char *rcvmsg)
{
	int index=0;
	int size=0;
	char sndmsg[256]="";
	char username[64]="";
	char password[64]="";
	index=deal_rcvmsg(rcvmsg);
	rcvmsg[index]='\0';
	strcpy(username,rcvmsg+1);
	strcpy(password,rcvmsg+index+2);
	size=user_register(db,username,password,sndmsg);
	mysendMsg(newfd,sndmsg,size);
	return 0;
}

//修改用户状态为在线
int update_state_online(sqlite3 *db,char *username)
{
	char buf[128]="";
	char *msg=NULL;
	sprintf(buf,"update usermsg set state='online' where username=\"%s\"",username);
	if(sqlite3_exec(db,buf,NULL,NULL,&msg)!=SQLITE_OK){
		ERR_MSG("update state fail",msg);
		return -1;
	}

	return 0;
}

//修改用户状态为离线
int update_state_offline(sqlite3 *db,char *username)
{
	char buf[128]="";
	char *msg=NULL;
	sprintf(buf,"update usermsg set state='offline' where username=\"%s\"",username);
	if(sqlite3_exec(db,buf,NULL,NULL,&msg)!=SQLITE_OK){
		ERR_MSG("update state fail",msg);
		return -1;
	}

	return 0;
}

//处理客户端的登录请求
int do_login(int newfd,sqlite3 *db,char *rcvmsg)
{
	int size=0;
	int index=0;
	char sndmsg[256]="";
	char username[64]="";
	char password[64]="";
	index=deal_rcvmsg(rcvmsg);
	rcvmsg[index]='\0';
	strcpy(username,rcvmsg+1);
	strcpy(password,rcvmsg+index+2);
	size=user_login(db,username,password,sndmsg);
	mysendMsg(newfd,sndmsg,size);
	return 0;
}

//处理客户端的查询单词请求
int do_select_world(int newfd,sqlite3 *db,char *username,char *rcvmsg)
{
	int size=0;
	char sndmsg[256]="";
	linkList *p;
	p=select_word(db,rcvmsg+1,username);
	while(p->next!=NULL){
		p=p->next;
		size=strlen(p->msg);
		mysendMsg(newfd,p->msg,size);
	}
	strcpy(sndmsg,"break");
	size=strlen(sndmsg);
	mysendMsg(newfd,sndmsg,size);
	list_destroy(p);
	p=NULL;
	return 0;
}

//处理客户端的查看历史信息请求
int do_select_history(int newfd,sqlite3 *db,char *username)
{
	int size=0;
	char sndmsg[256]="";
	linkList *p;
	p=select_history(db,username);
	while(p->next!=NULL){
		p=p->next;
		size=strlen(p->msg);
		mysendMsg(newfd,p->msg,size);
	}
	strcpy(sndmsg,"break");
	size=strlen(sndmsg);
	mysendMsg(newfd,sndmsg,size);
	list_destroy(p);
	p=NULL;
	return 0;
}

//以下功能代码,主要是为了减少TCP的粘包问题

//发送指定大小数据
int writen(int fd,char *msg,int size)
{
	char *buf=msg;
	int count=size;
	int len=0;
	while(count>0){
		len=send(fd,buf,count,0);
		if(len<0){
			perror("send");
			return -1;
		}else if(0==len){
			return size-count;
		}
		buf+=len;
		count-=len;
	}
	return size;
}

//发送数据
int mysendMsg(int fd,char *msg,int size)
{
	if(fd<0 || msg==NULL || size<=0){
		return -1;
	}
	char *buf=(char *)malloc(size+4);
	int len=htonl(size);
	memcpy(buf,(char *)&len,4);
	memcpy(buf+4,msg,size);
	int res=writen(fd,buf,size+4);
	if(res<size+4){
		free(buf);
		fprintf(stderr,"发送失败\n");
		return -1;
	}
	free(buf);
	return size;
}

//接受指定大小数据
int readn(int fd,char *msg,int size)
{
	char *buf=msg;
	int count=size;
	int len=0;
	while(count>0){
		len=recv(fd,buf,count,0);
		if(len<-1){
			perror("recv");
			return -1;
		}else if(0==len){
			return 0;
		}
		buf+=len;
		count-=len;
	}
	return size;
}

//接受数据
int myrecvMsg(int fd,char **msg)
{
	int len;
	int res;
	res=readn(fd,(char *)&len,4);
	if(res==0){
		return 0;
	}
	len=ntohl(len);
	char *buf=(char *)malloc(len+1);
	res=readn(fd,buf,len);
	if(res<len){
		fprintf(stderr,"接收失败\n");
		free(buf);
		return -1;
	}
	buf[len]='\0';
	*msg=buf;
	return len;
}

        Ser.c:

#include <stdio.h>	
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdlib.h>
#include <signal.h>
#include <sys/wait.h>
#include <string.h>
#include "SerOperation.h"

typedef void (*sighandler_t)(int);

//僵尸进程处理函数
void handler(int sig)
{
	while(waitpid(-1,NULL,WNOHANG)>0);
}

int main(int argc, const char *argv[])
{
	if(argc<3){
		fprintf(stderr,"传参不足 %s ip port\n",argv[0]);
		return -1;
	}

	//创建流式套接字
	int sfd=socket(AF_INET,SOCK_STREAM,0);
	if(sfd<0){
		perror("socket fail");
		return -1;
	}
	
	//允许端口快速重用
	int reuse=1;
	if(setsockopt(sfd,SOL_SOCKET,SO_REUSEADDR,&reuse,sizeof(reuse))<0){
		perror("setsockopt");
		return -1;
	}

	//填充服务器地址信息结构体
	struct sockaddr_in sin;
	sin.sin_family=AF_INET;
	sin.sin_port=htons(atoi(argv[2]));
	sin.sin_addr.s_addr=inet_addr(argv[1]);

	//绑定ip和端口
	if(bind(sfd,(struct sockaddr *)&sin,sizeof(sin))<0){
		perror("bind fail");
		return -1;
	}

	//设置为被动监听状态
	if(listen(sfd,10)<0){
		perror("listen fail");
		return -1;
	}

	//信号处理僵尸进程
	if(signal(SIGCHLD,handler)==SIG_ERR){
		perror("signal fail");
		return -1;
	}

	//储存客户端地址信息结构体
	struct sockaddr_in cin;
	socklen_t addrlen=sizeof(cin);

	//创建数据库,并将单词导入数据库
	//drop_table();
	sqlite3 *db=create_database();
	//import_words(db);

	int newfd;
	char username[64]="";    //用来保存登录的用户名
	char *rcvmsg=NULL;

	//循环等待客户端连接
	while(1){
		newfd=accept(sfd,(struct sockaddr *)&cin,&addrlen);
		if(newfd<0){
			perror("accept fail");
			return -1;
		}

		printf("[ %s : %d ]connect\n",inet_ntoa(cin.sin_addr),ntohs(cin.sin_port));

		//如果有客户端连接就去创建一个子进程与客户端通信
		//父进程继续等待其他客户端连接
		pid_t pid=fork();
		if(pid>0){
			close(newfd);
		}else if(0==pid){
			close(sfd);
			while(1){
				rcvmsg=NULL;
				if(0==myrecvMsg(newfd,&rcvmsg)){
					//用户掉线,用户状态设置为离线状态
					update_state_offline(db,username);
					printf("[ %s : %d ]quit\n",inet_ntoa(cin.sin_addr),ntohs(cin.sin_port));
					break;
				}
				switch(rcvmsg[0]){
				case 'R':
					do_register(newfd,db,rcvmsg);
					free(rcvmsg);
					break;
				case 'L':
					bzero(username,sizeof(username));
					do_login(newfd,db,rcvmsg);
					strcpy(username,rcvmsg+1);
					free(rcvmsg);
					break;
				case 'F':
					do_select_world(newfd,db,username,rcvmsg);
					free(rcvmsg);
					break;
				case 'H':
					do_select_history(newfd,db,username);
					free(rcvmsg);
					break;
				case 'Q':
					update_state_offline(db,username);
					free(rcvmsg);
					break;
				default:
					continue;
				}
			}
			close(newfd);
			exit(0);
		}else{
			perror("fork fail");
			return -1;
		}
	}

	//关闭套接字
	close(sfd);
	//关闭数据库
	close_database(db);
	db=NULL;
	
	return 0;
}

客户端:

        所需头文件:CliOperation.h

        所需源文件:CliOperation.c

        主函数源文件:Cli.c

        CliOperation.h:

#ifndef __CLIOPERATION_H__
#define __CLIOPERATION_H__

#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <arpa/inet.h>

//主菜单
void pri_menu();

//功能菜单
void fun_menu(int cfd);

int Register(int cfd);

int Login(int cfd);

int Quit(int cfd);

int SelectWord(int cfd);

int SelectHistory(int cfd);

int writen(int fd,char *msg,int size);
int mysendMsg(int fd,char *msg,int size);
int readn(int fd,char *msg,int size);
int myrecvMsg(int fd,char **msg);

#endif

        CliOperation.c:

#include "./CliOperation.h"

void pri_menu()
{
    printf("\t************************\n");
    printf("\t*******电 子 词 典******\n");
    printf("\t********1> 注 册********\n");
    printf("\t********2> 登 录********\n");
    printf("\t********3> 退 出********\n");
    printf("\t************************\n");
}

void fun_menu(int cfd)
{
	char choose;
	while(1){
		system("clear");
		printf("\t***************************\n");
		printf("\t********用 户 功 能********\n");
		printf("\t********1> 查询单词********\n");
		printf("\t********2> 查看历史记录****\n");
		printf("\t********3> 放回上一级******\n");
		printf("\t***************************\n");
		fprintf(stdout,"请输入你的选择>>>");
		choose=getchar();
		while(getchar()!='\n');
		switch(choose){
			case '1':
				SelectWord(cfd);
				break;
			case '2':
				SelectHistory(cfd);
				break;
			case '3':
				Quit(cfd);
				return ;
			default:
				fprintf(stderr,"input choose error,try again\n");
				break;
		}
		fprintf(stdout,"请输入任意字符清屏>>>");
		fflush(stdout);
		while(getchar()!='\n');
	}
}

//用户注册
int Register(int cfd)
{
	char sndmsg[256]="";
	char *rcvmsg=NULL;
	char username[64]="";
	char password[64]="";
	int size=0;
	//输入用户名
	fprintf(stdout,"请输入用户名>>>");
	fgets(username,sizeof(username),stdin);
	username[strlen(username)-1]=0;
	size+=strlen(username);
	
	//输入密码
	fprintf(stdout,"请输入密码>>>");
	fgets(password,sizeof(password),stdin);
	password[strlen(password)-1]=0;
	size+=strlen(password);
	
	//发送用户名和密码给服务器
	sndmsg[0]='R';
	sprintf(sndmsg+1,"%s##%s",username,password);
	mysendMsg(cfd,sndmsg,size+3);
	
	//接收结果信息
	myrecvMsg(cfd,&rcvmsg);
	
	//处理信息
	fprintf(stdout,"%s\n",rcvmsg);
	free(rcvmsg);

	return 0;
}

//用户登录
int Login(int cfd)
{
	char sndmsg[256]="";
	char *rcvmsg=NULL;;
	char username[64]="";
	char password[64]="";
	int size=0;
	//输入用户名
	fprintf(stdout,"请输入用户名>>>");
	fgets(username,sizeof(username),stdin);
	username[strlen(username)-1]=0;
	size+=strlen(username);
	
	//输入密码
	fprintf(stdout,"请输入密码>>>");
	fgets(password,sizeof(password),stdin);
	password[strlen(password)-1]=0;
	size+=strlen(password);
	
	//发送用户名和密码给服务器
	sndmsg[0]='L';
	sprintf(sndmsg+1,"%s##%s",username,password);
	mysendMsg(cfd,sndmsg,size+3);
	
	//接收结果信息
	myrecvMsg(cfd,&rcvmsg);
	
	//处理信息
	fprintf(stdout,"%s\n",rcvmsg);
	if(strcmp(rcvmsg,"login success!")==0){
		return 1;
	}
	free(rcvmsg);
	return 0;
}

//用户退出
int Quit(int cfd)
{
	char sndmsg='Q';
	mysendMsg(cfd,&sndmsg,1);
	
	return 0;
}

//查询单词
int SelectWord(int cfd)
{
	char sndmsg[256];
	char *rcvmsg=NULL;
	int size=0;
	//输入要查询的单词
	sndmsg[0]='F';
	fprintf(stdout,"请输入你要查询的单词>>>");
	fgets(sndmsg+1,sizeof(sndmsg),stdin);
	sndmsg[strlen(sndmsg)-1]=0;
	size=strlen(sndmsg);

	//发送单词给服务器
	mysendMsg(cfd,sndmsg,size+1);

	//接收结果信息
	while(1){
		myrecvMsg(cfd,&rcvmsg);
		//处理信息
		if(strcmp(rcvmsg,"break")==0){
			free(rcvmsg);
			break;
		}
		fprintf(stdout,"%s\n",rcvmsg);
		free(rcvmsg);
	}
	return 0;
}

//查看历史记录
int SelectHistory(int cfd)
{
	char sndmsg='H';
	char *rcvmsg=NULL;
	int i=0;
	//给服务器发送请求
	mysendMsg(cfd,&sndmsg,1);

	//接收结果信息
	while(1){
		myrecvMsg(cfd,&rcvmsg);
		//处理信息
		if(strcmp(rcvmsg,"break")==0){
			free(rcvmsg);
			break;
		}
		fprintf(stdout,"%s",rcvmsg);
		i++;
		if(i%3==0){
			putchar(10);
		}else{
			printf("\t\t");
		}
		free(rcvmsg);
	}
	if(1==i){
		putchar(10);
	}
	return 0;
}

//以下功能代码,主要是为了减少TCP的粘包问题

//发送指定大小数据
int writen(int fd,char *msg,int size)
{
	char *buf=msg;
	int count=size;
	while(count>0){
		int len=send(fd,buf,count,0);
		if(len<0){
			perror("send");
			return -1;
		}else if(0==len){
			return size-count;
		}
		buf+=len;
		count-=len;
	}
	return size;
}

//发送数据
int mysendMsg(int fd,char *msg,int size)
{
	if(fd<0 || msg==NULL || size<=0){
		return -1;
	}
	char *buf=(char *)malloc(size+4);
	int len=htonl(size);
	memcpy(buf,(char *)&len,4);
	memcpy(buf+4,msg,size);
	int res=writen(fd,buf,size+4);
	if(res<size+4){
		free(buf);
		fprintf(stderr,"发送失败\n");
		return -1;
	}
	free(buf);
	return size;
}

//接受指定大小数据
int readn(int fd,char *msg,int size)
{
	char *buf=msg;
	int count=size;
	int len=0;
	while(count>0){
		int len=recv(fd,buf,count,0);
		if(len<-1){
			perror("recv");
			return -1;
		}else if(0==len){
			return 0;
		}
		buf+=len;
		count-=len;
	}
	return size;
}

//接受数据
int myrecvMsg(int fd,char **msg)
{
	int len;
	int res;
	res=readn(fd,(char *)&len,4);
	if(res==0){
		return 0;
	}
	len=ntohl(len);
	char *buf=(char *)malloc(len+1);
	res=readn(fd,buf,len);
	if(res<len){
		fprintf(stderr,"接收失败\n");
		free(buf);
		return -1;
	}
	buf[len]='\0';
	*msg=buf;
	return len;
}

        Cli.c:

#include <stdio.h>	
#include <netinet/in.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <unistd.h>
#include "./CliOperation.h"

int main(int argc, const char *argv[])
{
	if(argc<3){
		fprintf(stderr,"传参不足 %s ip port\n",argv[0]);
		return -1;
	}
	
	//创建流式套接字
	int cfd=socket(AF_INET,SOCK_STREAM,0);
	if(cfd<0){
		perror("socket");
		return -1;
	}

	//非绑定

	//填充服务器地址信息结构体
	struct sockaddr_in sin;
	sin.sin_family=AF_INET;
	sin.sin_port=htons(atoi(argv[2]));
	sin.sin_addr.s_addr=inet_addr(argv[1]);
	
	//连接服务器
	if(connect(cfd,(struct sockaddr *)&sin,sizeof(sin))<0){
		perror("connect");
		return -1;
	}

	char msg[256];
	char choose;
	while(1){
START:
		system("clear");
		pri_menu();
		fprintf(stdout,"请输入你的选择>>>");
		choose=getchar();
		while(getchar()!='\n');
		switch(choose){
			case '1':
				Register(cfd);	
				break;
			case '2':
				if(Login(cfd)==1){
					fun_menu(cfd);
					goto START;
				}
				break;
			case '3':
				goto END;
				break;
			default:
				fprintf(stderr,"input choose error,try again!\n");
				break;
		}
		fprintf(stdout,"请输入任意字符清屏>>>");
		fflush(stdout);
		while(getchar()!='\n');
	}

END:
	close(cfd);
	
	return 0;
}

 Makefile:

TARGET:=main
SER:=ser
CLI:=cli
CAN:=-c -o
CC:=gcc
OBJS:=Ser.o SerOperation.o linklist.o
OBJC:=Cli.o CliOperation.o

$(TARGET):$(OBJS) $(OBJC)
	$(CC) $(OBJS) -o $(SER) -lsqlite3
	$(CC) $(OBJC) -o $(CLI) 

%.o:%.c
	$(CC) $< $(CAN) $@

clean:
	rm -rf $(OBJS) $(OBJC) $(SER) $(CLI)

运行效果:

        注册:    

         登录:

         查询单词:

         查看历史记录:

         退出:

查看图形化数据库:

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值