Realize the chat room based on Linux socket from 0 - realize the login and registration function of the chat room - 3

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:

  1. The server should be started first and listen to the connection of the client;
  2. When the client starts, first connect to the server and display the login and registration interface;
  3. After receiving the client connection, the server will create a sub thread dedicated to the communication of the client;
  4. 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;
  5. After receiving the client registration information, the server enters the registration process;
  6. 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;
  7. After receiving the server registration processing instruction, the client will print the prompt information and display the menu in step 2.

Sign in

  1. The server should be started first and listen to the connection of the client;
  2. When the client starts, first connect to the server and display the login and registration interface;
  3. After receiving the client connection, the server will create a sub thread dedicated to the communication of the client;
  4. 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;
  5. After receiving the client registration information, the server enters the login processing flow;
  6. 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;
  7. 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:

  1. This article only introduces the realization of login registration function;
  2. Because this article only discusses the implementation of functions, it is not perfect for many abnormal and wrong operations;
  3. 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;
  4. The registration login does not realize the secondary verification and implicit input of password.

Tags: Linux

Posted by mharju on Tue, 17 May 2022 20:25:37 +0300