#define _POSIX_C_SOURCE 200809L #include /* assert */ #include /* err, warn */ #include /* bool */ #include /* exit */ #include /* memcmp, memcpy, memset */ #include /* recv, send, socketpair */ #include /* waitpid */ #include /* alarm, close, fork */ /* Naprogramujte proceduru ‹agree›, která obdrží: * * • ‹sock_fd›, popisovač datagramového socketu, * • ‹our_set›, ukazatel na pole, které popisuje podmnožinu ⟦S⟧ * množiny ⟦{ k ∈ ℤ | 0 ≤ k < 512 }⟧, a to tak, že číslo ⟦i⟧ je * přítomno právě když je v poli nastavený ⟦i⟧-tý bit,¹ * • ‹intersection› – ukazatel na neinicializované pole stejné * velikosti a stejného významu, do kterého uloží výsledek. * * Procedura (server) přijme od klienta datagram, který bude * obsahovat množinu čísel ve stejné podobě, jakou má parametr * ‹our_set›, provede průnik s ‹our_set› a tuto odešle jako odpověď * klientovi. Klient výslednou množinu potvrdí nebo zamítne zasláním * jednobajtového datagramu s hodnotou 1 (potvrzení) nebo 0 * (zamítnutí). Můžete předpokládat, že všechny příchozí datagramy * mají téhož odesílatele. * * Návratová hodnota ‹agree› bude: * * • ‹-3› v případě, že se domluva nezdařila – klient vybranou * množinu zamítl, * • ‹-2› při chybě protokolu (klient poslal data v nečekaném tvaru), * • ‹-1› v případě, že nastala systémová chyba na straně serveru, * • konečně ‹0› byla-li podmnožina úspěšně domluvena, pak zároveň * do ‹intersection› tuto uloží (v žádném jiném případě hodnoty * v ‹intersection› měnit nebude). * * ¹ Bity počítáme od nejméně významného bitu prvního bajtu až po * nejvýznamnější bit posledního bajtu v poli. */ int agree( int sock_fd, const char our_set[ 64 ], char intersection[ 64 ] ); /* ┄┄┄┄┄┄┄ %< ┄┄┄┄┄┄┄┄┄┄ následují testy ┄┄┄┄┄┄┄┄┄┄ %< ┄┄┄┄┄┄┄ */ static void close_or_warn( int fd ) { if ( close( fd ) == -1 ) warn( "closing fd %d", fd ); } static int run_client( const int serv_fd, const char set[ 64 ], unsigned char response, const char expected[ 64 ] ) { // response == 0xff je výhybka pro testování neúplné množiny if ( send( serv_fd, set, response == 0xff ? 32 : 64, 0 ) == -1 ) err( 1, "sending set to server" ); if ( response == 0xff ) { close_or_warn( serv_fd ); return 0; } char buf[ 64 ]; if ( recv( serv_fd, buf, 64, 0 ) == -1 ) err( 1, "receiving set from server" ); // response == 0xfe je výhybka pro testování prázdné odpovědi if ( send( serv_fd, &response, response == 0xfe ? 0 : 1, 0 ) == -1 ) err( 1, "sending reply to server" ); close_or_warn( serv_fd ); if ( expected ) return memcmp( buf, expected, 64 ); return 0; } static pid_t spawn_client( int serv_fd, int client_fd, const char set[ 64 ], unsigned char response, const char expected[ 64 ] ) { pid_t pid = fork(); if ( pid == -1 ) err( 1, "fork" ); // V potomkovi poběží klient s přednastavenými zprávami if ( pid == 0 ) { // Bezpodmínečně ukončit klient po 3 sekundách pro případ, že by se // vlivem chybného serveru zasekl v čekání na odpověď. alarm( 3 ); close_or_warn( client_fd ); exit( run_client( serv_fd, set, response, expected ) ); } return pid; } static int reap_client( pid_t pid ) { int status; if ( waitpid( pid, &status, 0 ) == -1 ) err( 1, "waitpid" ); if ( WIFEXITED( status ) ) return WEXITSTATUS( status ); return 1; } static int negotiate( const char server_set[ 64 ], const char client_set[ 64 ], unsigned char response, char result[ 64 ], const char expected[ 64 ] ) { // V době forku musí serverový socket existovat a být navázán. // int sock_fd = prepare_socket( "zt.r1_server" ); 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 = spawn_client( server_fd, client_fd, client_set, response, expected ); int rv = agree( client_fd, server_set, result ); close_or_warn( server_fd ); close_or_warn( client_fd ); if ( reap_client( pid ) != 0 ) return 1; return rv; } static bool check_intersection( const char server_set[ 64 ], const char client_set[ 64 ], const char expected[ 64 ] ) { char received[ 64 ]; assert( ( negotiate( server_set, client_set, 0x01, received, expected ) == 0 ) ); return memcmp( received, expected, 64 ) == 0; } int main( void ) { // Ukončit testy po 5 sekundách, pokud se zaseknou. alarm( 5 ); char set_empty[ 64 ]; memset( set_empty, 0x00, 64 ); char set_full[ 64 ]; memset( set_full, 0xff, 64 ); char set_even[ 64 ]; memset( set_even, 0x55, 64 ); char set_odd[ 64 ]; memset( set_odd, 0xaa, 64 ); char set_a[ 64 ]; for ( int i = 0; i < 64; ++i ) set_a[ i ] = i; // V následujících testech klient vždy přijme nabízený průnik assert( check_intersection( set_empty, set_empty, set_empty ) ); assert( check_intersection( set_empty, set_full, set_empty ) ); assert( check_intersection( set_full, set_empty, set_empty ) ); assert( check_intersection( set_full, set_full, set_full ) ); assert( check_intersection( set_odd, set_full, set_odd ) ); assert( check_intersection( set_full, set_even, set_even ) ); assert( check_intersection( set_even, set_odd, set_empty ) ); assert( check_intersection( set_full, set_a, set_a ) ); assert( check_intersection( set_a, set_full, set_a ) ); // Test na odmítnutí char received[ 64 ]; memcpy( received, set_a, 64 ); assert( ( negotiate( set_full, set_full, 0x00, received, set_full ) == -3 ) ); assert( ( memcmp( received, set_a, 64 ) == 0 ) ); assert( ( negotiate( set_even, set_odd, 0x00, received, set_empty ) == -3 ) ); assert( ( memcmp( received, set_a, 64 ) == 0 ) ); // Test na několikanásobné použití socketu za sebou 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; pid = spawn_client( server_fd, client_fd, set_even, 0x01, set_even ); assert( ( agree( client_fd, set_full, received ) == 0 ) ); assert( ( memcmp( received, set_even, 64 ) == 0 ) ); assert( reap_client( pid ) == 0 ); pid = spawn_client( server_fd, client_fd, set_odd, 0x01, set_odd ); assert( ( agree( client_fd, set_full, received ) == 0 ) ); assert( ( memcmp( received, set_odd, 64 ) == 0 ) ); assert( reap_client( pid ) == 0 ); pid = spawn_client( server_fd, client_fd, set_a, 0x00, set_a ); assert( ( agree( client_fd, set_full, received ) == -3 ) ); assert( ( memcmp( received, set_odd, 64 ) == 0 ) ); assert( reap_client( pid ) == 0 ); close_or_warn( client_fd ); close_or_warn( server_fd ); // Test na nedodržení protokolu: klient odpoví něco jiného než 0/1 assert( ( negotiate( set_full, set_a, 0x42, received, set_a ) == -2 ) ); assert( ( memcmp( received, set_odd, 64 ) == 0 ) ); // Test na nedodržení protokolu: klient posílá neúplnou množinu assert( ( negotiate( set_full, set_a, 0xff, received, NULL ) == -2 ) ); assert( ( memcmp( received, set_odd, 64 ) == 0 ) ); // Test na nedodržení protokolu: klient pošle prázdnou odpověď assert( ( negotiate( set_full, set_a, 0xfe, received, NULL ) == -2 ) ); assert( ( memcmp( received, set_odd, 64 ) == 0 ) ); return 0; }