The use of socket asynchronous programming--libevent

Source: Internet
Author: User
Tags epoll htons

This article introduces the application of Libevent in socket asynchronous programming. In some performance-demanding network applications, in order to prevent program blocking in the socket I/O operations cause program performance degradation, you need to use asynchronous programming, that is, the program ready to read and write functions (or interfaces) and register to the system, Then, when needed, only to submit read and write requests to the system to continue to do their own things, the actual read and write operations by the system at the appropriate time to call our program registration interface. Asynchronous programming brings some difficulty in understanding and writing to some of the program apes, because some of the simple programs that we usually write are executed sequentially, and asynchronous programming disrupts the execution order of the program, and some code execution is often not too clear, so the complexity of the programming is greatly increased.

Note: In this case, the use of the word system is not accurate, it can actually be the asynchronous call mechanism of their own package, more common is some of the available libraries, such as Libevent,ace

Want to know libevent work principle can self-query information, online related to a large pile, can also read the source code for analysis, this article only from the perspective of use to do a simple introduction, see how to quickly introduce libevent into our program. Any application will inevitably need to host its functionality of the underlying os,libevent is no exception, the internal is through the encapsulation operating system IO multiplexing mechanism, on the Linux system may be epoll, Kqueu, and so on, depending on the specific OS supported IO multiplexing mode, It is epoll on my system, so it can be understood that Libevent provides a more user-friendly interface than Epoll, freeing the program ape from the details of the network IO processing so that it can focus on the processing of the target problem.

First, install libevent to any directory

wget http://monkey.org/~provos/libevent-1.4.13-stable.tar.gz
TAR–XZVF libevent-1.4.13-stable.tar.gz
CD libevent-1.4.13-stable
./configure--prefix=/home/mydir/libevent
Make && make install

Now suppose we want to design a server program that receives data from the client and writes the received data back to the client. The program is constructed below, since this is just a demo, so the program will not handle the error, assuming all the calls are successful

2 #define PORT 25341
3 #define BACKLOG 5
4 #define MEM_SIZE 1024
5
6 struct event_base* base;
7
8 int main (int argc, char* argv[])
9 {
Ten struct sockaddr_in my_addr;
int sock;
12
Sock = socket (af_inet, sock_stream, 0);
int yes = 1;
setsockopt (sock, Sol_socket, so_reuseaddr, &yes, sizeof (int.));
memset (&my_addr, 0, sizeof (MY_ADDR));
my_addr.sin_family = af_inet;
My_addr.sin_port = htons (port);
MY_ADDR.SIN_ADDR.S_ADDR = Inaddr_any;
Bind (sock, (struct sockaddr*) &my_addr, sizeof (struct sockaddr));
Listen (sock, BACKLOG);
22
Listen_ev the event of the struct;
Base = Event_base_new ();
Event_set (&listen_ev, sock, ev_read| Ev_persist, On_accept, NULL);
Event_base_set (base, &listen_ev);
Event_add (&listen_ev, NULL);
Event_base_dispatch (base);
29
return 0;
31}

