My favorites
▼
|
Sign in
ishbits
Bits of Ish's code.
Project Home
Downloads
Wiki
Issues
Source
Checkout
Browse
Changes
Source path:
svn
/
trunk
/
libevent-examples
/
echo-server
/
libevent_echosrv2.c
‹r36
r42
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
/*
* libevent echo server example.
*/
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
/* For inet_ntoa. */
#include <arpa/inet.h>
/* Required by event.h. */
#include <sys/time.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <errno.h>
#include <err.h>
/* Easy sensible linked lists. */
#include "queue.h"
/* Libevent. */
#include <event.h>
/* Port to listen on. */
#define SERVER_PORT 5555
/* Length of each buffer in the buffer queue. Also becomes the amount
* of data we try to read per call to read(2). */
#define BUFLEN 1024
/**
* In event based programming we need to queue up data to be written
* until we are told by libevent that we can write. This is a simple
* queue of buffers to be written implemented by a TAILQ from queue.h.
*/
struct bufferq {
/* The buffer. */
u_char *buf;
/* The length of buf. */
int len;
/* The offset into buf to start writing from. */
int offset;
/* For the linked list structure. */
TAILQ_ENTRY(bufferq) entries;
};
/**
* A struct for client specific data, also includes pointer to create
* a list of clients.
*
* In event based programming it is usually necessary to keep some
* sort of object per client for state information.
*/
struct client {
/* Events. We need 2 event structures, one for read event
* notification and the other for writing. */
struct event ev_read;
struct event ev_write;
/* This is the queue of data to be written to this client. As
* we can't call write(2) until libevent tells us the socket
* is ready for writing. */
TAILQ_HEAD(, bufferq) writeq;
};
/**
* Set a socket to non-blocking mode.
*/
int
setnonblock(int fd)
{
int flags;
flags = fcntl(fd, F_GETFL);
if (flags < 0)
return flags;
flags |= O_NONBLOCK;
if (fcntl(fd, F_SETFL, flags) < 0)
return -1;
return 0;
}
/**
* This function will be called by libevent when the client socket is
* ready for reading.
*/
void
on_read(int fd, short ev, void *arg)
{
struct client *client = (struct client *)arg;
struct bufferq *bufferq;
u_char *buf;
int len;
/* Because we are event based and need to be told when we can
* write, we have to malloc the read buffer and put it on the
* clients write queue. */
buf = malloc(BUFLEN);
if (buf == NULL)
err(1, "malloc failed");
len = read(fd, buf, BUFLEN);
if (len == 0) {
/* Client disconnected, remove the read event and the
* free the client structure. */
printf("Client disconnected.\n");
close(fd);
event_del(&client->ev_read);
free(client);
return;
}
else if (len < 0) {
/* Some other error occurred, close the socket, remove
* the event and free the client structure. */
printf("Socket failure, disconnecting client: %s",
strerror(errno));
close(fd);
event_del(&client->ev_read);
free(client);
return;
}
/* We can't just write the buffer back as we need to be told
* when we can write by libevent. Put the buffer on the
* client's write queue and schedule a write event. */
bufferq = calloc(1, sizeof(*bufferq));
if (bufferq == NULL)
err(1, "malloc faild");
bufferq->buf = buf;
bufferq->len = len;
bufferq->offset = 0;
TAILQ_INSERT_TAIL(&client->writeq, bufferq, entries);
/* Since we now have data that needs to be written back to the
* client, add a write event. */
event_add(&client->ev_write, NULL);
}
/**
* This function will be called by libevent when the client socket is
* ready for writing.
*/
void
on_write(int fd, short ev, void *arg)
{
struct client *client = (struct client *)arg;
struct bufferq *bufferq;
int len;
/* Pull the first item off of the write queue. We probably
* should never see an empty write queue, but make sure the
* item returned is not NULL. */
bufferq = TAILQ_FIRST(&client->writeq);
if (bufferq == NULL)
return;
/* Write the buffer. A portion of the buffer may have been
* written in a previous write, so only write the remaining
* bytes. */
len = bufferq->len - bufferq->offset;
len = write(fd, bufferq->buf + bufferq->offset,
bufferq->len - bufferq->offset);
if (len == -1) {
if (errno == EINTR || errno == EAGAIN) {
/* The write was interrupted by a signal or we
* were not able to write any data to it,
* reschedule and return. */
event_add(&client->ev_write, NULL);
return;
}
else {
/* Some other socket error occurred, exit. */
err(1, "write");
}
}
else if ((bufferq->offset + len) < bufferq->len) {
/* Not all the data was written, update the offset and
* reschedule the write event. */
bufferq->offset += len;
event_add(&client->ev_write, NULL);
return;
}
/* The data was completely written, remove the buffer from the
* write queue. */
TAILQ_REMOVE(&client->writeq, bufferq, entries);
free(bufferq->buf);
free(bufferq);
}
/**
* This function will be called by libevent when there is a connection
* ready to be accepted.
*/
void
on_accept(int fd, short ev, void *arg)
{
int client_fd;
struct sockaddr_in client_addr;
socklen_t client_len = sizeof(client_addr);
struct client *client;
/* Accept the new connection. */
client_fd = accept(fd, (struct sockaddr *)&client_addr, &client_len);
if (client_fd == -1) {
warn("accept failed");
return;
}
/* Set the client socket to non-blocking mode. */
if (setnonblock(client_fd) < 0)
warn("failed to set client socket non-blocking");
/* We've accepted a new client, allocate a client object to
* maintain the state of this client. */
client = calloc(1, sizeof(*client));
if (client == NULL)
err(1, "malloc failed");
/* Setup the read event, libevent will call on_read() whenever
* the clients socket becomes read ready. We also make the
* read event persistent so we don't have to re-add after each
* read. */
event_set(&client->ev_read, client_fd, EV_READ|EV_PERSIST, on_read,
client);
/* Setting up the event does not activate, add the event so it
* becomes active. */
event_add(&client->ev_read, NULL);
/* Create the write event, but don't add it until we have
* something to write. */
event_set(&client->ev_write, client_fd, EV_WRITE, on_write, client);
/* Initialize the clients write queue. */
TAILQ_INIT(&client->writeq);
printf("Accepted connection from %s\n",
inet_ntoa(client_addr.sin_addr));
}
int
main(int argc, char **argv)
{
int listen_fd;
struct sockaddr_in listen_addr;
int reuseaddr_on = 1;
/* The socket accept event. */
struct event ev_accept;
/* Initialize libevent. */
event_init();
/* Create our listening socket. This is largely boiler plate
* code that I'll abstract away in the future. */
listen_fd = socket(AF_INET, SOCK_STREAM, 0);
if (listen_fd < 0)
err(1, "listen failed");
if (setsockopt(listen_fd, SOL_SOCKET, SO_REUSEADDR, &reuseaddr_on,
sizeof(reuseaddr_on)) == -1)
err(1, "setsockopt failed");
memset(&listen_addr, 0, sizeof(listen_addr));
listen_addr.sin_family = AF_INET;
listen_addr.sin_addr.s_addr = INADDR_ANY;
listen_addr.sin_port = htons(SERVER_PORT);
if (bind(listen_fd, (struct sockaddr *)&listen_addr,
sizeof(listen_addr)) < 0)
err(1, "bind failed");
if (listen(listen_fd, 5) < 0)
err(1, "listen failed");
/* Set the socket to non-blocking, this is essential in event
* based programming with libevent. */
if (setnonblock(listen_fd) < 0)
err(1, "failed to set server socket to non-blocking");
/* We now have a listening socket, we create a read event to
* be notified when a client connects. */
event_set(&ev_accept, listen_fd, EV_READ|EV_PERSIST, on_accept, NULL);
event_add(&ev_accept, NULL);
/* Start the libevent event loop. */
event_dispatch();
return 0;
}
Show details
Hide details
Change log
r37
by jason.ish on Jul 21, 2011
Diff
Silly renames.
Go to:
...nt-examples/buffered-echo-server
...les/buffered-echo-server-example
...nk/libevent-examples/echo-server
...ent-examples/echo-server-example
Project members,
sign in
to write a code review
Older revisions
r35
by jason.ish on Mar 6, 2011
Diff
Another rename.
r32
by jason.ish on Mar 6, 2011
Diff
Directory re-org.
r6
by jason.ish on Aug 31, 2006
Diff
rename
All revisions of this file
File info
Size: 7736 bytes, 296 lines
View raw file
Powered by
Google Project Hosting