©min technology sharing –54min.com (RSS feed) | Original link: http://54min.com/post/using-epoll-method-create-non-blocking-socket.html
Epoll method to implement non-blocking socket
event-based method and Epoll
Epoll is the Event-based method for implementing asynchronous Io/non-blocking IO. After the Linux kernel 2.5.44 epoll joined the Linux kernel, select and poll, instead of the loop style method, are more efficient and more applicable to high concurrent multiple-client applications than the latter. The loop style method has a time complexity of O (n) (because the specified file descriptor needs to be detected linearly), while the time complexity of event-based methods such as Epoll is O (1). Event-based by setting the callback function for different events, the corresponding function is automatically executed when the event occurs (Epoll uses callbacks in the kernel file structure).
Other event-based methods similar to Epoll are: Kqueue (Freebsd/netbsd/openbsd/darwin),/dev/poll (Solaris/hpux), Pollset (AIX), Event Completion (Solaris), I/O completion Ports (Windows), and so on. So if the program is running on the target platform for Linux (Kernel > 2.5.44) You can use Epoll, if other UNIX platforms can consider the appropriate approach (such as using Kqueue on Unix such as FreeBSD). If you want to use a cross-platform, portable event-based, you can use the Libevent library, which supports a variety of methods (Select/poll, Epoll, Kqueue,/dev/poll, and so on). system calls provided by Epoll
Epoll contains 3 system calls: Epoll_create, Epoll_ctl and epoll_wait (#include <sys/epoll.h>). The next step is to create a Epoll file descriptor using Epoll_create, and then use Epoll_ctl to add the IO file descriptor to listen to Epoll, and the last loop to call Epoll_ Wait to detect the changes in the events related to IO fd, and then take appropriate measures. epoll_create
int epoll_create (int size); Create Epoll
Where size tells kernel the size of the event backing store that needs to be prepared for the IO file descriptor added later. Open an Epoll file descriptor by requesting the kernel allocate a event backing store dimensioned for size descriptors. The size is isn't the maximum size of the backing store but just a hint to the kernel about I to dimension internal Ures.
The size value is no longer used from the Linux 2.6.8, but its assignment requires > 0. Reference: http://www.kernel.org/doc/man-pages/online/pages/man2/epoll_create.2.html
The Epoll file descriptor created with Epoll_create needs to be closed at the end of the program by using Close (), for example:
int EPFD;
if ((EPFD = epoll_create (1)) = = 1) {
perror ("Epoll_create failed");
Exit (exit_failure);
}
...
Close (EPFD);
Epoll_ctl
int epoll_ctl (int epfd, int op, int fd, struct epoll_event *event); Add/update/del IO file descriptor
to is watched on the Epoll instance//EPFD: That is epoll_create file Epoll created above Descrip Tor
//op: specifies what action to take on the specified FD, including the following:
epoll_ctl_add Add the specified file descriptor to the Epoll
epoll_ctl_mod modifies the event of the specified file descriptor, equivalent to update
Epoll_ctl_del clears the specified file descriptor from the Epoll, the event is ignored and can NULL
//fd: io file to manipulate Descriptor
//event: Represents the event linked to this file descriptor
The definition of struct epoll_event is as follows:
struct Epoll_event {
__uint32_t events;//epoll Events
epoll_data_t data; User data variable
};
typedef Union EPOLL_DATA {
void *ptr;
int FD;
__uint32_t u32;
__uint64_t u64;
} epoll_data_t;
The events field of the struct epoll_event is a bit set (which can be assigned by the | operator), and the supported event type is:
Epollin //ready for read
epollout //ready to write
epollpri //urgent data available for read
epollerr //error condition happened, Epoll_wait detects this event by default does not need to be set epollhup//hang up happended on The FD, epoll_wait will default to detect the event without setting
Epollet //settings using the edge triggered mode, Epoll mode using level triggered
See more: Http://linux.die.net/man/2/epoll_ctl
Epoll_ctl return value: Successfully returned 0, error returned-1 epoll_wait
int epoll_wait (int epfd, struct epoll_event *events, int maxevents, int timeout);
EPFD for Epoll file descriptor//events, as defined
above , returns the number of events//maxevents the event that has changed
, must > 0
/ /timeout wait for the milliseconds; similar to poll, if timeout is set to-1 epoll_wait will continue to wait; If the timeout is set to 0, Epoll_wait will return immediately
The function is similar to the Select and poll functions, and waits until the events of the specified IO fd defined by EPFD are changed or the milliseconds time specified by the timeout parameter expires before returning. The wait and blocks until events on the watched set happens or timeout expires.
Return value: Successful return of number of FD ready for requested IO; 0 indicates that there is no ready FD after timeout; -1 indicates an error occurred. Epoll detects events change in two modes: edge-triggered and level-triggered
Calling Epoll_wait will return the events that changed the IO fd,epoll support two modes: level triggered:
As long as the events that occurred did not end, each call to Epoll_wait shows that the events exist. For example, when the state of an IO FD becomes available for reading, the call to Epoll_wait returns the event, and if the read process is not completed the next time the epoll_wait is called, the Epoll_ Wait will still return the event. Edge triggered (Epollet)
Unlike level triggered, it emits event information only when event is generated, and then no longer sends this message even if the event does not end. For example, when an IO fd state changes to available for reading, the call Epoll_wait returns the event, and if the read process is not completed the next time the epoll_wait is called, Epoll_ Wait does not return immediately but waits for the new events or until the timetout time. Edge triggered event distribution delivers events only if events happens on the monitored file.
Epoll defaults to the level triggered mode, if you need to use the edge triggered mode for an IO fd, add struct when calling Epoll_ctl to specify its epoll_event epollet events. Epoll Implementation non-blocking Socket instance:
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/types.h> #include <sys/socket.h> #include <unistd.h> #include <fcntl.h> #include <arpa/inet.h&
Gt #include <sys/epoll.h> #include <errno.h> #define DEFAULT_PORT 1984//default port #define Buff_siz E 1024//buffer size #define EPOLL_MAXEVENTS The maximum number of events returned by//epoll_wait #define EPOLL_TIMEOUT 500 0//epoll_wait Timeout milliseconds//function: Set sock to non-blocking mode void Setsocknonblock (int sock) {int
Flags
Flags = FCNTL (sock, F_GETFL, 0);
if (Flags < 0) {perror ("Fcntl (F_GETFL) failed");
Exit (Exit_failure); if (Fcntl (sock, F_SETFL, flags |
O_nonblock) < 0) {perror ("Fcntl (F_SETFL) failed");
Exit (Exit_failure);
int main (int argc, char *argv[]) {//Get custom port unsigned short int port;
if (argc = = 2) {port = atoi (argv[1]);
else if (ARGC < 2) {port = Default_port;
else {fprintf (stderr, "USAGE:%s [port]\n", argv[0]);
Exit (Exit_failure);
//create socket int sock;
if ((sock = socket (pf_inet, sock_stream, 0)) = = 1) {perror ("socket failed");
Exit (Exit_failure);
printf ("Socket done\n");
In the case of the ' address already the ' error message int yes = 1;
if (setsockopt (sock, Sol_socket, so_reuseaddr, &yes, sizeof (int))) {perror ("setsockopt failed");
Exit (Exit_failure);
//Set Sock to Non-blocking Setsocknonblock (sock);
Create the socket address struct sockaddr_in bind_addr to bind;
memset (&bind_addr, 0, sizeof (BIND_ADDR));
bind_addr.sin_family = af_inet; BIND_ADDR.SIN_ADDR.S_ADDR = hTonl (Inaddr_any); Set to accept arbitrary address Bind_addr.sin_port = htons (port); Converts the host byte order to the network byte//bind sock to the created socket address if (bind sock, struct sockaddr *
&BIND_ADDR, sizeof (bind_addr)) = = 1) {perror ("bind failed");
Exit (Exit_failure);
printf ("Bind done\n");
Listen if (Listen (sock, 5) = = 1) {perror ("Listen Failed");
Exit (Exit_failure);
printf ("Listen done\n");
Create Epoll (epoll file descriptor) int EPFD;
if ((EPFD = epoll_create (1)) = = 1) {perror ("Epoll_create failed");
Exit (Exit_failure);
///Add sock to epoll struct epoll_event event;
Event.events = Epollin;
EVENT.DATA.FD = sock;
if (Epoll_ctl (EPFD, Epoll_ctl_add, sock, &event) = = 1) {perror ("Epoll_ctl");
Exit (Exit_failure);
} Initialize the parameters of epoll_wait struct epoll_event events[epoll_maxevents];
memset (events, 0, sizeof (events));
Circular listening int conn_sock;
struct sockaddr_in client_addr;
Socklen_t Client_addr_len;
Char Client_ip_str[inet_addrstrlen];
int res;
int i;
Char Buffer[buff_size];
int recv_size; while (1) {//Each loop call epoll_wait listening res = epoll_wait (EPFD, events, epoll_maxevents, Epoll_timeout)
;
if (Res < 0) {perror ("epoll_wait failed");
Exit (Exit_failure);
else if (res = = 0) {fprintf (stderr, "no socket ready for read within%d secs\n", epoll_timeout/1000);
Continue
(i = 0; i < res; i++) {descriptor)//detects the Events,loop fd for res io file. Events[i] is the detected event, the domain events[i].events represents which events are specific, and the domain EVENTS[I].DATA.FD is the corresponding IO fd iF ((Events[i].events & Epollerr) | |
(Events[i].events & epollhup) | | (! (Events[i].events & Epollin)) {//Because Events[i].events uses each bit to represent an event, it is possible to determine whether a specific event is included using the ' & ' operator//here to determine whether there is a Epol
Lerr, Epollhup and other event fprintf (stderr, "Epoll error\n");
Close (EVENTS[I].DATA.FD);
Continue ///Response to IO FD detected event if (EVENTS[I].DATA.FD = = sock) {//current FD is Ser Ver socket, not read but accept all client connection requests while (1) {Client_addr_len = sizeof (clien
T_ADDR);
Conn_sock = Accept (sock, (struct sockaddr *) &client_addr, &client_addr_len); if (Conn_sock = = 1) {if (errno = = Eagain) | | (errno = = Ewouldblock)) {No new connection in//non-blocking modeRequest, jump out while (1) break;
else {perror ("Accept failed");
Exit (Exit_failure); } if (!inet_ntop (Af_inet, & (CLIENT_ADDR.SIN_ADDR), Client_ip_str, si
Zeof (CLIENT_IP_STR))) {perror ("Inet_ntop failed");
Exit (Exit_failure);
printf ("Accept a client from:%s\n", CLIENT_IP_STR);
Set Conn_sock as Non-blocking setsocknonblock (Conn_sock);
Add Conn_sock to Epoll listening event.events = Epollin;
EVENT.DATA.FD = Conn_sock; if (Epoll_ctl (EPFD, Epoll_ctl_add, Conn_sock, &event) = = 1) {perror ("Epoll_ctl" Epoll_c
Tl_add) failed "); Exit (Exit_failure);
}} else {//current FD is a client-connected socket that can be read (read from client)
Conn_sock = EVENTS[I].DATA.FD;
memset (buffer, 0, sizeof (buffer));
if ((Recv_size = recv (conn_sock, buffer, sizeof (buffer), 0)) = = 1 && (errno!= eagain)) {
Recv in non-blocking mode, returns-1 and errno for eagain indicates that no readable data is currently available, and does not represent an error perror ("Recv failed");
Exit (Exit_failure);
printf ("Recved from conn_sock=%d:%s (%d length string) \ n", Conn_sock, buffer, recv_size); Immediately write back the received content if (send (conn_sock, buffer, recv_size, 0) = = 1 && (errno!= Eagai N) && (errno!= ewouldblock)) {//send in non-blocking mode, returns-1 and errno to Eagain or ewouldblock indicates that none of the current
Writable data, does not represent an error Perror ("Send Failed");
Exit (Exit_failure);
printf ("Send to conn_sock=%d done\n", Conn_sock); Remove the current socket from Epoll listening (the article says: After closing con_sock, it is automatically removed from Epoll, so this code can omit) if (Epoll_ctl EPFD, Epoll_ctl_del
, Conn_sock, NULL) = = 1) {perror ("Epoll_ctl (Epoll_ctl_del) failed");
Exit (Exit_failure); //Turn off connection if (Close (conn_sock) = = 1) {perror ("closed FA
Iled ");
Exit (Exit_failure);
printf ("Close conn_sock=%d done\n", Conn_sock); }} close (sock); Closes the server's listening socket close (EPFD);
Close Epoll file descriptor return 0;
}
Test: Compile and run the program, and then try to run multiple telnet localhost 1984 and server for communication.
Note: When Epoll uses Epoll_ctl to specify events for file descriptor, the default level triggered is used, that is, if events do not complete the call to epoll_wait each time the event is returned, as follows:
struct Epoll_event event;
EVENT.DATA.FD = sock;
Event.events = Epollin | Epollet;
if (Epoll_ctl (EPFD, Epoll_ctl_add, sock, &event) = = 1) {
perror ("Epoll_ctl (Epoll_ctl_add) failed");
Exit (exit_failure);
}
You can specify that the event of the FD uses the edge triggered model, and if the model is used, Epoll_wait detects each event change only once, and therefore needs attention after epoll_wait processing (for example, when there is a readable event), Note that the data is read complete). Refer to the code described in the article.