#include <stdio.h>
#include <stdlib.h>	/* EXIT_FAILURE */
#include <stdbool.h>	/* bool true false */
#include <inttypes.h>	/* intptr_t */

#include <signal.h>	/* SIGPIPE SIG_IGN signal(3) */

#include <errno.h>	/* EPIPE */

#include <string.h>	/* memset(3) */

#include <assert.h>	/* assert(3) */

#include <fcntl.h>	/* close(2) */

#if _WIN32
#include <winsock2.h>
#else
#include <sys/time.h>	/* struct timeval */
#include <sys/socket.h>	/* AF_INET socklen_t struct sockaddr
			 * struct sockaddr_storage */

#include <netinet/in.h>	/* struct sockaddr_in */
#endif

#include <unistd.h>	/* close(2) */

#include <err.h>	/* err(3) errx(3) */

#include <event.h>	/* struct event struct event_base event_init(3)
			 * event_base_loop(3) */

#include <arena/proto.h>

#include "socket.h"
#include "bufio.h"


static const char mesg[]	= "one\ntwo\nthree\nbreak ";

static void user_catch_write(struct bufio *eio, void *sent, size_t len, enum bufio_errno err, void *s) {
	static int nsent	= 0;

	assert(err == BUFIO_ESUCCESS);

	assert(len == sizeof mesg - 1);

	if (++nsent > 21) {
		bufio_close(eio);
		socket_close(s);

		return;
	}

	bufio_writen(eio, mesg, sizeof mesg - 1, &user_catch_write, s, &(struct timeval){ 2, 0 });

	return;
} /* user_catch_write() */


static void serv_catch_line(struct bufio *eio, void *ln, size_t len, enum bufio_errno err, void *arg) {
	static char lnbuf[sizeof mesg];
	struct socket *s	= arg;

	assert(err == BUFIO_ESUCCESS || err == BUFIO_EEOF);

	if (len > 0)
		warnx("read %d bytes: %.*s", (int)len, (int)(len - 1), (char *)ln);

	if (err == BUFIO_EEOF) {
		bufio_close(eio);
		socket_close(s);

		return;
	}

	assert(len > 0);

	bufio_gets(eio, lnbuf, sizeof lnbuf, &serv_catch_line, s, &(struct timeval){ 2, 0});

	return;
} /* serv_catch_line() */


static void main_catch_accept(struct socket *s, struct socket *p, enum socket_errno so_errno, void *arg) {
	static char ln[sizeof mesg];
	struct bufio *eio;
	enum bufio_errno io_errno;

	if (so_errno == SOCKET_ECANCELLED)
		return /* void */;

	assert(so_errno == SOCKET_ESUCCESS);

	socket_close(s);

	warnx("caught accept");

	assert(eio = bufio_open(0, 0, &io_errno));

	bufio_set_source(eio, socket_to_source(p));
	bufio_set_sink(eio, socket_to_sink(p));

	bufio_gets(eio, ln, sizeof ln, &serv_catch_line, p, &(struct timeval){ 2, 0});

	return /* void */;
} /* main_catch_accept() */


static void main_catch_connect(struct socket *s, enum socket_errno err, void *arg) {
	struct bufio *eio;
	enum bufio_errno io_errno;

	assert(err == SOCKET_ESUCCESS);

	warnx("caught connect");

	assert(eio = bufio_open(0, 0, &io_errno));

	bufio_set_source(eio, socket_to_source(s));
	bufio_set_sink(eio, socket_to_sink(s));

	bufio_writen(eio, mesg, sizeof mesg - 1, &user_catch_write, s, &(struct timeval){ 2, 0 });

	return /* void */;
} /* main_catch_connect() */


int main(void) {
	struct event_base *ev_base;
	struct socket *client, *server;
	struct sockaddr_in sin;
	struct socket_options opts;
	struct socket_name so_name;
	enum socket_errno so_errno;
#if _WIN32
	WSADATA wsaData;
#endif

#if _WIN32
	if (NO_ERROR != WSAStartup(MAKEWORD(2, 2), &wsaData))
		err(EXIT_FAILURE, "WSAStartup");
#endif

#ifdef SIGPIPE
	signal(SIGPIPE, SIG_IGN);
#endif

	if (!(ev_base = event_init()))
		err(EXIT_FAILURE, "event_init");

	(void)memset(&sin, '\0', sizeof sin);

	sin.sin_family		= AF_INET;
	sin.sin_port		= htons(4000);
	sin.sin_addr.s_addr	= htonl(INADDR_LOOPBACK);

	(void)socket_name_init_addr(&so_name, (struct sockaddr *)&sin, sizeof sin);

	opts			= socket_defaults;
	opts.sa_reuseaddr	= 1;

	if (0 == (server = socket_open(&so_name, &opts, ev_base, ARENA_STDLIB, &so_errno)))
		err(EXIT_FAILURE, "socket_open");

	socket_accept(server, &main_catch_accept, server, 0);


	if (0 == (client = socket_open(&so_name, &opts, ev_base, ARENA_STDLIB, &so_errno)))
		err(EXIT_FAILURE, "socket_open");

	socket_connect(client, &main_catch_connect, client, 0);

	event_base_loop(ev_base, 0);

	return 0;
} /* main() */



syntax highlighted by Code2HTML, v. 0.9.1