Created
February 17, 2010 05:31
-
-
Save yinyin/306332 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* The ROT13 server example code from LibEvent-Book written by Nick Mathewson. | |
* http://github.com/nmathewson/libevent-book | |
* | |
* The code is modified for my practice. | |
* compiling w/: gcc `pkg-config libevent --cflags --libs` rot13-server.c | |
*/ | |
/* For sockaddr_in */ | |
#include <netinet/in.h> | |
/* For socket functions */ | |
#include <sys/socket.h> | |
/* For fcntl */ | |
#include <fcntl.h> | |
#include <event2/event.h> | |
#include <event2/buffer.h> | |
#include <event2/bufferevent.h> | |
#include <assert.h> | |
#include <unistd.h> | |
#include <string.h> | |
#include <stdlib.h> | |
#include <stdio.h> | |
#include <errno.h> | |
#define MAX_CHAR_PER_LINE 8 | |
void do_read(evutil_socket_t fd, short events, void *arg); | |
void do_write(evutil_socket_t fd, short events, void *arg); | |
char | |
rot13_char(char c) | |
{ | |
/* We don't want to use isalpha here; setting the locale would change | |
* which characters are considered alphabetical. */ | |
if ((c >= 'a' && c <= 'm') || (c >= 'A' && c <= 'M')) | |
return c + 13; | |
else if ((c >= 'n' && c <= 'z') || (c >= 'N' && c <= 'Z')) | |
return c - 13; | |
else | |
return c; | |
} | |
void | |
readcb(struct bufferevent *bev, void *ctx) | |
{ | |
struct evbuffer *input, *output; | |
size_t n; | |
int i; | |
/* the bufferevent_get_ operations shall not failed since if such | |
* evbuffer struct not existed the bufferevent_init operation will | |
* fail. */ | |
input = bufferevent_get_input(bev); | |
output = bufferevent_get_output(bev); | |
{ | |
char *line; | |
while ((line = evbuffer_readln(input, &n, EVBUFFER_EOL_LF))) { | |
for (i = 0; i < n; ++i) | |
line[i] = rot13_char(line[i]); | |
if( 0 != evbuffer_add(output, line, n) ) | |
{ | |
fprintf(stderr, "ERR: Failed on append data into output buffer. @(%s:%d)\n", __FILE__, __LINE__); | |
} | |
if( 0 != evbuffer_add(output, "\n", 1) ) | |
{ | |
fprintf(stderr, "ERR: Failed on append data into output buffer. @(%s:%d)\n", __FILE__, __LINE__); | |
} | |
free(line); | |
} | |
} | |
if( evbuffer_get_length(input) >= MAX_CHAR_PER_LINE ) | |
{ | |
/* Too long; just process what there is and go on so that the buffer | |
* doesn't grow infinitely long. */ | |
char buf[1024]; | |
while (evbuffer_get_length(input)) { | |
int n = evbuffer_remove(input, buf, sizeof(buf)); | |
for (i = 0; i < n; ++i) { | |
buf[i] = rot13_char(buf[i]); | |
} | |
if( 0 != evbuffer_add(output, buf, n) ) | |
{ | |
fprintf(stderr, "ERR: Failed on append data into output buffer. @(%s:%d)\n", __FILE__, __LINE__); | |
} | |
} | |
if( 0 != evbuffer_add(output, "\n", 1) ) | |
{ | |
fprintf(stderr, "ERR: Failed on append data into output buffer. @(%s:%d)\n", __FILE__, __LINE__); | |
} | |
} | |
} | |
void | |
errorcb(struct bufferevent *bev, short error, void *ctx) | |
{ | |
if (error & BEV_EVENT_EOF) { | |
/* connection has been closed, do any clean up here */ | |
/* ... */ | |
fprintf(stderr, "ERR: (CB) BEV_EVENT_EOF @(%s:%d)\n", __FILE__, __LINE__); | |
} else if (error & BEV_EVENT_ERROR) { | |
/* check errno to see what error occurred */ | |
/* ... */ | |
fprintf(stderr, "ERR: (CB) BEV_EVENT_ERROR @(%s:%d)\n", __FILE__, __LINE__); | |
} else if (error & BEV_EVENT_TIMEOUT) { | |
/* must be a timeout event handle, handle it */ | |
/* ... */ | |
fprintf(stderr, "ERR: (CB) BEV_EVENT_TIMEOUT @(%s:%d)\n", __FILE__, __LINE__); | |
} | |
else { | |
fprintf(stderr, "ERR: (CB) UNEXPECTED error=0x%X @(%s:%d)\n", (int)(error), __FILE__, __LINE__); | |
} | |
bufferevent_free(bev); /* file descriptor would/shall be close since we set BEV_OPT_CLOSE_ON_FREE on create bufferevent */ | |
} | |
void | |
do_accept(evutil_socket_t listener, short event, void *arg) | |
{ | |
struct event_base *base = arg; | |
struct sockaddr_storage ss; | |
socklen_t slen; | |
int fd; | |
slen = sizeof(ss); | |
fd = accept(listener, (struct sockaddr*)&ss, &slen); | |
if( fd < 0 ) | |
{ | |
fprintf(stderr, "ERR: Failed on accept(). @(%s:%d) ERRNO=[%d, %s]\n", __FILE__, __LINE__, errno, strerror(errno)); | |
return; | |
} | |
else if( fd > FD_SETSIZE ) | |
{ | |
fprintf(stderr, "ERR: FD larger than FD_SETSIZE=%d, drop connection. @(%s:%d)\n", (FD_SETSIZE), __FILE__, __LINE__); | |
close(fd); | |
return; | |
} | |
else | |
{ | |
struct bufferevent *bev; | |
/* {{{ make socket non-blocking */ | |
if( 0 != evutil_make_socket_nonblocking(fd) ) | |
{ | |
fprintf(stderr, "ERR: Cannot make client socket non-blocking. @(%s:%d)\n", __FILE__, __LINE__); | |
close(fd); | |
return; | |
} | |
/* }}} make socket non-blocking */ | |
if( NULL == (bev = bufferevent_socket_new(base, fd, BEV_OPT_CLOSE_ON_FREE)) ) | |
{ | |
fprintf(stderr, "ERR: Cannot allocate bufferevent for client socket FD. @(%s:%d)\n", __FILE__, __LINE__); | |
close(fd); | |
return; | |
} | |
bufferevent_setcb(bev, readcb, NULL, errorcb, NULL); | |
bufferevent_setwatermark(bev, EV_READ, 0, MAX_CHAR_PER_LINE); /* readcb would be invoke when more than 0 bytes are arrived, read/buffer-filling stops when MAX_CHAR_PER_LINE bytes in buffer */ | |
bufferevent_enable(bev, EV_READ|EV_WRITE); | |
} | |
return; | |
} | |
void | |
run(void) | |
{ | |
evutil_socket_t listener; | |
struct sockaddr_in sin; | |
struct event_base *base; | |
struct event *listener_event; | |
if( NULL == (base = event_base_new()) ) | |
{ | |
fprintf(stderr, "ERR: Cannot setup default event_base. @(%s:%d)\n", __FILE__, __LINE__); | |
return; | |
} | |
else | |
{ | |
enum event_method_feature f; | |
fprintf(stderr, "INFO: LibEvent2 backend=%s [", event_base_get_method(base)); | |
f = event_base_get_features(base); | |
if( 0 != (f & EV_FEATURE_ET) ) | |
{ fprintf(stderr, "Edge-triggered Events; "); } | |
if( 0 != (f & EV_FEATURE_O1) ) | |
{ fprintf(stderr, "O(1) Event Notification; "); } | |
if( 0 != (f & EV_FEATURE_FDS) ) | |
{ fprintf(stderr, "All FD Types; "); } | |
fprintf(stderr, "] @(%s:%d)\n", __FILE__, __LINE__); | |
} | |
sin.sin_family = AF_INET; | |
sin.sin_addr.s_addr = INADDR_ANY; | |
sin.sin_port = htons(10000); | |
/* {{{ get socket file descriptor for server socket */ | |
{ | |
if( -1 == (listener = socket(AF_INET, SOCK_STREAM, 0)) ) | |
{ | |
perror("Cannot allocate server socket.\n"); | |
return; | |
} | |
} | |
/* }}} get socket file descriptor for server socket */ | |
/* {{{ make socket non-blocking */ | |
if( 0 != evutil_make_socket_nonblocking(listener) ) | |
{ | |
fprintf(stderr, "ERR: Cannot make server socket non-blocking. @(%s:%d)\n", __FILE__, __LINE__); | |
return; | |
} | |
/* }}} make socket non-blocking */ | |
/* {{{ set server socket option */ | |
{ | |
int one = 1; | |
if( 0 != setsockopt(listener, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)) ) | |
{ | |
perror("Cannot set socket option SO_REUSEADDR.\n"); | |
return; | |
} | |
} | |
/* }}} set server socket option */ | |
if( 0 != bind(listener, (struct sockaddr*)(&sin), sizeof(sin)) ) | |
{ | |
perror("bind"); | |
return; | |
} | |
if( 0 != listen(listener, 16) ) | |
{ | |
perror("listen"); | |
return; | |
} | |
if( NULL == (listener_event = event_new(base, listener, EV_READ|EV_PERSIST, do_accept, (void*)base)) ) | |
{ | |
fprintf(stderr, "ERR: Failed on event_new(), insufficient memory or incompatiable event. @(%s:%d)\n", __FILE__, __LINE__); | |
return; | |
} | |
if( 0 != event_add(listener_event, NULL) ) | |
{ | |
fprintf(stderr, "ERR: Failed on event_add(). @(%s:%d)\n", __FILE__, __LINE__); | |
return; | |
} | |
event_base_dispatch(base); | |
} | |
int | |
main(int c, char **v) | |
{ | |
setvbuf(stdout, NULL, _IONBF, 0); | |
run(); | |
return 0; | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment