Socket Programming Chapter Six IO Multiplexing--select, poll, Epoll

Source: Internet
Author: User
Tags assert bind epoll int size socket strlen thread htons

Article reference from: http://blog.csdn.net/tennysonsky/article/details/45745887 (Akihabara-mike VS Mike "Linux system programming--i/o Multiplexing Select, poll, Epoll the difference between the use of ")

In addition, there is a good article: Epoll mechanism: epoll_create, Epoll_ctl, epoll_wait, close (fish think Yuan's column)


In the previous article, I simply learned the basic concept of IO multiplexing, and here I will learn three ways to achieve it:Select,poll,Epoll.


I/O multiplexing is a technique that occurs in order to resolve a process or thread blocking a call to an I/O system so that a process or thread does not block a particular I/O system call.

Select (), poll (), Epoll () are all mechanisms for I/O multiplexing. I/O multiplexing uses a mechanism that can monitor multiple descriptors and, once a descriptor is ready (usually read-ready or write-ready, before the file descriptor is read-write), notifies the program to read and write accordingly . But select (), poll (), Epoll () are essentially synchronous I/O, because they all need to read and write when the read-write event is ready, that is, the read-write process is blocked, and asynchronous I/O is not responsible for reading and writing, asynchronous i/ The implementation of O is responsible for copying the data from the kernel to the user space.

The biggest advantage of I/O multiplexing compared to multi-threading (the TPC (Thread per Connection) model) and multiple processes (typical Apache model (Process Per Connection, PPC) is the low system overhead, The system does not need to establish new processes or threads, and it does not have to maintain these threads and processes.



use of select ()

Required header file:

#include <sys/select.h>


Function Prototypes:

int select (int Nfds, fd_set *readfds, Fd_set *writefds, Fd_set *exceptfds, struct timeval *timeout);


Function Description:

Monitors and waits for multiple file descriptor property changes (readable, writable, or error exceptions). The Select () function monitors file descriptors in 3 categories, Writefds, Readfds, and Exceptfds, respectively. After the call, the Select () function blocks until a descriptor is ready (with data readable, writable, or with an error exception), or timeout (timeout specifies the wait time) before the function returns. When the Select () function returns, you can find the ready descriptor by traversing Fdset.


Parameter description:

Nfds: The range of file descriptors to monitor, generally take the maximum number of monitored descriptors +1, as in this case write 10, so that the descriptor 0, 1, 2 ... 9 will be monitored, and the maximum value on Linux is typically 1024.

READFD: A set of readable descriptors for monitoring, which is stored as long as the file descriptor is ready to read.

Writefds: A collection of writable descriptors that are monitored.

Exceptfds: Monitored error exception descriptor collection.


Three parameters Readfds, Writefds, and Exceptfds specify the descriptor for which we want the kernel to monitor read, write, and exception conditions. If you do not need to use one of the conditions, you can set it to null.


Some of the more important macros:

Empties the collection
void Fd_zero (Fd_set *fdset); 

Adds a given file descriptor to the collection
void fd_set (int FD, fd_set *fdset);

Removes a given file descriptor from the collection
void fd_clr (int FD, fd_set *fdset);

Checks whether the specified file descriptor in the collection can read 
and write int fd_isset (int FD, fd_set *fdset); 

Timeout: The timeout period, which tells the kernel how long it will take to wait for any one of the specified descriptors to be ready. Its timeval structure is used to specify the number of seconds and microseconds for this time period.
struct Timeval
{
time_t tv_sec;       /* sec */
suseconds_t tv_usec;/* microseconds */
};


Three possible function return conditions:

1) Always wait: timeout is set to null pointer null, and no descriptor is ready.

2) Wait for fixed time: timeout is set to a fixed time, returns when a descriptor is ready, and returns 0 if the time is up, even if no file descriptor is ready.

3) do not wait (no blocking): Returns immediately after the description word is checked, which is called polling. For this reason, the time value of the struct Timeval variable is specified as 0 seconds, 0 microseconds, the file descriptor property has no change returned 0, and there is a change to return the number of prepared descriptors.


function return value:

Success: The number of ready descriptors (while modifying Readfds, Writefds, and Exceptfds three parameters), time-out returned 0;
Error:-1.


