#define _POSIX_C_SOURCE 200809L #include /* assert */ #include /* errno, ENOENT, ENOTSOCK */ #include /* err, errx, warn */ #include /* openat */ #include /* bool, false */ #include /* memcmp */ #include /* send, socketpair */ #include /* waitpid */ #include /* close, fork, lseek, ftruncate, read, unlink */ /* V této přípravě je Vaším úkolem naprogramovat datagramový server, * který bude ukládat příchozí datagramy do souboru. Procedura * ‹log_packets› obdrží tyto parametry: * * • ‹sock_fd› – popisovač socketu, * • ‹log_fd› – soubor, do kterého bude logovat, * • ‹n› – (vstupně-výstupní parametr) počet paketů. * * Poté, co je obdržen a zapsán ‹n›-tý paket, procedura skončí. * Je-li paket delší než 1024 bajtů, zapište pouze prvních 1024 * bajtů. * * Pakety ukládejte do souboru v následujícím formátu: (‹délka› ‹tělo›), * přitom délka nechť je dvoubajtové číslo zapsané vyšším bajtem napřed. * Za délkou paketu následuje přesně jedna mezera. Tělo je obsah paketu. * Každá dvojice je ukončena znakem nového řádku. * * Nastane-li nějaká systémová chyba, vrátí -1 a nastaví hodnotu * ‹errno› odpovídajícím způsobem, jinak vrátí nulu. Po ukončení * procedury bude, bez ohledu na důvod, v ‹*n› uložen skutečný počet * paketů úspěšně zapsaných do souboru ‹log_fd›. */ int log_packets( int sock_fd, int log_fd, int *n ); /* ┄┄┄┄┄┄┄ %< ┄┄┄┄┄┄┄┄┄┄ následují testy ┄┄┄┄┄┄┄┄┄┄ %< ┄┄┄┄┄┄┄ */ static void unlink_if_exists( const char *file ) { if ( unlink( file ) == -1 && errno != ENOENT ) err( 2, "unlink" ); } static void close_or_warn( int fd, const char *name ) { if ( close( fd ) == -1 ) warn( "closing %s", name ); } static int create_file( const char *name ) { unlink_if_exists( name ); int fd; if ( ( fd = openat( AT_FDCWD, name, O_CREAT | O_TRUNC | O_RDWR, 0666 ) ) == -1 ) err( 2, "creating %s", name ); return fd; } static void send_or_die( int fd, const void *buffer, int nbytes ) { int bytes_sent = send( fd, buffer, nbytes, 0 ); if ( bytes_sent == -1 ) err( 1, "sending %d bytes", nbytes ); if ( bytes_sent != nbytes ) errx( 1, "unexpected short send: %d/%d sent", bytes_sent, nbytes ); } 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 sender( int fd ) { /* case 1 */ send_or_die( fd, NULL, 0 ); /* case 2 */ send_or_die( fd, "Zivot", 5 ); send_or_die( fd, "je", 2 ); send_or_die( fd, "boj", 3 ); /* case 3 */ send_or_die( fd, "Let", 3 ); send_or_die( fd, "there", 5 ); send_or_die( fd, "be", 2 ); send_or_die( fd, "rock", 4 ); /* case 4 */ send_or_die( fd, "The recv(), recvfrom(), and recvmsg()", 41 ); send_or_die( fd, " calls are used to receive messages from a socket.", 50 ); send_or_die( fd, " They may be used to receive data on both connectionless", 57 ); send_or_die( fd, "and connection-oriented sockets.", 32 ); /* case 5 */ unsigned char big[ 2048 ]; for ( unsigned i = 0; i < sizeof( big ); ++ i ) big[ i ] = i; send_or_die( fd, big, sizeof( big ) ); } static bool check_file( int fd, const char *expected, int size ) { if ( lseek( fd, 0, SEEK_SET ) == -1 ) err( 1, "lseek" ); char buffer[ 256 ]; int rv = 0; int out_size = 0; while ( ( rv = read( fd, buffer, sizeof( buffer ) ) ) > 0 ) { if ( out_size + rv > size ) return false; if ( memcmp( expected + out_size, buffer, rv ) != 0 ) return false; out_size += rv; } return rv == 0; } static void check_case( const char *out, int size, int n, int server_fd, int file_fd ) { int init_n = n; assert( log_packets( server_fd, file_fd, &n ) == 0 ); assert( n == init_n ); assert( check_file( file_fd, out, size ) ); if ( ftruncate( file_fd, 0 ) == -1 ) err( 1, "ftruncate" ); if ( lseek( file_fd, 0, SEEK_SET ) == -1 ) err( 1, "lseek" ); } int main( void ) { unlink_if_exists( "zt.p2_out" ); int fds[ 2 ]; if ( socketpair( AF_UNIX, SOCK_DGRAM, 0, fds ) == -1 ) err( 1, "pipe" ); int server_fd = fds[ 0 ], client_fd = fds[ 1 ]; int client_pid = fork(); if ( client_pid == -1 ) err( 1, "fork" ); if ( client_pid == 0 ) { close_or_warn( server_fd, "server end of a socket" ); sender( client_fd ); close_or_warn( client_fd, "client end of a socket" ); return 0; } close_or_warn( client_fd, "client end of a socket" ); int file_fd = create_file( "zt.p2_out" ); /* case 1 */ const char *out1 = "\x00\x00 \n"; check_case( out1, 4, 1, server_fd, file_fd ); /* case 2 */ const char *out2 = "\x00\x05 Zivot\n" "\x00\x02 je\n" "\x00\x03 boj\n"; check_case( out2, 22, 3, server_fd, file_fd ); /* case 3 */ const char *out3 = "\x00\x03 Let\n" "\x00\x05 there\n" "\x00\x02 be\n" "\x00\x04 rock\n"; check_case( out3, 30, 4, server_fd, file_fd ); /* case 4 */ const char *out4 = "\x00\x29 The recv(), recvfrom(), and recvmsg()\n" "\x00\x32 calls are used to receive messages from a socket.\n" "\x00\x39 They may be used to receive data on both connectionless\n" "\x00\x20 and connection-oriented sockets.\n"; check_case( out4, 196, 4, server_fd, file_fd ); /* case 5 */ char out5[ 1028 ]; out5[ 0 ] = 0x04; out5[ 1 ] = 0x00; out5[ 2 ] = 0x20; out5[ 1027 ] = '\n'; for ( int i = 0; i < 1024; ++ i ) out5[ i + 3 ] = i; check_case( out5, sizeof( out5 ), 1, server_fd, file_fd ); assert( reap( client_pid ) == 0 ); close_or_warn( file_fd, "zt.p2_out" ); close_or_warn( server_fd, "server end of a socket" ); unlink_if_exists( "zt.p2_out" ); return 0; }