练习: 使用搭建好的服务器和客户端,实现一个完整的注册,登录功能
服务器使用链表 + 文件IO的形式去记录账号和密码
代码实现:
服务器端:
#include <myhead.h>
struct Pack{char flags;char name[20];char pswd[20];
};
//创建单链表结构体类型
typedef struct node{union{int len;struct Pack data;};struct node *next;
}Link,* Plink;
Plink L;
//创建链表表头
Plink create_link(){Plink p=(Plink)malloc(sizeof(Link));if(p==NULL){perror("malloc");return NULL;}p->len=0;p->next=NULL;return p;
}
//采用尾插法增加注册信息
int insert_tail(Plink L,struct Pack *Pdata){//创建新节点Plink p=(Plink)malloc(sizeof(Link));if(p==NULL){perror("malloc");return -1;}p->data=*Pdata;//接收数据Plink t=L;while(t->next!=NULL){//遍历到尾部t=t->next;}p->next=t->next;t->next=p;L->len++;return 1;
}
//查找登录信息
int research_link(Plink L,struct Pack *Pdata){Plink t=L->next;while(t!=NULL){if(strcmp(t->data.name,Pdata->name)==0&&\strcmp(t->data.pswd,Pdata->pswd)==0){return 1;}t=t->next;}return -1;
}
void handler(int signo){if(signo==SIGINT){FILE *fp=fopen("./server.txt","a+");if(fp==NULL){perror("fopen");return;}Plink t=L->next;while(t!=NULL){fprintf(fp,"%s %s\n",t->data.name,t->data.pswd);t=t->next;}fclose(fp);exit(EXIT_SUCCESS);}
}
int main(int argc, const char *argv[])
{//当服务器断线时,将链表信息存储到文件中保存if(signal(SIGINT,handler)==SIG_ERR){perror("signal");return -1;}if(argc!=2){printf("请输入端口号\n");return -1;} int port=atoi(argv[1]);//从终端接收端口号//创建tcp套接字int server=socket(AF_INET,SOCK_STREAM,0);//准备一个tcp用的地址信息结构体,用于存放ip和portaddr_in_t addr={0};addr.sin_family=AF_INET;addr.sin_port=htons(port);//端口号转为网络字节序addr.sin_addr.s_addr=inet_addr("127.0.0.1");//用于测试案例使用//使用已经准备好的地址信息结构体,为套接字绑定ip和portif(bind(server,(addr_t *)&addr,sizeof(addr))==-1){//因为端口号容易被占用,所以bind会容易出错,需要判断perror("bind error");return -1;}//监听listen(server,10);addr_in_t client_addr={0};socklen_t client_len=sizeof(addr);int client=accept(server,(addr_t *)&client_addr,&client_len);printf("客户端连接成功\n");//服务器读取消息/*int flags=fcntl(client,F_GETFL);flags=flags|0_NONBLOCK;fcntl(client,F_SETFL,flags);*/struct Pack pack={0};L=create_link();//创建链表表头while(1){memset(&pack,0,sizeof(pack));int res=read(client,&pack,sizeof(pack));if(res==0){ printf("客户端断开连接\n");close(client); raise(SIGINT);//当客户端断开连接时,向自己进程发送信号break;}if(pack.flags==1){//链表增加注册信息采用尾插法char flag1;if(insert_tail(L,&pack)==1){printf("注册信息已加载\n");flag1=1;write(client,&flag1,1);//往客户端回复已注册成功消息}else{printf("注册错误,无法加载\n");flag1=0;write(client,&flag1,1);//往客户端回复注册失败消息}}else if(pack.flags==2){//遍历链表已查找登录信息char flag2;if(research_link(L,&pack)==1){printf("与服务器注册信息匹配\n");flag2=1;write(client,&flag2,1);//往客户端回复登录成功消息}else{printf("与服务器注册信息不匹配\n");flag2=0;write(client,&flag2,1);//往客户端回复登录失败消息}}}return 0;
}
客户端端 :
#include <myhead.h>
struct Pack{char flags;char name[20];char pswd[20];
};
void menu(){printf("****用户注册登录功能****\n");printf("****1.注册**2.登录******\n");printf("*********0.退出*********\n");
}
void logic_user(struct Pack *ps,int client){printf("请输入注册姓名:");scanf("%s",ps->name);printf("请输入注册密码:");scanf("%s",ps->pswd);write(client,ps,sizeof(struct Pack));printf("注册信息已发送\n");
}
void pswd_user(struct Pack *ps,int client){printf("请输入登录姓名:");scanf("%s",ps->name);printf("请输入登录密码:");scanf("%s",ps->pswd);write(client,ps,sizeof(struct Pack));printf("登录信息已发送\n");
}
int main(int argc, const char *argv[])
{ if(argc!=2){printf("请输入端口号\n");return -1;}int port=atoi(argv[1]);//从终端接收端口号//创建tcp套接字int client=socket(AF_INET,SOCK_STREAM,0);//准备一个tcp用的地址信息结构体,用于存放ip和portaddr_in_t addr={0};addr.sin_family=AF_INET;addr.sin_port=htons(port);//端口号转为网络字节序addr.sin_addr.s_addr=inet_addr("127.0.0.1");//与服务器连接if(connect(client,(addr_t *)&addr,sizeof(addr))==-1){perror("connect error");return -1;}struct Pack pack={0};//向客户端发送消息while(1){menu();char res=0;memset(&pack,0,sizeof(pack));printf("请输入选项:");scanf("%d",&pack.flags);switch(pack.flags){case 1:logic_user(&pack,client);read(client,&res,1);//从服务器读取注册完成信号if(res==1){printf("注册成功\n");}else{printf("注册失败\n");}break;case 2:pswd_user(&pack,client);read(client,&res,1);//从服务器读取登录完成信号if(res==1){printf("登录成功\n");}else{printf("登录失败\n");}break;case 0:exit(EXIT_SUCCESS);default:printf("输入选项错误,请重新输入\n");break;} }return 0;
}
实现结果: