In the previous article, we have talked about how to build a multi-threaded server model, which can support multiple clients to connect to the server at the same time. In this article, we will realize multiple clients, how to register information with the server and realize the function of login.
data structure
Then, the example code of the previous article continues to add functions. To realize the function of registration and login, we must let the server and client send and receive signaling in a unified format when interacting with data packets. Signaling Formats
//Structure of C/S Communication struct protocol{ int cmd; //command int state; //Store command return information char name[32]; //user name char data[64]; //data };
Command type: The commands in the signaling format are defined as follows:
/*cmd*/ #define BROADCAST 0X00000001 / / broadcast data #define PRIVATE 0X00000002 / / private chat #Define register 0x00000004 / / registered account #define LOGIN 0X00000008 / / login #Define onlineuser 0x00000010 / / show online users #Define logout 0x00000020 / / exit
Online user information The server needs to maintain all user information and know whether the user is online and registered.
//Online users struct ONLINE{ int fd; //-1: The user is offline > 0: the user has logged in, and the corresponding socket int flage; //-1 this entry has no user information 1: this entry has user registration information char name[32]; //Registered user name char passwd[32]; //User name and password }; struct ONLINE online[MAX_USER_NUM];
The registered client information needs to be stored in the server. For simplicity, we don't need to store the database temporarily. We only define a global array to store the client information, and only 64 clients are allowed to log in.
Server processing result return value
/*return code*/ #define OP_ OK 0x80000000. / / the operation succeeds #define ONLINEUSER_ OK 0x80000001 / / the online user is displayed, but not finished #define ONLINEUSER_ Over 0x80000002 / / the online user is displayed and has been sent #define NAME_EXIST 0X80000003 / / registration information. The user name already exists #define NAME_PWD_NMATCH 0X80000004 / / when logging in, the user name and password entered are incorrect #define USER_ Logged 0x80000005 / / when logging in, you will be prompted that the user is online #define USER_ NOT_ / / when you log in, you will be prompted to register 80000x0006
Functional flow chart
Now let's draw a flow chart according to the function.
register
As shown in the figure above:
- The server should be started first and listen to the connection of the client;
- When the client starts, first connect to the server and display the login and registration interface;
- After receiving the client connection, the server will create a sub thread dedicated to the communication of the client;
- After selecting registration, prompt the user name and password, encapsulate the registration information into the structure variable msg, and send the signaling to the server;
- After receiving the client registration information, the server enters the registration process;
- Registration function: first find out whether the user name exists, the location registered in the array online [], and the flag value is 1, otherwise it is - 1; If the user name is already registered, return NAME_EXIST error message; If the user name is not registered, find a free location, save the user name and password in the database online [], and return the signaling of successful registration;
- After receiving the server registration processing instruction, the client will print the prompt information and display the menu in step 2.
Sign in
- The server should be started first and listen to the connection of the client;
- When the client starts, first connect to the server and display the login and registration interface;
- After receiving the client connection, the server will create a sub thread dedicated to the communication of the client;
- After selecting login, prompt the user name and password, encapsulate the login information into the structure variable msg, and send the signaling to the server;
- After receiving the client registration information, the server enters the login processing flow;
- Login function: first, check whether the user name and password match in the array online [], find and return the corresponding subscript, save the socket connected to the client into the corresponding entry, and return the login success information to the client; If it is not found, return - 1 and return 0X80000004 error message to the client;
- After receiving the server registration processing instruction, the client will print the prompt information and set the client's online flag login_ If f is 1, the menu corresponding to the chat function will be displayed.
code
chat.h
#ifndef _TCP_CHAT #define _TCP_CHAT #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <string.h> #include <pthread.h> #include <stdlib.h> #include <string.h> #define SERVER_PORT 8888 //Online users struct ONLINE{ int fd; //-1 int flage; //registed or not char name[32]; char passwd[32]; }; #define MAX_USER_NUM 64 //Structure of C/S Communication struct protocol{ int cmd; int state; char name[32]; char data[64]; }; /*cmd*/ #define BROADCAST 0X00000001 #define PRIVATE 0X00000002 #define REGISTE 0X00000004 #define LOGIN 0X00000008 #define ONLINEUSER 0X00000010 #define LOGOUT 0X00000020 /*return code*/ #define OP_OK 0X80000000 #define ONLINEUSER_OK 0X80000001 #define ONLINEUSER_OVER 0X80000002 #define NAME_EXIST 0X80000003 #define NAME_PWD_NMATCH 0X80000004 #define USER_LOGED 0X80000005 #define USER_NOT_REGIST 0X80000006 #endif
client.c
/********************************************* Official account: one port Linux *********************************************/ #include "chat.h" int sockfd; int addrlen; struct sockaddr_in server_addr; pthread_t pid; int login_f = -1; void *func(void *arg) { int len; char buf[64]={0}; while(1) { if(login_f != 1) { continue; } len = read(sockfd,buf,sizeof(buf)); if(len<=0) { close(sockfd); return; } buf[len]='\0'; printf("%s\n",buf); } } void broadcast(int fd) { } void private(int fd) { } void list_online_user(sockfd) { } int registe(int fd) { struct protocol msg,msgback; msg.cmd = REGISTE; printf("input your name\n"); scanf("%s",msg.name); printf("input your passwd\n"); scanf("%s",msg.data); write(sockfd,&msg,sizeof(msg)); read(sockfd,&msgback,sizeof(msgback)); if(msgback.state != OP_OK) { printf("Name had exist,try again!\n"); getchar(); getchar(); return -1; }else{ printf("Regist success!\n"); getchar(); getchar(); return 0 ; } } int login(int fd) { struct protocol msg,msgback; msg.cmd = LOGIN; printf("input your name\n"); scanf("%s",msg.name); printf("input your passwd\n"); scanf("%s",msg.data); write(sockfd,&msg,sizeof(msg)); read(sockfd,&msgback,sizeof(msgback)); if(msgback.state != OP_OK) { printf("Name had exist,try again!\n"); getchar(); getchar(); login_f = -1; return NAME_PWD_NMATCH; }else{ printf("Login success!\n"); getchar(); getchar(); login_f = 1; return OP_OK ; } } int logout(int fd) { close(fd); login_f = -1; } int main(int argc, char **argv) { int sel; int ret; int min_sel,max_sel; int portnumber; struct protocol msg; if(argc<3) { printf("cmd: %s ip portnumber\n",argv[0]); return; } //argv2 stores the port number. Read the port and convert it into an integer variable if((portnumber=atoi(argv[2]))<0) { fprintf(stderr,"Usage:%s hostname portnumber\a\n",argv[0]); exit(1); } sockfd = socket(PF_INET,SOCK_STREAM,0); if(sockfd<0) { perror("socket() fail\n"); return; } server_addr.sin_family = PF_INET; server_addr.sin_port = htons(portnumber); server_addr.sin_addr.s_addr = inet_addr(argv[1]); addrlen = sizeof(struct sockaddr_in); connect(sockfd,(struct sockaddr* )&server_addr,addrlen); pthread_create(&pid, NULL,func, NULL); while(1) { //getchar(); system("clear"); if(login_f == -1){ printf("\t 1 register\n"); printf("\t 2 Sign in\n"); }else if(login_f == 1){ printf("\t 3 Public chat\n"); printf("\t 4 Private chat\n"); printf("\t 5 Online list\n"); } printf("\t 0 sign out\n"); fflush(stdin); scanf("%d",&sel); if(sel == 0) { break; } if(login_f == 1) { min_sel = 3; max_sel = 5; }else if(login_f == -1){ min_sel = 1; max_sel = 2; } if(sel<min_sel || sel > max_sel) { printf("Valid choice ,try again\n"); continue; } switch(sel) { case 1: registe(sockfd); break; case 2: ret = login(sockfd); break; case 3: broadcast(sockfd); break; case 4: private(sockfd); break; case 5: list_online_user(sockfd); case 0: logout(sockfd); break; default: break; } if(sel == 0) { exit(0); } } }
server.c
/********************************************* Official account: one port Linux *********************************************/ #include "chat.h" struct ONLINE online[MAX_USER_NUM]; void del_user_online(int index) { int i; char buf[128]={0}; if(index <0) { return; } online[index].fd = -1; sprintf(buf,"%s offline\n",online[index].name); //Notify all clients that a user is offline for(i=0;i<MAX_USER_NUM;i++) { if(online[i].fd == -1) { continue; } write(online[i].fd,buf,strlen(buf)); } return; } int add_user(int sockfd,struct protocol*msg) { int i,index = -1; char buf[128]={0}; for(i=0;i<64;i++)//Add to online user list { if(online[i].flage == -1) { online[i].flage= 1; strcpy(online[i].name,msg->name); strcpy(online[i].passwd,msg->data); printf("regist %s to %d \n",msg->name,i); index = i; return index; } } return index; } void broadcast(int index,struct protocol*msg) { } int find_dest_user_online(int sockfd,int *index,struct protocol*msg) { int i; for(i=0;i<MAX_USER_NUM;i++) { //this pos not use if(online[i].flage== -1) { continue; } if((strcmp(msg->name,online[i].name)==0)&&(strcmp(msg->data,online[i].passwd)==0)) { if(online[i].fd == -1) { online[i].fd = sockfd; *index = i ; return OP_OK; }else{ //user had loged printf("%s had login\n",online[i].name); return USER_LOGED; } } } return NAME_PWD_NMATCH; } int find_dest_user(char *name) { int i; for(i=0;i<MAX_USER_NUM;i++) { if(online[i].flage == -1) { continue; } if(strcmp(name,online[i].name)==0) { return i; } } return -1; } void private(int index,struct protocol*msg) { } void list_online_user(int index) { } void registe(int sockfd,int *index,struct protocol*msg) { int dest_index; char buf[128]; struct protocol msg_back; msg_back.cmd = REGISTE; //Find the man dest_index = find_dest_user(msg->name); if(dest_index == -1) { // this user can registe *index = add_user(sockfd,msg); online[*index].flage = 1; msg_back.state = OP_OK; printf("user %s regist success!\n",msg->name); write(sockfd,&msg_back,sizeof(msg_back)); return; }else{ msg_back.state = NAME_EXIST; printf("user %s exist!\n",msg->name); write(sockfd,&msg_back,sizeof(msg_back)); return; } } void login(int sockfd,int *index,struct protocol*msg) { int i; int ret; char buf[128]; struct protocol msg_back; msg_back.cmd = LOGIN; //Find the man ret = find_dest_user_online(sockfd,index,msg); if(ret != OP_OK) { msg_back.state = ret; strcpy(buf,"there is no this user\n"); printf("user %s login fail!\n",msg->name); write(sockfd,&msg_back,sizeof(msg_back)); return; }else{ msg_back.state = OP_OK; strcpy(msg_back.data,"login success\n"); printf("user %s login success!index =%d \n",msg->name,*index); write(online[*index].fd,&msg_back,sizeof(msg_back)); } //Notify all clients that a user is online sprintf(buf,"%s online\n",online[*index].name); for(i=0;i<MAX_USER_NUM;i++) { if(online[i].fd != -1) { write(online[i].fd,buf,strlen(buf)); } } } void *func(void *arg) { int sockfd = *((int*)arg); char buf[64]; int len; int index = -1;//The user is in the subscript of the online user list struct protocol msg; free(arg); //Enter the chat while(1) { len = read(sockfd,&msg,sizeof(msg)); if(len<=0) {//Offline printf("%s offline\n",online[index].name); //Remove from online list del_user_online(index); close(sockfd); return; } switch(msg.cmd) { case REGISTE: registe(sockfd,&index,&msg); break; case LOGIN: login(sockfd,&index,&msg); break; case BROADCAST: broadcast(index,&msg); break; case PRIVATE: private(index,&msg); break; case ONLINEUSER: list_online_user(index); break; default: break; } } } int main(int argc, char **argv) { int lsfd,newfd; int addrLen,cliaddrlen; struct sockaddr_in my_addr; struct sockaddr_in cli_adr; char buf[64]="xuezhiqian fuhele\n"; pthread_t pid; int *arg; int i; int portnumber; if(argc<2) { printf("cmd: %s portnumber\n",argv[0]); return; } /*¶˿ںŲ»¶ԣ¬͋³ö*/ if((portnumber=atoi(argv[1]))<0) { fprintf(stderr,"Usage:%s portnumber\a\n",argv[0]); exit(1); } lsfd = socket(PF_INET,SOCK_STREAM,0); if(lsfd<0) { perror("socket() fail\n"); return; } bzero(&my_addr,sizeof(struct sockaddr_in)); my_addr.sin_family = PF_INET; my_addr.sin_port = htons(portnumber); my_addr.sin_addr.s_addr = htonl(INADDR_ANY); addrLen = sizeof(struct sockaddr_in); if(bind(lsfd,(struct sockaddr* )&my_addr ,addrLen)<0) { perror("bind() fail\n"); return; } listen(lsfd,5); cliaddrlen = sizeof(struct sockaddr_in); for(i=0;i<64;i++) { online[i].fd = -1; online[i].flage= -1; } while(1) { newfd = accept(lsfd,(struct sockaddr *)&cli_adr,&cliaddrlen); printf("client:ip:%s port:%d \n", inet_ntoa(cli_adr.sin_addr),cli_adr.sin_port); arg = malloc(sizeof(int)); *arg = newfd;//You must find out why you want to apply for memory pthread_create(&pid,NULL,func, (void*)arg); } close(newfd); close(lsfd); }
screenshot
Client 1 registration
User name: yikoulinux
Password: qqqq
Client log
Server log
Client 2 Registration
User name: yikoupeng
Password: qqqq
Server / client log
Client 1 login
Login log
Press enter, the client will hide the login and registration menus, and display the menus of public chat, private chat and online list. As shown in the figure below:
Client 2 login
Login log
remarks:
- This article only introduces the realization of login registration function;
- Because this article only discusses the implementation of functions, it is not perfect for many abnormal and wrong operations;
- The information of online users should be saved in the database [such as sqlite]. In order to facilitate readers' understanding, this article temporarily replaces it with an array;
- The registration login does not realize the secondary verification and implicit input of password.