Line 13th indicates that a TCP socket was created. Line 15th is the usual practice of the server program, set this option, in the parent-child process model, when the child process for customer service, if the parent process exits, you can restart the program to complete the seamless upgrade of the service, or before all the parent and child processes completely exited before the program will be bound on the port failed. It is also impossible to complete the seamless upgrade (more information can refer to the function description or Mr. Steven's < Network programming >). Line 24th is used to create a global variable for event handling, which can be understood as a total butler responsible for centralizing the various ingress and egress IO events, which is responsible for receiving and distributing information about all input and output IO events, which is called function event_base_new (), which is used in many programs. _init (), the difference is that the former is thread-safe, and the latter is non-thread-safe, the latter in its official description has been marked as obsolete functions, and the former is suggested to replace, libevent there are many similar functions, such as the proposed Event_base_ Dispatch instead of Event_dispatch, with event_assign instead of Event_set and Event_base_set, the detailed description of the Libevent interface is shown in its official description libevent_doc. The 25th line shows that in Listen_en this event listens to the read operation of this descriptor sock, when the read message arrives is called the On_accept function, the Ev_persist parameter tells the system to continuously listen to the read event on the sock, if the parameter is not added, Each time you want to listen to this event, you need to repeatedly call the 26-row Event_add function, from the previous code, the sock this descriptor is bind to the local socket port, so its corresponding readable event is naturally from the client connection arrives, We can call the accept non-blocking return client connection. The 26th Line Listen_ev Register to base this event, the equivalent of telling the butler to handle IO Please note the events on my Listen_ev. The 27th line is equivalent to telling the steward of the processing IO that you sent me when my event arrives (call the On_accept function), so the initialization of the Listen_ev is complete. The 28th line formally launches the Libevent event processing mechanism, causes the system to run up, runs the program the word will discover Event_base_dispatch is an infinite loop.

The following is the contents of the On_accept function

   1:void on_accept (int sock, short event, void* Arg)
   2: {
   3:     struct sockaddr_in cli_addr;
   4:     int newfd, sin_size;
   5:     //Read_ev must allocate from heap memory, otherwise the program would crash from Segmant fault
   6:     struct event* Read_ev = (struct event*) malloc (sizeof (struct event));;
   7:     sin_size = sizeof (struct sockaddr_in);
   8:     newfd = Accept (sock, (struct sockaddr*) &cli_addr, &sin_size);
   9:     event_set (Read_ev, NEWFD, ev_read| Ev_persist, On_read, Read_ev);
  Ten:     event_base_set (base, Read_ev);
  One:     event_add (Read_ev, NULL);
  

第9-12 is the same as 24-26 of the previous main function, which is to listen for a readable event on behalf of the customer's descriptor newfd, when there is a data arrival that calls the On_read function. Here are some highlights to note, one is that Read_ev needs to be malloc out of the heap, if it is allocated on the stack, then when the function returns the memory occupied by the variable is freed, so the event main loop Event_base_dispatch accesses invalid memory and causes the process to crash (that is, crash The second note is that line 9th Read_ev passed as a parameter to the On_read function.

The following is the contents of the On_read function

   1:void On_read (int sock, short event, void* Arg)
   2: {
   3:     struct event* Write_ev;
   4:     int size;
   5:     char* buffer = (char*) malloc (mem_size);
   6:     bzero (buffer, mem_size);
   7:     size = recv (sock, buffer, mem_size, 0);
   8:     printf ("Receive data:%s, size:%d\n", buffer, size);
   9:     if (size = = 0) {
  Ten:         event_del (struct event*) arg;
  One: Free         (struct event*) arg;
  :         Close (sock);
  :         return;
  :     }
  :     Write_ev = (struct event*) malloc (sizeof (struct event));;
  :     Event_set (Write_ev, sock, Ev_write, on_write, buffer);
  :     Event_base_set (base, Write_ev);
  :     Event_add (Write_ev, NULL);
  19:}

Line 9th, when read from the socket returned 0 flag the other side has closed the connection, so this time there is no need to continue to listen to the event on the socket interface, because ev_read in the On_accept function is registered with the Ev_persist parameter, so the call to display Event_ The Del function cancels the listener for the event. The 第18-21 line is similar to the 6-11 rows of the On_accept function, and when writable, calls the On_write function, noting that the 19th row of buffer is passed to On_write as a parameter. This procedure has more serious problems, which are explained later.

Implementation of On_write function

1 void on_write (int sock, short event, void* Arg)
2 {
3 char* buffer = (char*) arg;
4 Send (sock, buffer, strlen (buffer), 0);
5
6 free (buffer);

7}

The On_write function writes back the data to the client and then releases the buffer from malloc in the On_read function. In many of the book-based programming guidance is to emphasize the ownership of resources, often ask who to allocate resources, who release resources, so that the management of resources to blame is more clear, not prone to problems, but through this example we found that in asynchronous programming, the allocation and release of resources are often by different owners operation, Therefore, it is also a relatively easy place to go wrong.

In fact, after reading the data from the socket in the On_read function, the program can directly call the Write/send interface to write back the data to the customer, because the write event has been satisfied, there is no async asynchronous problem, here to On_ The asynchronous operation of write is only to illustrate the problem of resource management and release in asynchronous programming, on the other hand, the direct call to the Write/send function to write data to the client may cause the program to block the IO operation for a long time, such as the socket output buffer is full, then write/ The send operation blocks until a buffer is available for the actual write operation, and by registering the On_accept function with the Write event, Libevent will invoke our callback function at the appropriate time ( For example, if the socket output buffer is full, it will be handled by the libevent design algorithm, so that when the callback on_accept function, we will not have real IO blocking when invoking the IO operation. Note: The front brackets are the functions that I personally think a library should implement, as to whether libevent is implementing such a function is unclear and does not intend to delve into it.

Then take a look at the problem in the On_read function mentioned earlier, first Write_ev is dynamically allocated memory, but not released, so there is a memory leak, in addition, the malloc operation in On_read, then when the function is called multiple times will cause memory multiple leaks. The workaround here is that the descriptor of the socket can encapsulate a struct to protect the read and write events as well as the data buffers, and the complete code is as follows

#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <stdio.h>

#include <event.h>


#define PORT 25341
#define BACKLOG 5
#define MEM_SIZE 1024

struct event_base* base;
struct Sock_ev {
struct event* Read_ev;
struct event* Write_ev;
char* buffer;
};

void release_sock_event (struct sock_ev* ev)
{
Event_del (Ev->read_ev);
Free (Ev->read_ev);
Free (Ev->write_ev);
Free (ev->buffer);
Free (EV);
}

void On_write (int sock, short event, void* Arg)
{
char* buffer = (char*) arg;
Send (sock, buffer, strlen (buffer), 0);

Free (buffer);
}

void On_read (int sock, short event, void* Arg)
{
struct event* Write_ev;
int size;
struct sock_ev* ev = (struct sock_ev*) arg;
Ev->buffer = (char*) malloc (mem_size);
Bzero (Ev->buffer, mem_size);
Size = recv (sock, Ev->buffer, mem_size, 0);
printf ("Receive data:%s, size:%d\n", ev->buffer, size);
if (size = = 0) {
Release_sock_event (EV);
Close (sock);
Return
}
Event_set (Ev->write_ev, sock, Ev_write, On_write, Ev->buffer);
Event_base_set (base, Ev->write_ev);
Event_add (Ev->write_ev, NULL);
}

void on_accept (int sock, short event, void* Arg)
{
struct sockaddr_in cli_addr;
int NEWFD, sin_size;
struct sock_ev* ev = (struct sock_ev*) malloc (sizeof (struct Sock_ev));
Ev->read_ev = (struct event*) malloc (sizeof (struct event));
Ev->write_ev = (struct event*) malloc (sizeof (struct event));
sin_size = sizeof (struct sockaddr_in);
NEWFD = Accept (sock, (struct sockaddr*) &cli_addr, &sin_size);
Event_set (Ev->read_ev, NEWFD, ev_read| Ev_persist, On_read, Ev);
Event_base_set (base, Ev->read_ev);
Event_add (Ev->read_ev, NULL);
}

int main (int argc, char* argv[])
{
struct sockaddr_in my_addr;
int sock;

Sock = socket (af_inet, sock_stream, 0);
int yes = 1;
setsockopt (sock, Sol_socket, so_reuseaddr, &yes, sizeof (int));
memset (&my_addr, 0, sizeof (MY_ADDR));
my_addr.sin_family = af_inet;
My_addr.sin_port = htons (port);
MY_ADDR.SIN_ADDR.S_ADDR = Inaddr_any;
Bind (sock, (struct sockaddr*) &my_addr, sizeof (struct sockaddr));
Listen (sock, BACKLOG);

struct event Listen_ev;
Base = Event_base_new ();
Event_set (&listen_ev, sock, ev_read| Ev_persist, On_accept, NULL);
Event_base_set (base, &listen_ev);
Event_add (&listen_ev, NULL);
Event_base_dispatch (base);

return 0;

}

When compiling the program, add the-levent connection option to connect to the Libevent shared library, but the following error is still made when executing: Error while loading shared Libraries:libevent-1.4.so.2:cannot Open Shared object File:no such file or directory, this is the location where the program cannot find the shared library, and by performing the Echo $LD _library_path can see that the system library environment variable does not have the path we installed. That is, the path developed by--prefix, execute export ld_library_path=/home/mydir/libevent/lib/: $LD _library_path Add the path to the system environment variable, and then execute the program.

Test results

http://blog.csdn.net/flyingleo1981/article/details/51833572

The use of socket asynchronous programming--libevent

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.