当前位置:网站首页>Implementation of knowledge consolidation source code 1: epoll implementation of TCP server

Implementation of knowledge consolidation source code 1: epoll implementation of TCP server

2022-07-06 04:17:00 yun6853992

1: Background description

tcp Network communication is a business function that is often repeated in daily business

===》 dependent socket Interface :socket,bind,listen,accept,send,recv Are all familiar to us

===》 dependent io Multiplexing scheme :select,poll,epoll You can choose to use it according to the business scenario

===》 But in fact , Simple tcp In the process of server implementation , There are always some details to pay attention to ,

===》 And consider every re implementation , Rewrite... Many times , Start thinking about backing up some code ....

2:tcp Server source code of demo(epoll Monitor client connections and business processing )

As tcp Server for , Use epoll Listen for readable events ( monitor accept Connect , And listening and receiving ), Conduct business processing .

there epoll Adopted ET Pattern .

You can use the network serial port tool to test , Or realize a tcp The client of .

My code is in linux Environmental use gcc Compiled and tested , The test passed .

Code details you can pay attention to :

===》 Set up socket fd Non blocking

===》 Set ports to be reusable

===》 as well as epoll Event management

/************************************************ info:  Realization tcp Server code   Listening port , Get the connection to the client , And analyze the data  data: 2022/02/10 author: hlp ************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <sys/socket.h>
#include <arpa/inet.h>

#include <sys/epoll.h>

#include <fcntl.h>
#include <unistd.h>
#include <errno.h>

// Realization tcp Server function of 
//1: establish socket
//2:bind listen accept
//3:recv send
//4: Set up fd reusable , Non blocking .
//5: How to use io Multiplexing ?  What if we use the event mechanism ?

#define VPS_PORT 9999

// establish socket
int vps_init_socket();
// establish epoll  And conduct event listening and processing 
void vsp_socket_exec(int listenfd);

int main()
{
    
	int fd = vps_init_socket();
	if(fd < 0)
	{
    
		printf("create vps socket fd error. \n");
		return -1;
	}else
	{
    
		printf("create vps socket fd success. \n");
	}

	//epoll monitor   Callback for processing 
	vsp_socket_exec(fd);

	printf("vps socket end. \n");
	return 0;
}

// Set up fd Non blocking   By default  fd It's blocked 
int SetNonblock(int fd) {
    
	int flags;
	flags = fcntl(fd, F_GETFL, 0);
	if (flags < 0)
		return flags;
	flags |= O_NONBLOCK;
	if (fcntl(fd, F_SETFL, flags) < 0) 
		return -1;
	return 0;
}

// establish   Server side socket, there ip and port It's dead 
int vps_init_socket()
{
    
	int fd = socket(AF_INET, SOCK_STREAM, 0);
	if(fd < 0)
	{
    
		printf("create socket error. \n");
		return -1;
	}
	// Set up fd Non blocking   Set ports to be reusable 
	SetNonblock(fd);
	int optval = 1;
	setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(int));

	// Definition fd Related parameters 
	struct sockaddr_in server_addr;
	memset(&server_addr, 0, sizeof(struct sockaddr_in));
	server_addr.sin_family = AF_INET;
	server_addr.sin_port = htons(VPS_PORT);
	server_addr.sin_addr.s_addr = htonl(INADDR_ANY);

	if(bind(fd, (struct sockaddr*)&server_addr, sizeof(struct sockaddr)) < 0)
	{
    
		printf("vps socket bind error \n");
		return -1;
	}

	// Set up fd For passive sockets   for accept use   Set up listen The size of the queue  
	if(listen(fd , 20) < 0)
	{
    
		printf("vps socket listen error \n");
		return -1;
	}

	printf("create and set up socket success. start accept.. \n");
	return fd;
}

// Can be combed socket Associated interface   Non blocking   And parameters   Network byte order correlation 
/* #include <netinet/in.h> typedef uint32_t in_addr_t; struct in_addr { in_addr_t s_addr; }; struct sockaddr_in { __SOCKADDR_COMMON (sin_); in_port_t sin_port; struct in_addr sin_addr; unsigned char sin_zero[sizeof (struct sockaddr) - __SOCKADDR_COMMON_SIZE - sizeof (in_port_t) - sizeof (struct in_addr)]; };*/

