Network Programming Using libevent-(I)
After learning UNIX network programming in zookeeper, we know that there will be several solutions when dealing with multiple users:
- A new connection.
fork()
Produce a process processing.
- A new connection.
pthread_create()
Produce a thread processing.
- A new connection enters event-based array, and the main process processes all I/O in nonblocking mode.
These three methods all have their own shortcomings:
- Use
fork()
The cost of each connection is too high.
- The problem of using multi-thread is solved by thread-safe and deadlock, and the problem of memory-leak must be handled.
- The event-based method is difficult to implement in practice, especially when you notice that the event is generated, nonblocking is required, and buffering needs to be implemented, the memory-leak problem that multi-thread will encounter will be more serious here. In a multi-CPU system, there is no way to use all CPU resources.
Of course, they have their own solutions for the first two parties:
- Solution in poll mode: when a process processes a connection, it does not directly die, but zookeeper returns to the zookeeper processing of accept, however, in this case, the memory-leak problem occurs. For example, a person who uses this method usually adds "after processing n connections, it will die again by the parent process ."
fork()
One click New 」. The most famous example is Apache 1.3.
- Thread-safe issues can be written by yourself, or used directly by other thread-safe libraries. The memory-leak issue can be analyzed through the garbage collection library. This mode is used for thread MPM of Apache 2.0.
However, currently, highly efficient servers prefer event-based. On the one hand, there is no overhead caused by create process/thread, on the other hand, you do not need to pass through shared memory or mutex to submit data between different processes/threads.
However, the event-based Real-time operations are as follows:
select()
Andpoll()
The efficiency is too slow, resulting in a high cost of determining "What events are generated" each time. This is supported by BSD.kqueue()
Linux supportepoll()
And Solaris support/dev/poll
But neither of these functions is standard. Therefore, it is necessary to change them on different platforms.
- Because it is nonblocking
write()
Orsend()
When the time is reached, buffering is required.
- It cannot be used because it is nonblocking.
fgets()
Or other similar functions, so you need to engrave a nonblocking on your own.fgets()
. However, the information received by the user cannot be recorded once.read()
Orrecv()
There is only one line, so you have to do buffering yourself.
In fact, these three things are handled by the libevent library.
In addition, it is worth noting that libevent uses 3-clause BSD license instead of GPL, so it does not want to open a program license (such as for commercial purposes) and other libraries. Network Programming Using libevent-(II)
Next, we will explain how to use libevent. For convenience, we will directly compile a very simple time server as an example: when you attempt to provide the time directly on the server end, the server end will end up waiting.
In these examples, I use FreeBSD 6.0 as the zookeeper platform, and libevent 1.1a as the event-based library and compile.gcc -I/usr/local/include -o timeserver timeserver.c -L/usr/local/lib -levent
(If the libevent header and library are placed in/usr/include
And/usr/lib
Can omit these two numbers ).
The original program comes at the end of the article.
event_init()
Represents the number of changes used to initialize libevent.
event_set(&ev, s, EV_READ | EV_PERSIST, connection_accept, &ev)
Sets
Put this file descriptionev
(The first and second data records), and inform the event (the third dataEV_READ
) Call at the time of productionconnection_accept()
(Fourth Region ).ev
When the data volume goes in (the fifth data volume ).
TheEV_PERSIST
This indicates that when the call is in, do not remove this event (the upload queue is retained in the event queue ).connection_accept()
Internalconnection_time()
Program comparison.
Whileevent_add(&ev, NULL)
Isev
Wait until the event queue is reached. The second parameter specifies the timeout time and is setNULL
This parameter is ignored.
Lastevent_dispatch()
Indicates that the event loop is input. When any file description in the queue generates an event, the callback function will be executed.
This program is very rough, and we don't care about blocking in many places. When it starts, you can redirect to port 7000, and the following result appears: gslin @ NetNews [~] [3:14/W5] T 0 7000
gslin@netnews [~/work/C] [3:15/W3] t 0 7000
Trying 0.0.0.0...
Connected to 0.
Escape character is '^]'.
Fri Nov 25 03:15:10 2005
Connection closed by foreign host.
This is the most basic use. You canman event
See the complete description.
This istimeserver.c
:
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <event.h>
#include <stdio.h>
#include <time.h>
void connection_time(int fd, short event, struct event *arg)
{
char buf[32];
struct tm t;
time_t now;
time(&now);
localtime_r(&now, &t);
asctime_r(&t, buf);
write(fd, buf, strlen(buf));
shutdown(fd, SHUT_RDWR);
free(arg);
}
void connection_accept(int fd, short event, void *arg)
{
/* for debugging */
fprintf(stderr, "%s(): fd = %d, event = %d./n", __func__, fd, event);
/* Accept a new connection. */
struct sockaddr_in s_in;
socklen_t len = sizeof(s_in);
int ns = accept(fd, (struct sockaddr *) &s_in, &len);
if (ns < 0) {
perror("accept");
return;
}
/* Install time server. */
struct event *ev = malloc(sizeof(struct event));
event_set(ev, ns, EV_WRITE, (void *) connection_time, ev);
event_add(ev, NULL);
}
int main(void)
{
/* Request socket. */
int s = socket(PF_INET, SOCK_STREAM, 0);
if (s < 0) {
perror("socket");
exit(1);
}
/* bind() */
struct sockaddr_in s_in;
bzero(&s_in, sizeof(s_in));
s_in.sin_family = AF_INET;
s_in.sin_port = htons(7000);
s_in.sin_addr.s_addr = INADDR_ANY;
if (bind(s, (struct sockaddr *) &s_in, sizeof(s_in)) < 0) {
perror("bind");
exit(1);
}
/* listen() */
if (listen(s, 5) < 0) {
perror("listen");
exit(1);
}
/* Initial libevent. */
event_init();
/* Create event. */
struct event ev;
event_set(&ev, s, EV_READ | EV_PERSIST, connection_accept, &ev);
/* Add event. */
event_add(&ev, NULL);
event_dispatch();
return 0;
}
Network Programming Using libevent-(III)
There is no direct relationship between this secondary failover and network programming.
In batch nonblocking Network Program, buffering is usually handled, but it is not easy to understand, mainly becauseread()
Orrecv()
Without a security guarantee, you can increase the size of a row at a time.
The buffer library provided in the libevent can be used. The complete description is as follows:man event
You can see that the most common application isevbuffer_add()
,evbuffer_readline()
The other functions can be used if they exist. When necessary, let's look at the usage of these functions.
Directly providelibevent-buff.c
As an example, check the result of the response and check the source code again:
#include <sys/time.h>
#include <event.h>
#include <stdio.h>
void printbuf(struct evbuffer *evbuf)
{
for (;;) {
char *buf = evbuffer_readline(evbuf);
printf("* buf = %p, the string = /"/e[1;33m%s/e[m/"/n", buf, buf);
if (buf == NULL)
break;
free(buf);
}
}
int main(void)
{
struct evbuffer *evbuf;
evbuf = evbuffer_new();
if (evbuf == NULL) {
fprintf(stderr, "%s(): evbuffer_new() failed./n", __func__);
exit(1);
}
/* Add "gslin" into buffer. */
u_char *buf1 = "gslin";
printf("* Add /"/e[1;33m%s/e[m/"./n", buf1);
evbuffer_add(evbuf, buf1, strlen(buf1));
printbuf(evbuf);
u_char *buf2 = " is reading./nAnd he is at home./nLast.";
printf("* Add /"/e[1;33m%s/e[m/"./n", buf2);
evbuffer_add(evbuf, buf2, strlen(buf2));
printbuf(evbuf);
evbuffer_free(evbuf);
}
Content Source: http://www.cublog.cn/u/5251/showart_270584.html
By the way recommended reference: http://unx.ca/log/category/libevent/