#define _POSIX_C_SOURCE 200809L #include /* exit */ #include /* assert */ #include /* read, write, fork, close */ #include /* Naprogramujte proceduru ‹decode_slip›, která dostane dva * popisovače – ‹in_fd›, který odkazuje na připojený proudový * socket, a ‹out_fd›, který odkazuje na datagramový socket * s nastaveným implicitním příjemcem. * * Z ‹in_fd› bude číst velmi jednoduchý zapouzdřovací protokol: * každý datagram je zakódován do jednoho rámce, přitom jednotlivé * rámce jsou v proudu dat odděleny bajtem ‹0xc0›. Oddělovač se * v takto vymezených rámcích (a tedy ani odpovídajících * datagramech) nesmí objevit.¹ Můžete předpokládat, že velikost * jednoho rámce nepřesáhne 512 bajtů. * * Procedura ‹decode_slip› konečně z každého rámce vytvoří jeden * datagram, který odešle na ‹out_fd›. */ int decode_slip( int in_fd, int out_fd ); /* ¹ Jako možné rozšíření můžete naprogramovat verzi, kde budou * bajty ‹0xc0› v datagramech povoleny – v rámcích jsou pak * nahrazeny sekvencí ‹0xdb 0xdc› a bajty ‹0xdb› z původních * datagramů jsou nahrazeny sekvencí ‹0xdb 0xdd›. */ /* ┄┄┄┄┄┄┄ %< ┄┄┄┄┄┄┄┄┄┄ následují testy ┄┄┄┄┄┄┄┄┄┄ %< ┄┄┄┄┄┄┄ */ #include /* socketpair */ #include /* waitpid */ #include /* strcmp, strlen */ static void close_or_warn( int fd, const char *name ) { if ( close( fd ) == -1 ) warn( "closing %s", name ); } 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; } pid_t spawn_decoder( int *fd_stream, int *fd_dgram ) { int sock_stream[ 2 ]; if ( socketpair( AF_UNIX, SOCK_STREAM, 0, sock_stream ) == -1 ) err( 1, "stream socketpair" ); int sock_dgram[ 2 ]; if ( socketpair( AF_UNIX, SOCK_DGRAM, 0, sock_dgram ) == -1 ) err( 1, "dgram socketpair" ); pid_t pid = fork(); if ( pid == -1 ) err( 1, "fork" ); if ( pid > 0 ) { close_or_warn( sock_stream[ 1 ], "child's stream socket in parent" ); close_or_warn( sock_dgram[ 1 ], "child's dgram socket in parent" ); *fd_stream = sock_stream[ 0 ]; *fd_dgram = sock_dgram[ 0 ]; return pid; } alarm( 1 ); close_or_warn( sock_stream[ 0 ], "parent's stream socket in child" ); close_or_warn( sock_dgram[ 0 ], "parent's dgram socket in child" ); int rv = decode_slip( sock_stream[ 1 ], sock_dgram[ 1 ] ); close_or_warn( sock_stream[ 1 ], "child's stream socket" ); close_or_warn( sock_dgram[ 1 ], "child's dgram socket" ); exit( rv ); } static int push( int fd, const char *msg ) { if ( write( fd, msg, strlen( msg ) ) == -1 ) return warn( "push write" ), 0; return 1; } static int pull( int fd, const char *msg ) { char buf[ 256 ]; int bytes_recvd = recv( fd, buf, sizeof buf - 1, 0 ); if ( bytes_recvd == -1 ) return warn( "pull recv" ), 0; buf[ bytes_recvd ] = '\0'; if ( strcmp( msg, buf ) != 0 ) return warnx( "pull unexpected message: %s", buf ), 0; return 1; } int main( void ) { int fd_stream, fd_dgram; pid_t pid = spawn_decoder( &fd_stream, &fd_dgram ); assert( push( fd_stream, "hello\xc0" ) ); assert( pull( fd_dgram, "hello" ) ); assert( push( fd_stream, "multiple\xc0" "frames\xc0" "at once\xc0" ) ); assert( pull( fd_dgram, "multiple" ) ); assert( pull( fd_dgram, "frames" ) ); assert( pull( fd_dgram, "at once" ) ); assert( push( fd_stream, "oh no\xc0" "packet " ) ); assert( pull( fd_dgram, "oh no" ) ); assert( push( fd_stream, "frag" ) ); assert( push( fd_stream, "mentation\xc0" "empty datagram\xc0\xc0" "and" ) ); assert( pull( fd_dgram, "packet fragmentation" ) ); assert( pull( fd_dgram, "empty datagram" ) ); assert( pull( fd_dgram, "" ) ); assert( push( fd_stream, " the last has no separator" ) ); close_or_warn( fd_stream, "parent's stream socket" ); assert( pull( fd_dgram, "and the last has no separator" ) ); close_or_warn( fd_dgram, "parent's dgram socket" ); assert( reap( pid ) == 0 ); }