Below with the Socket example, two clients, one of each 5s send a fixed string to the server, another acquisition terminal keyboard input, send it to the server, a server, using IO multiplexing processing these two clients. The code is as follows:


Server:

#include <cstdio> #include <sys/select.h> #include <unistd.h> #include <stdlib.h> #include < cstring> #include <cassert> #include <sys/types.h> #include <sys/socket.h> #include <netinet/
in.h> #include <arpa/inet.h> const int buffer_size = 4096;

const int server_port = 2222;

inline int max (int a, int b) {return (a > B?: A, b);}
	int main () {int server_socket;
	Char Buff1[buffer_size];
	Char Buff2[buffer_size];
	Fd_set RfDs;
	struct Timeval TV;
	int ret;

	int n;
	Server_socket = socket (af_inet, sock_stream, 0);

	ASSERT (Server_socket! =-1);
	struct sockaddr_in server_addr;
	memset (&server_addr, 0, sizeof (SERVER_ADDR));
	server_addr.sin_family = af_inet;
	Server_addr.sin_port = htons (Server_port);

	SERVER_ADDR.SIN_ADDR.S_ADDR = htonl (Inaddr_any);
	ASSERT (Bind (server_socket, (struct sockaddr *) &server_addr, sizeof (SERVER_ADDR))! =-1);
	
	ASSERT (Listen (Server_socket, 5)! =-1); struct sockaddr_in client_addr1,CLIENT_ADDR2;
	
	socklen_t client_addr_len = sizeof (struct sockaddr_in);

	printf ("waiting...\n");
	Here we first establish two TCP connections int connfd1 = Accept (Server_socket, (struct sockaddr*) &client_addr1, &client_addr_len);
	ASSERT (Connfd1! =-1);
	printf ("Connect from%s:%d\n", Inet_ntoa (CLIENT_ADDR1.SIN_ADDR), Ntohs (Client_addr1.sin_port));
	int connfd2 = Accept (Server_socket, (struct sockaddr*) &AMP;CLIENT_ADDR2, &client_addr_len);
	ASSERT (Connfd2! =-1);

	printf ("Connect from%s:%d\n", Inet_ntoa (CLIENT_ADDR2.SIN_ADDR), Ntohs (Client_addr2.sin_port));
		while (1) {Fd_zero (&AMP;RFDS);
		Fd_set (CONNFD1, &rfds);

		Fd_set (CONNFD2, &rfds);
		Tv.tv_sec = 10;
		
		tv.tv_usec = 0;
		printf ("select...\n");
		ret = SELECT (Max (CONNFD1, CONNFD2) + 1, &rfds, NULL, NULL, NULL);
		
		ret = SELECT (Max (CONNFD1, CONNFD2) + 1, &rfds, NULL, NULL, &AMP;TV);
		if (ret = =-1) perror ("Select ()"); else if (Ret > 0) {if (Fd_isset (CONNFD1, &rfds)) {n = recv (connfd1, buff1, buffer_size, 0);					Buff1[n] = ' + ';
			Note Manually add the string terminator to printf ("Connfd1:%s\n", buff1);
				} if (Fd_isset (CONNFD2, &rfds)) {n = recv (connfd2, buff2, buffer_size, 0);					Buff2[n] = ' + ';
			Note Manually add the string terminator to printf ("Connfd2:%s\n", BUFF2);
	}} else printf ("Time out\n");
} return 0;
 }

Client 1:

#include <cstdio>
#include <unistd.h>
#include <stdlib.h>
#include <cstring>
#include <cassert>
#include <sys/types.h>
#include <sys/socket.h>
#include < netinet/in.h>
#include <arpa/inet.h>

const int buffer_size = 4096;
const int server_port = 2222;

int main ()
{
	int client_socket;
	const char *SERVER_IP = "127.0.0.1";
	Char buffsend[buffer_size] = "I ' m from D.cpp";

	Client_socket = socket (af_inet, sock_stream, 0);
	ASSERT (Client_socket! =-1);

	struct sockaddr_in server_addr;
	memset (&server_addr, 0, sizeof (SERVER_ADDR));
	server_addr.sin_family = af_inet;
	Server_addr.sin_port = htons (server_port);
	SERVER_ADDR.SIN_ADDR.S_ADDR = inet_addr (server_ip);

	ASSERT (Connect (client_socket, (struct sockaddr *) &server_addr, sizeof (SERVER_ADDR))! =-1);
	
	while (1)
	{
		assert (send (Client_socket, Buffsend, strlen (Buffsend), 0)! =-1);
		Sleep (5);
	}
	Close (client_socket);

	return 0;
}