//  establish epoll  Return the epollfd  Failure to return -1
int create_epoll_and_add_listenfd(int listenfd);
//  As a server   Has been to epoll monitor   Business processing 
int vps_epoll_wait_do_cycle(int epfd, int listenfd);
//  Events trigger   Processing connection requests 
int vps_accept_exec(int epfd, int listenfd);
//  Events trigger   Processing readable requests   Reading data   There is no monitor to write here , I understand that the business is not complicated and frequent , I write and send directly 
int vps_recv_exec(int epfd, int connfd);

// establish epoll  monitor acceptfd,  Listen to the logic of receiving and sending 
void  vsp_socket_exec(int listenfd)
{
    
	// establish epollfd, And join the listening node 
	int epollfd = -1;
	if((epollfd = create_epoll_and_add_listenfd(listenfd)) <0)
	{
    
		printf("create epollfd error. \n");
		close(listenfd);
		return ;
	}

	printf("create epollfd [%d] success, start epoll wait... \n", epollfd);

	// Use epoll_wait Yes epoll monitor 
	vps_epoll_wait_do_cycle(epollfd, listenfd);
	return;
}

// establish epoll  And give epoll Add a listening node  EPOLL_ADD listenfd
int create_epoll_and_add_listenfd(int listenfd)
{
    
	// establish epoll
	int epfd = -1;
	epfd = epoll_create(1); // Parameter ignored must be greater than 0
	if(epfd == -1)
	{
    
		printf("create vsp epoll error. \n");
		return -1;
	}
	//epoll_ctl Join a node 
	struct epoll_event event;
	event.data.fd = listenfd;
	event.events = EPOLLIN | EPOLLET;  // Monitor access   use ET

	if(epoll_ctl(epfd, EPOLL_CTL_ADD, listenfd, &event) == -1)
	{
    
		printf("vps epoll add listenfd error. \n");
		close(epfd);
		return -1;
	}
	printf("vps epoll create success and add listenfd success.[%d] \n", epfd);
	return epfd;
}

// Use epoll_wait Yes epfd monitor   And then business processing  
int vps_epoll_wait_do_cycle(int epfd, int listenfd)
{
    
	struct epoll_event event_wait[1024];
	int nready = 0;

	while(1) // If multithreading   You should set the termination flag here 
	{
    
		//int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);
		nready = epoll_wait(epfd, event_wait, 1024, 1000);
		if(nready < 0)
		{
    
			if (errno == EINTR)//  The signal is interrupted 
    		{
    
	    		printf("vps epoll_wait return and errno is EINTR \n");
                continue;
    		}
            printf("vps epoll_wait error.[%s]\n", strerror(errno));
            break;
		}

		if(nready == 0)
		{
    
			continue;
		}

		// There are already related events triggered here   Conduct business processing 
		for(int i = 0; i<nready; i++)
		{
    
			// Process readable , distinguish listenfd
			if(event_wait[i].events & EPOLLIN)
			{
    
				if(event_wait[i].data.fd == listenfd)
				{
    
					// Handle accept  It should listen and read here   No listening and writing 
					vps_accept_exec(epfd, event_wait[i].data.fd);
				}else
				{
    
					// Handle recv,  It is possible that the opposite end is actively closed ,
					vps_recv_exec(epfd, event_wait[i].data.fd); 
				}
			}

			// In this case, we should start from epoll Remove , And shut down fd
    		// If it is not the business that the client terminates after sending it , Are we not del, There are only exceptions del
    		if (event_wait[i].events & (EPOLLERR | EPOLLHUP)) //EPOLLHUP  Have finished reading 
    		{
    
    			printf("epoll error [EPOLLERR | EPOLLHUP].\n");
    			epoll_ctl(epfd, EPOLL_CTL_DEL, event_wait[i].data.fd, NULL);
    			close(event_wait[i].data.fd);
    		}
		}
	} 
	return 0;
}

// The general design is   After receiving   Delete event Listen for readable Events , Insert the reply string , Listen for writable events to send .
// Either use reactor The mode handles reception and transmission here   or , Don't pay attention to the monitoring of sending for the time being , Business is not sent frequently here , Therefore, the necessary data will be returned directly after receiving 

