diff options
-rw-r--r-- | docs/network/README | 3 | ||||
-rw-r--r-- | include/wolf/port/unused.h (renamed from src/port/unused.h) | 0 | ||||
-rw-r--r-- | tests/network/test1.c | 189 |
3 files changed, 180 insertions, 12 deletions
diff --git a/docs/network/README b/docs/network/README index edfacb2..e011a99 100644 --- a/docs/network/README +++ b/docs/network/README @@ -26,4 +26,5 @@ Links: asynchronous I/O interfaces - Boinc: http://boinc.berkeley.edu: some ideas around protocol FSMs in networking - +- http://www.artima.com/articles/io_design_patterns1.html: on Reactor + and Proactor and a hybrid the emulated Proactor diff --git a/src/port/unused.h b/include/wolf/port/unused.h index c18ed27..c18ed27 100644 --- a/src/port/unused.h +++ b/include/wolf/port/unused.h diff --git a/tests/network/test1.c b/tests/network/test1.c index e37f4cb..b5b2aa8 100644 --- a/tests/network/test1.c +++ b/tests/network/test1.c @@ -3,24 +3,39 @@ #include "port/stdio.h" /* for fprintf */ #include "port/string.h" /* for memset */ #include "port/stdbool.h" /* for bool, true, false */ +#include "port/unused.h" /* for WOLF_UNUSED */ #include <unistd.h> /* for close */ #include <errno.h> /* for errno */ #include <fcntl.h> /* for fcntl */ #include <sys/select.h> /* for select */ +#include <assert.h> /* for assertions */ +#include <signal.h> /* for signal */ -static void wolf_network_sock_nonblocking( int fd ) { +static bool wolf_network_sock_nonblocking( int fd ) { int flags; flags = fcntl( fd, F_GETFL, 0 /* ignored */ ); + if( flags < 0 ) return false; flags |= O_NONBLOCK; flags = fcntl( fd, F_SETFL, flags ); + if( flags < 0 ) return false; + return true; } #define MAX_IDLE_TIMEOUT 10 +#define WRITE_BUFFER_SIZE 1024 +#define READ_BUFFER_SIZE 1024 #define max(a,b) ((a) < (b) ? (b) : (a)) +static bool terminate = false; + +static void term_handler( int sig ) { + WOLF_UNUSED( sig ); + terminate = true; +} + int main( int argc, char *argv[] ) { char *host; char *service; @@ -30,6 +45,17 @@ int main( int argc, char *argv[] ) { int fd; int res; int idle_secs; + char read_buffer[READ_BUFFER_SIZE]; + char write_buffer[WRITE_BUFFER_SIZE]; + size_t stdout_to_write; + size_t stdout_written; + size_t fd_to_write; + size_t fd_written; + size_t total_stdin_read; + size_t total_fd_read; + size_t total_fd_written; + size_t total_stdout_written; + int count; if( argc != 3 ) { fprintf( stderr, "usage: test1 <host> <port>\n" ); @@ -72,11 +98,36 @@ int main( int argc, char *argv[] ) { goto FAIL; } - wolf_network_sock_nonblocking( STDIN_FILENO ); - wolf_network_sock_nonblocking( STDOUT_FILENO ); - wolf_network_sock_nonblocking( fd ); + if( !wolf_network_sock_nonblocking( STDIN_FILENO ) ) { + (void)close( fd ); + fprintf( stderr, "set nonblocking failed for stdin: %s (%d)\n", strerror( errno ), errno ); + goto FAIL; + } + if( !wolf_network_sock_nonblocking( STDOUT_FILENO ) ) { + (void)close( fd ); + fprintf( stderr, "set nonblocking failed for stdout: %s (%d)\n", strerror( errno ), errno ); + goto FAIL; + } + if( !wolf_network_sock_nonblocking( fd ) ) { + (void)close( fd ); + fprintf( stderr, "set nonblocking failed for socket: %s (%d)\n", strerror( errno ), errno ); + goto FAIL; + } idle_secs = 0; + count = 0; + stdout_to_write = 0; + stdout_written = 0; + fd_to_write = 0; + fd_written = 0; + total_stdin_read = 0; + total_fd_read = 0; + total_fd_written = 0; + total_stdout_written = 0; + + signal( SIGTERM, term_handler ); + signal( SIGINT, term_handler ); + signal( SIGPIPE, SIG_IGN ); do { int max_fd; @@ -84,6 +135,7 @@ int main( int argc, char *argv[] ) { fd_set write_set; fd_set error_set; struct timeval timeout; + ssize_t rres; FD_ZERO( &read_set ); FD_ZERO( &write_set ); @@ -97,14 +149,23 @@ int main( int argc, char *argv[] ) { max_fd = max( max_fd, STDOUT_FILENO ); max_fd = max( max_fd, fd ); - FD_SET( STDIN_FILENO, &read_set ); - FD_SET( fd, &read_set ); - FD_SET( STDOUT_FILENO, &write_set ); - FD_SET( fd, &write_set ); + if( fd_to_write == 0 ) { + FD_SET( STDIN_FILENO, &read_set ); + } FD_SET( STDIN_FILENO, &error_set ); + if( stdout_to_write > 0 ) { + FD_SET( STDOUT_FILENO, &write_set ); + } FD_SET( STDOUT_FILENO, &error_set ); + if( stdout_to_write == 0 ) { + FD_SET( fd, &read_set ); + } + if( fd_to_write > 0 ) { + FD_SET( fd, &write_set ); + } FD_SET( fd, &error_set ); - + + /* wait for events or timeout */ res = select( max_fd + 1, &read_set, &write_set, &error_set, &timeout ); if( res < 0 ) { if( errno == EINTR || errno == EAGAIN ) { @@ -128,11 +189,117 @@ int main( int argc, char *argv[] ) { /* something happened */ idle_secs = 0; + /* handle error conditions */ + if( FD_ISSET( STDOUT_FILENO, &error_set ) ) { + fprintf( stderr, "Error flag on stdout!\n" ); + } + if( FD_ISSET( STDIN_FILENO, &error_set ) ) { + fprintf( stderr, "Error flag on stdin!\n" ); + } + if( FD_ISSET( fd, &error_set ) ) { + fprintf( stderr, "Error flag on socket!\n" ); + } + + /* empty the socket read buffer to stdout */ if( FD_ISSET( STDOUT_FILENO, &write_set ) ) { -// res = write( STDOUT_FILENO, write_buf + assert( stdout_to_write > 0 ); + rres = write( STDOUT_FILENO, read_buffer + stdout_written, stdout_to_write ); + if( rres < 0 ) { + if( errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR ) { + /* skip */ + } else { + fprintf( stderr, "write to stdout failed: %s (%d)\n", + strerror( errno ), errno ); + (void)close( fd ); + goto FAIL; + } + } else { + assert( rres >= 0 ); + stdout_written += (size_t)rres; + assert( stdout_to_write >= (size_t)rres ); + stdout_to_write -= (size_t)rres; + total_stdout_written += (size_t)rres; + } + } + + /* empty the socket write buffer writing to fd */ + if( FD_ISSET( fd, &write_set ) ) { + assert( fd_to_write > 0 ); + rres = write( fd, write_buffer + fd_written, fd_to_write ); + if( rres < 0 ) { + if( errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR ) { + /* skip */ + } else { + fprintf( stderr, "write to socket failed: %s (%d)\n", + strerror( errno ), errno ); + (void)close( fd ); + goto FAIL; + } + } else { + assert( rres >= 0 ); + fd_written += (size_t)rres; + assert( fd_to_write >= (size_t)rres ); + fd_to_write -= (size_t)rres; + total_fd_written += (size_t)rres; + } + } + + /* read from socket, fill socket read buffer */ + if( FD_ISSET( fd, &read_set ) ) { + assert( stdout_to_write == 0 ); + rres = read( fd, read_buffer, READ_BUFFER_SIZE ); + if( rres < 0 ) { + if( errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR ) { + /* skip */ + } else { + fprintf( stderr, "read from socket failed: %s (%d)\n", + strerror( errno ), errno ); + (void)close( fd ); + goto FAIL; + } + } else if( rres == 0 ) { + /* EOF on socket */ + } else { + assert( rres > 0 ); + stdout_written = 0; + stdout_to_write = (size_t)rres; + total_fd_read += (size_t)rres; + } + } + + /* read from stdin, fill socket write buffer */ + if( FD_ISSET( STDIN_FILENO, &read_set ) ) { + assert( fd_to_write == 0 ); + rres = read( STDIN_FILENO, write_buffer, WRITE_BUFFER_SIZE ); + if( rres < 0 ) { + if( errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR ) { + /* skip */ + } else { + fprintf( stderr, "read from stdin failed: %s (%d)\n", + strerror( errno ), errno ); + (void)close( fd ); + goto FAIL; + } + } else if( rres == 0 ) { + /* EOF on STDIN */ + } else { + assert( rres > 0 ); + fd_written = 0; + fd_to_write = (size_t)rres; + total_stdin_read += (size_t)rres; + } } } - } while( true ); + + count++; + if( count % 1000 == 0 ) { + fprintf( stderr, "Transfered stdin: %d, stdout: %d, fd-in: %d, fd-out: %d\r", + total_stdin_read, total_stdout_written, total_fd_read, total_fd_written ); + } + } while( !terminate ); + + fprintf( stderr, "Terminated with stdin: %d, stdout: %d, fd-in: %d, fd-out: %d\n", + total_stdin_read, total_stdout_written, total_fd_read, total_fd_written ); END: res = close( fd ); |