练习: 使用搭建好的服务器和客户端,实现一个完整的注册,登录功能
服务器使用链表 + 文件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和port
addr_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和port
if(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和port
addr_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;
}
实现结果: