Skip to content

Instantly share code, notes, and snippets.

@yinyin
Created February 17, 2010 05:31
Show Gist options
  • Save yinyin/306332 to your computer and use it in GitHub Desktop.
Save yinyin/306332 to your computer and use it in GitHub Desktop.
/**
* 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