Client 2:

 #include <cstdio> #include <unistd.h> #include <stdlib.h> #include <cstring> #include <cassert> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #
Include <arpa/inet.h> const int buffer_size = 4096;

const int server_port = 2222;
	int main () {int client_socket;
	const char *SERVER_IP = "127.0.0.1";

	Char Buffsend[buffer_size];
	Client_socket = socket (af_inet, sock_stream, 0);

	ASSERT (Client_socket! =-1);
	struct sockaddr_in server_addr;
	memset (&server_addr, 0, sizeof (SERVER_ADDR));
	server_addr.sin_family = af_inet;
	Server_addr.sin_port = htons (Server_port);

	SERVER_ADDR.SIN_ADDR.S_ADDR = inet_addr (SERVER_IP);
	
	ASSERT (Connect (client_socket, (struct sockaddr *) &server_addr, sizeof (SERVER_ADDR))! =-1);
		while (1) {fgets (Buffsend, Buffer_size, stdin);
	ASSERT (Send (Client_socket, Buffsend, strlen (Buffsend), 0)! =-1);

	} close (Client_socket);
return 0; }

The above three parts of the code is defective, the code is not a good way to end, are the while (1) dead Loop, the end of the run need to use Ctrl + C ⊙﹏⊙




use of poll ()


The essence of Select () and poll () system calls , the former is introduced in BSD UNIX, which is introduced in System v. The mechanism of poll () is similar to select (), which is not substantially different from select (), that managing multiple descriptors is also polling and processing according to the state of the descriptor, but poll () does not have a limit on the maximum number of file descriptors (but the performance is also degraded if the number is too large). The disadvantage of poll () and select () is that an array containing a large number of file descriptors is copied in between the user state and the kernel's address space, regardless of whether the file descriptor is ready, and its overhead increases linearly as the number of file descriptors increases.


Required header file:

#include <poll.h>

Function Prototypes:

int poll (struct POLLFD *fds, nfds_t nfds, int timeout);

Function Description:

Monitors and waits for multiple file descriptor property changes.


Function parameters:

1) FDS: Unlike select () using three Fd_set, poll () is implemented using a POLLFD pointer. An array of POLLFD structures that includes the file descriptor and events that you want to monitor, which are determined by event domain events in the structure, and the events that actually occur after the call are filled in the revents domain of the struct.


struct pollfd{
int fd;         /* File descriptor */short
events;   /* Wait for events */short
revents;  /* Events that actually occurred */
};

_1, FD: Each pollfd struct specifies a monitored file descriptor that can pass multiple structures, indicating that poll () monitors multiple file descriptors.


_2, Events: The events field of each struct is the event mask that monitors the file descriptor, which is set by the user.


The _3, revents:revents domain is the action result event mask of the file descriptor, which is set by the kernel when the call returns. Any events requested in the events domain may be returned in the revents domain.


The mask value for the event is as follows:


Pollin | Pollpri equivalent to the Read event of select (), Pollout | Pollwrband is equivalent to the Write event of select (). Pollin equivalent to Pollrdnorm | Pollrdband, while pollout is equivalent to Pollwrnorm. For example, to monitor whether a file descriptor is readable and writable at the same time, we can set events to Pollin | Pollout.

The events domain of each struct is set by the user, telling the kernel what we are concerned about, and the revents domain is set at the time of return, to indicate what happened to the descriptor.

2) Nfds: used to specify the number of elements in the first parameter array.


3) Timeout: Specifies the number of milliseconds to wait, poll () will return regardless of whether I/O is ready.





function return value:

On success, poll () returns the number of file descriptors in the struct that do not have a revents field of 0, and if no events occur before the timeout, poll () returns 0;

On Failure, poll () returns-1.



Here we re-implement the above example with Poll (), and only modify the server-side code as follows:

#include <cstdio> #include <poll.h> #include <unistd.h> #include <stdlib.h> #include < cstring> #include <cassert> #include <sys/types.h> #include <sys/socket.h> #include <netinet/
in.h> #include <arpa/inet.h> const int buffer_size = 4096;

const int server_port = 2222;
	int main () {int server_socket;
	Char Buff1[buffer_size];
	Char Buff2[buffer_size];
	struct Timeval TV;
	int ret;

	int n;
	Server_socket = socket (af_inet, sock_stream, 0);

	ASSERT (Server_socket! =-1);
	struct sockaddr_in server_addr;
	memset (&server_addr, 0, sizeof (SERVER_ADDR));
	server_addr.sin_family = af_inet;
	Server_addr.sin_port = htons (Server_port);

	SERVER_ADDR.SIN_ADDR.S_ADDR = htonl (Inaddr_any);
	ASSERT (Bind (server_socket, (struct sockaddr *) &server_addr, sizeof (SERVER_ADDR))! =-1);
	
	ASSERT (Listen (Server_socket, 5)! =-1);
	struct sockaddr_in client_addr1, CLIENT_ADDR2;
	
	socklen_t client_addr_len = sizeof (struct sockaddr_in); Printf("waiting...\n");
	int connfd1 = Accept (Server_socket, (struct sockaddr*) &client_addr1, &client_addr_len);
	ASSERT (Connfd1! =-1);
	printf ("Connect from%s:%d\n", Inet_ntoa (CLIENT_ADDR1.SIN_ADDR), Ntohs (Client_addr1.sin_port));
	int connfd2 = Accept (Server_socket, (struct sockaddr*) &AMP;CLIENT_ADDR2, &client_addr_len);
	ASSERT (Connfd2! =-1);

	printf ("Connect from%s:%d\n", Inet_ntoa (CLIENT_ADDR2.SIN_ADDR), Ntohs (Client_addr2.sin_port));
	struct POLLFD rfds[2];
	RFDS[0].FD = CONNFD1;
	Rfds[0].events = Pollin;
	RFDS[1].FD = CONNFD2;
	Rfds[1].events = Pollin;
	Tv.tv_sec = 10;
	
	tv.tv_usec = 0;
		while (1) {printf ("poll...\n");
		
		RET = Poll (RFDS, 2,-1);
		if (ret = =-1) perror ("poll ()"); else if (Ret > 0) {if ((rfds[0].revents & pollin) = = Pollin) {n = recv (connfd1, buff1, buffer_size, 0
				);
				Buff1[n] = ' + ';
			printf ("Connfd1:%s\n", buff1); } if ((Rfds[1].revents & pollin) = = Pollin) {n = recv (connfd2, BUFF2, Buffer_size, 0);
				Buff2[n] = ' + ';
			printf ("Connfd2:%s\n", BUFF2);
	}} else printf ("Time out\n");
} return 0; }







use of Epoll ()


Epoll is presented in kernel 2.6, which is an enhanced version of the previous select () and poll (). Compared to select () and poll (), Epoll is more flexible and has no descriptor restrictions. Epoll uses a file descriptor to manage multiple descriptors, storing events of user-cared file descriptors in an event table in the kernel, so that only one time is required for copy in user space and kernel space.


The Epoll operation process requires three interfaces, respectively, as follows:

#include <sys/epoll.h>  
int epoll_create (int size);  
int epoll_ctl (int epfd, int op, int fd, struct epoll_event *event);  
int epoll_wait (int epfd, struct epoll_event * events, int maxevents, int timeout);  



int epoll_create (int size);

Function:

This function generates a Epoll-specific file descriptor.

Parameters:

Size: Used to tell the kernel how large the number of listeners is, the parameter size does not limit the maximum number of descriptors that epoll can listen to, just a suggestion for the kernel to initially allocate internal data structures. since Linux 2.6.8, the size parameter has been ignored, meaning that it can fill any value that is greater than 0 .


return value:
Success: Epoll dedicated file descriptor
Failed:-1




int epoll_ctl (int epfd, int op, int fd, struct epoll_event *event);

Function:

The event registration function of Epoll, unlike select (), is to tell the kernel what type of event to listen to when it listens to events, but to register the type of event to listen on first.

Parameters:

Epfd:epoll dedicated file descriptor, Epoll_create () return value

OP: Represents an action, represented by three macros:

Epoll_ctl_add: Register the new FD to EPFD;
Epoll_ctl_mod: Modify the monitoring events of the registered FD;
Epoll_ctl_del: Delete a fd from the EPFD;

FD: File descriptor to listen on

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.