#define _POSIX_C_SOURCE 200809L #include /* assert */ #include /* err */ #include /* errno */ #include /* uint32_t, int64_t */ #include /* exit */ #include /* read, write, close, unlink, … */ #include /* socket, AF_* */ #include /* struct sockaddr_un */ #include /* ntohl */ /* Napište podprogram ‹multi_server›, který bude pracovat podobně * jako ‹memo_server› z dřívější přípravy, s tím rozdílem, že bude * mít několik paměťových buněk. Přijme tyto parametry: * * 1. ‹sock_fd› je popisovač proudového socketu, který je svázán * s adresou a je nastaven do režimu poslouchání, * 2. ‹count› je maximální počet připojení (po jeho dosažení se * podprogram vrátí), * 3. ‹initial› je ukazatel na počáteční obsah paměti, * 4. ‹size› je počet 32bitových buněk paměti. * * Server implementuje následující protokol: * * • klient může kdykoliv odeslat šestibajtovou zprávu, * • první dva bajty určují index buňky, se kterou chce klient * pracovat (významnější bajt je odeslán první), * • je-li zbytek zprávy ‹0xffff'ffff›, server odpoví aktuální * hodnotou požadované buňky, nebo ‹0xffff'ffff› je-li požadovaný * index větší nebo roven ‹size›, * • jinak je zbytek zprávy nová hodnota, kterou server do vybrané * buňky uloží, a klientu tuto akci potvrdí odesláním nové * hodnoty, nebo zprávou ‹0xffff'ffff› je-li požadovaný index * neplatný, * • uzavření spojení iniciuje vždy klient. * * Hodnoty buněk jsou odesílány nejvýznamnějším bajtem napřed. * Předchází-li potvrzení změny stavu nějakému dotazu na tentýž * index, odpověď na tento dotaz musí korespondovat takto potvrzené * změně (tzn. buď je odpověď takto potvrzený stav, nebo nějaký * novější). * * Návratová hodnota 0 znamená, že bylo úspěšně obslouženo ‹count› * klientů, -1 znamená systémovou chybu. */ int multi_server( int sock_fd, int count, const uint32_t *initial, int size ); /* ┄┄┄┄┄┄┄ %< ┄┄┄┄┄┄┄┄┄┄ následují testy ┄┄┄┄┄┄┄┄┄┄ %< ┄┄┄┄┄┄┄ */ #include /* waitpid */ #include /* signal, SIG_IGN, SIGPIPE */ static void close_or_warn( int fd, const char *name ) { if ( close( fd ) == -1 ) warn( "closing %s", name ); } static void unlink_if_exists( const char *file ) { if ( unlink( file ) == -1 && errno != ENOENT ) err( 2, "unlink" ); } 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 pid_t fork_server( int sock_fd, int clients, uint32_t *init, int size ) { pid_t pid = fork(); if ( pid == -1 ) err( 2, "fork" ); if ( pid == 0 ) { alarm( 3 ); exit( multi_server( sock_fd, clients, init, size ) ? 1 : 0 ); } close_or_warn( sock_fd, "server socket in client" ); return pid; } static const struct sockaddr_un test_addr = { .sun_family = AF_UNIX, .sun_path = "zt.a_socket" }; static int client_connect() { int fd = socket( AF_UNIX, SOCK_STREAM, 0 ); if ( fd == -1 ) err( 2, "socket" ); if ( connect( fd, ( const struct sockaddr * ) &test_addr, sizeof test_addr ) == -1 ) return -1; return fd; } struct client_msg { uint16_t idx; uint32_t val; } __attribute__(( packed )); static int client_set( int fd, uint16_t idx, uint32_t val ) { struct client_msg msg = { .idx = htons( idx ), .val = htonl( val ) }; uint32_t reply; if ( send( fd, &msg, sizeof msg, 0 ) == -1 ) return -1; if ( recv( fd, &reply, 4, MSG_WAITALL ) != 4 || reply != msg.val ) { return -1; } return 0; } static int client_get( int fd, uint16_t idx ) { struct client_msg msg = { .idx = htons( idx ), .val = -1 }; uint32_t reply; if ( send( fd, &msg, sizeof msg, 0 ) == -1 ) return -1; if ( recv( fd, &reply, sizeof reply, MSG_WAITALL ) == -1 ) return -1; return ntohl( reply ); } int main( void ) { if ( signal( SIGPIPE, SIG_IGN ) == SIG_ERR ) err( 2, "signal" ); char buffer[ 4 ]; uint32_t init[ 2 ] = { 33, 44 }; unlink_if_exists( test_addr.sun_path ); int sock_fd = socket( AF_UNIX, SOCK_STREAM, 0 ); if ( sock_fd == -1 ) err( 2, "socket" ); if ( bind( sock_fd, ( const struct sockaddr * ) &test_addr, sizeof test_addr ) == -1 ) err( 2, "bind" ); if ( listen( sock_fd, 3 ) == -1 ) err( 2, "listen" ); pid_t pid = fork_server( sock_fd, 3, init, 2 ); int c1 = client_connect(); int c2 = client_connect(); assert( client_get( c1, 0 ) == 33 ); assert( client_get( c2, 1 ) == 44 ); assert( client_set( c1, 0, 77 ) == 0 ); assert( client_get( c2, 0 ) == 77 ); assert( client_get( c1, 0 ) == 77 ); assert( client_get( c1, 1 ) == 44 ); int c3 = client_connect(); assert( send( c3, "\0", 1, 0 ) == 1 ); assert( client_set( c2, 1, 1077 ) == 0 ); assert( client_get( c1, 1 ) == 1077 ); assert( send( c3, "\0", 1, 0 ) == 1 ); assert( client_set( c1, 1, 7077 ) == 0 ); assert( client_get( c2, 1 ) == 7077 ); assert( send( c3, "\0\2", 2, 0 ) == 2 ); assert( client_set( c1, 1, 1300 * 7077 ) == 0 ); assert( client_get( c2, 1 ) == 1300 * 7077 ); close_or_warn( c1, "c1" ); assert( send( c3, "\1\0", 2, 0 ) == 2 ); assert( recv( c3, buffer, 1, 0 ) == 1 ); assert( client_get( c2, 0 ) == 2 * 65536 + 256 ); assert( client_get( c2, 1 ) == 1300 * 7077 ); close_or_warn( c2, "c2" ); close_or_warn( c3, "c3" ); assert( reap( pid ) == 0 ); unlink_if_exists( test_addr.sun_path ); return 0; }