#define _POSIX_C_SOURCE 200809L #include /* assert */ #include /* send, socketpair */ #include /* alarm, close, fork, read */ /* V tomto cvičení budeme realizovat opačný převod, než v tom * předchozím, zvolíme ale jiný typ zapouzdření – každému rámci * předchází dva bajty kódující jeho délku (významnější bajt první). * * Procedura ‹packet_seq› obdrží popisovač ‹in_fd› na kterém bude * přijímat datagramy, které zakóduje výše uvedeným způsobem do * proudu, který bude zapisovat do popisovače ‹out_fd›, který * odkazuje na připojený proudový socket. Je-li přijatý datagram * příliš velký, přebytečné bajty ignorujte. * * Uzavře-li protistrana výstupního popisovače spojení, proceduru * ‹packet_seq› považujeme za úspěšně dokončenou (s návratovou * hodnotou 0). V případě selhání nechť vrátí hodnotu -1. */ int packet_seq( int in_fd, int out_fd ); /* ┄┄┄┄┄┄┄ %< ┄┄┄┄┄┄┄┄┄┄ následují testy ┄┄┄┄┄┄┄┄┄┄ %< ┄┄┄┄┄┄┄ */ #include /* err, warn */ #include /* errno, EMSGSIZE */ #include /* signal, SIGPIPE, SIG_IGN, SIG_ERR */ #include /* memcmp, memset, strlen */ #include /* exit */ #include /* pid_t */ #include /* waitpid */ static void close_or_die( int fd, const char *name ) { if ( close( fd ) == -1 ) warn( "closing %s", name ); } static void socketpair_or_die( int type, int *sv ) { if ( socketpair( AF_UNIX, type, 0, sv ) == -1 ) err( 2, "creating socketpair" ); } static int reap( pid_t pid ) { int status; if ( waitpid( pid, &status, 0 ) == -1 ) err( 2, "waitpid" ); return WIFEXITED( status ) ? WEXITSTATUS( status ) : -1; } static int send_or_die( int sock_fd, const char *buf, size_t len ) { int rv = send( sock_fd, buf, len, 0 ); if ( rv == -1 && errno != EMSGSIZE ) err( 2, "sending data" ); return rv; } static pid_t fork_packet_seq( int *dgram, int *stream, int yours ) { pid_t pid = fork(); alarm( 5 ); /* 5s časovač, ať je vše zabito při uváznutí */ if ( pid == -1 ) err( 2, "fork" ); if ( pid > 0 ) return pid; /* Zavřeme nepotřebné konce. */ close_or_die( stream[ 1 - yours ], "stream socket" ); close_or_die( dgram[ 1 - yours ], "dgram socket" ); int rv = packet_seq( dgram[ yours ], stream[ yours ] ); /* Po konci používání zavřeme i ty zbylé. */ close_or_die( stream[ yours ], "stream socket" ); close_or_die( dgram[ yours ], "dgram socket" ); exit( rv == 0 ? 0 : 1 ); } static int read_exactly( int fd, char *buf, int size ) { int remains = size; int bytes; while ( remains > 0 && ( bytes = read( fd, buf + size - remains, remains ) ) > 0 ) { remains -= bytes; } if ( bytes == -1 ) err( 2, "read" ); return remains; } int main( void ) { /* Je nutné signál ‹SIGPIPE› ignorovat, pokud chceme být schopni * zachytit chybu ‹EPIPE› z ‹write›. */ signal( SIGPIPE, SIG_IGN ); int stream[ 2 ]; int dgram[ 2 ]; socketpair_or_die( SOCK_STREAM, stream ); socketpair_or_die( SOCK_DGRAM, dgram ); /* Zavolá se ‹packet_seq› ve vedlejším procesu. Zároveň mu * předáme popisovače (na [ 1 ]), o jejichž uzavření se dále * stará. */ pid_t pid = fork_packet_seq( dgram, stream, 1 ); /* Sockety předané druhému procesu jsou zde dále nepotřebné, tak * je uzavřeme. */ close_or_die( stream[ 1 ], "stream socket" ); close_or_die( dgram[ 1 ], "dgram socket" ); char data_a[] = "hello world"; char data_b[ 50000 ]; memset( data_b, 'a', sizeof( data_b ) ); char data_c[ 65535 ] = { 0 }; /* Dostatečně velké buffery na odeslaná data + dva bajty značící * velikost. */ char resp_a[ strlen( data_a ) + 2 ]; char resp_b[ 50002 ]; char resp_c[ 65537 ]; send_or_die( dgram[ 0 ], data_a, strlen( data_a ) ); assert( read_exactly( stream[ 0 ], resp_a, sizeof( resp_a ) ) == 0 ); assert( memcmp( data_a, resp_a + 2, strlen( data_a ) ) == 0 ); assert( resp_a[ 0 ] == ( char ) 0x00 ); assert( resp_a[ 1 ] == ( char ) 0x0B ); if ( send_or_die( dgram[ 0 ], data_b, sizeof( data_b ) ) != -1 ) { assert( read_exactly( stream[ 0 ], resp_b, sizeof( resp_b ) ) == 0 ); assert( memcmp( data_b, resp_b + 2, sizeof( data_b ) ) == 0 ); assert( resp_b[ 0 ] == ( char ) 0xC3 ); assert( resp_b[ 1 ] == ( char ) 0x50 ); } if ( send_or_die( dgram[ 0 ], data_c, sizeof( data_c ) ) != -1 ) { assert( read_exactly( stream[ 0 ], resp_c, sizeof( resp_c ) ) == 0 ); assert( resp_c[ 0 ] == ( char ) 0xFF ); assert( resp_c[ 1 ] == ( char ) 0xFF ); } /* Poslání dat proudu, který byl na druhé straně zavřen: */ close_or_die( stream[ 0 ], "stream socket" ); send_or_die( dgram[ 0 ], data_a, strlen( data_a ) ); assert( reap( pid ) == 0 ); close_or_die( dgram[ 0 ], "dgram socket" ); return 0; }