项目要求

服务器:
所需头文件: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)
运行效果:
注册: 

登录:



查询单词:



查看历史记录:

退出:


查看图形化数据库:

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

被折叠的 条评论
为什么被折叠?