//  Events trigger   Processing connection requests 
int vps_accept_exec(int epfd, int listenfd)
{
    
	// There is a link   need epoll receive  epoll_ctl Add listening for readable Events 
	struct  sockaddr_in cliaddr;
	socklen_t clilen = sizeof(struct sockaddr_in);

	//et Pattern   Take out all the connections 
	int clifd = -1;
	int ret = 0;
	while(clifd = accept(listenfd, (struct sockaddr *)&cliaddr, &clilen))
	{
    
		//accept  Returns a nonnegative integer normally   Return... On error -1  This debug Debug it 
		if(clifd == -1)
		{
    
			// The resource is temporarily unavailable   You should try again   But you should not retry indefinitely 
			if (((errno == EAGAIN) || (errno == EWOULDBLOCK) )&& ret <3) 
			{
    
				ret++;
				continue;
			}
			printf(" accept error: [%s]\n", strerror(errno));
			return -1;
		}
		// For those already connected fd To deal with   Should join epoll
		SetNonblock(clifd);
		// Join in epoll
		struct epoll_event clifd_event;
		clifd_event.data.fd = clifd;
		clifd_event.events = EPOLLIN | EPOLLET; //ET The mode should be read circularly 
		if(epoll_ctl(epfd, EPOLL_CTL_ADD, clifd, &clifd_event) == -1)
		{
    
			printf("vps accetp epoll ctl error . \n");
			close(clifd);
			return -1;
		}
		printf("accept success. [%d:%s:%d] connected \n",clifd, inet_ntoa(cliaddr.sin_addr), ntohs(cliaddr.sin_port));
	}
	return 0;
}

//  Events trigger   Processing readable requests   Reading data   There is no monitor to write here ,
int vps_recv_exec(int epfd, int connfd)
{
    
	// Here is the real business process , Receive data and actively send a return data .
	// If there's data   Receive   Until the reception is finished , Close the connection 
	printf("start recv data from client [%d].",connfd);
	// Business scenarios are not frequent here   The client terminates every time it sends ?
	// Try to let the client actively disconnect ,
	// You can implement a timer by yourself , Detect active disconnect processing 
	char recv_data[1024] = {
    0};
	int datalen = -1;
	// There may be a signal interruption   The receiving length is -1 Scene 
	while(1){
    
		// Can't take  ==0 Add here   Otherwise, it will loop when the client is disconnected 
		while((datalen = read(connfd,  recv_data,  1024)) > 0 )
		{
    
			printf("recv from [%d] data len[%d], data[%s] \n", connfd, datalen, recv_data);
			memset(recv_data, 0, 1024);
		}

		// Shut down on the client   When disconnected   The receiving length is 0
		printf("recv from [fd:%d] end \n", connfd);

		// Send a reply message to the received message   Here you can save some fd And the client ip and port Correlation , Construct reply message 
		const char * send_data = "hi i have recv your msg \n";
		if(strlen(send_data) ==  write(connfd, send_data, strlen(send_data)))
		{
    
			printf("send buff succes [len:%lu]%s", strlen(send_data), send_data);
		}

		// The server receives empty packets because the client is shut down , The corresponding... Should be closed fd And from epoll Remove 
		if(datalen == 0)
		{
    
			if(epoll_ctl(epfd, EPOLL_CTL_DEL, connfd, 0) == -1)
			{
    
				printf("vps [fd:%d] close ,remove from epoll event error\n", connfd);
			}else
			{
    
				printf("vps [fd:%d] close ,remove from epoll event success\n", connfd);
				close(connfd);
			}
			
			break;
		}


		// be equal to 0  It may be the end of the reading 
		if(datalen == -1)
		{
    
			printf("recv end error: [%s]\n", strerror(errno));// Inevitable trigger   Has received 
			if (errno == EWOULDBLOCK && errno == EINTR) // Don't deal with it 
			{
    
				continue;
			}
			// Do you want to remove this fd Well ?  Treat as removed  tcp Short connection 
			// if(epoll_ctl(epfd, EPOLL_CTL_DEL, connfd, 0) == -1)
			// {
    
			// printf("vps client [%d] remove from epoll error\n", connectfd);
			// }else
			// {
    
			// printf("vps client [%d] remove from epoll success\n", connectfd);
			// }
			
			// close(connfd);
			break;
		}
	}
	
	return 0;
}

3: Code testing

I use network tools to test :

 Insert picture description here

I started trying to accumulate some common code : Spare in your own code base

More of my knowledge comes from here , I recommend you understand :Linux,Nginx,ZeroMQ,MySQL,Redis,fastdfs,MongoDB,ZK, Streaming media ,CDN,P2P,K8S,Docker,TCP/IP, coroutines ,DPDK Etc , Learn now

原网站

版权声明
本文为[yun6853992]所创,转载请带上原文链接,感谢
https://yzsam.com/2022/02/202202132234018764.html