#define _POSIX_C_SOURCE 200809L #include /* assert */ #include /* err, warn, warnx */ #include /* errno */ #include /* socketpair, send, recv */ #include /* socketpair, send, recv */ #include /* exit, NULL */ #include /* strlen, strcmp */ #include /* close, fork, alarm */ /* Tato příprava doplňuje předchozí ‹p4_newsc› – Vaším úkolem bude * tentokrát naprogramovat odpovídající server. Krom popsaného * protokolu implementujte také nahrávání dat – jsou-li první 4 * bajty odeslané klientem ‹0xff 0xff 0xff 0xff›, následuje * libovolný počet bajtů (nejvýše ale 1020), které server připojí na * konec své sekvence. Procedura ‹news_server› bude mít tyto * parametry: * * • ‹sock_fd› – popisovač socketu, * • ‹n› – maximální počet klientů. * * Po odbavení ‹n› klientů procedura skončí. Nastane-li fatální * systémová chyba, procedura ihned skončí s chybou -1. Při chybě * komunikace s jednotlivým klientem server pokračuje v činnosti, * ale návratová hodnota bude -2. Nedošlo-li k žádné chybě, výsledek * bude 0. */ int news_server( int sock_fd, int n ); /* ┄┄┄┄┄┄┄ %< ┄┄┄┄┄┄┄┄┄┄ následují testy ┄┄┄┄┄┄┄┄┄┄ %< ┄┄┄┄┄┄┄ */ #include /* waitpid */ #include /* bool */ static void close_or_warn( int fd, const char *name ) { if ( close( fd ) == -1 ) warn( "closing %s", name ); } static pid_t fork_server( int sock_fds[ 2 ], int n ) { int server_fd = sock_fds[ 0 ], client_fd = sock_fds[ 1 ]; pid_t pid = fork(); if ( pid == -1 ) err( 2, "fork" ); if ( pid > 0 ) return pid; /* Proces se serverem se ukončí za 5 sekund; déle by testování jednoho * serveru nemělo trvat a při selhaných testech rodič zemře dřív, než * může potomka ukončit. */ alarm( 5 ); close_or_warn( client_fd, "client side of a socket" ); int rv = news_server( server_fd, n ); close_or_warn( server_fd, "server side of a socket" ); exit( rv ); } static int reap( pid_t pid ) { int status; if ( waitpid( pid, &status, 0 ) == -1 ) err( 2, "wait" ); if ( WIFEXITED( status ) ) return WEXITSTATUS( status ); else return -1; } static void append_span( int client_fd, const char *msg, size_t len ) { assert( len <= 1020 ); char buffer[ 1024 ] = { 0xff, 0xff, 0xff, 0xff }; memcpy( buffer + 4, msg, len ); if ( send( client_fd, buffer, len + 4, 0 ) == -1 ) err( 1, "sending data" ); } static void append( int client_fd, const char *msg ) { append_span( client_fd, msg, strlen( msg ) ); } static bool recv_compare( int client_fd, const char *msg, int len ) { char buf[ 1024 ]; int recvd = recv( client_fd, buf, len + 1, 0 ); if ( recvd == -1 ) err( 1, "receiving response" ); if ( recvd != len ) return false; return memcmp( buf, msg, len ) == 0; } static bool check_response( int client_fd, const char *hdr, const char *str, int len ) { if ( send( client_fd, hdr, 8, 0 ) == -1 ) err( 1, "sending header" ); bool res = recv_compare( client_fd, str, len ); return res; } int main( void ) { int sock_fds[ 2 ]; if ( socketpair( AF_UNIX, SOCK_DGRAM, 0, sock_fds ) == -1 ) err( 1, "socketpair" ); int server_fd = sock_fds[ 0 ], client_fd = sock_fds[ 1 ]; pid_t pid = fork_server( sock_fds, 14 ); close_or_warn( server_fd, "server side of a socket" ); assert( check_response( client_fd, "\0\0\0\0\0\0\0\0", "", 0 ) ); append( client_fd, "foo" ); assert( check_response( client_fd, "\0\0\0\0\0\0\0\3", "foo", 3 ) ); assert( check_response( client_fd, "\0\0\0\1\0\0\0\2", "oo", 2 ) ); assert( check_response( client_fd, "\1\0\0\0\0\0\0\4", "", 0 ) ); append( client_fd, "" ); assert( check_response( client_fd, "\0\0\0\0\0\0\0\3", "foo", 3 ) ); append( client_fd, "bar" ); assert( check_response( client_fd, "\0\0\0\0\0\0\0\6", "foobar", 6 ) ); append_span( client_fd, "some\0thing\0", 11 ); assert( check_response( client_fd, "\0\0\0\xa\0\0\0\4", "\0thi", 4 ) ); assert( check_response( client_fd, "\0\0\0\xc\0\0\0\6", "hing\0", 5 ) ); assert( check_response( client_fd, "\0\0\0\0\0\0\0\xd", "foobarsome\0th", 0xd ) ); assert( check_response( client_fd, "\xff\xff\xff\xfe\0\0\0\0", "", 0 ) ); assert( reap( pid ) == 0 ); close_or_warn( client_fd, "client side of a socket" ); return 0; }