#define _POSIX_C_SOURCE 200809L #include /* assert */ #include /* errno */ #include /* fork, close */ #include /* send, recv */ #include /* uint8_t */ #include /* Tato ukázka demonstruje použití systémového volání ‹recv› na * datagramovém socketu. Parametry podprogramu ‹count_until› mají * tento význam: * * • ‹sock_fd› je popisovač socketu, na který jsou doručovány * datagramy, * • ‹value› je hodnota, kterou budeme považovat za ukončovací – * dorazí-li jednobajtový datagram s hodnotou ‹value›, podprogram * skončí a vrátí celkový počet přijatých datagramů. * * Nastane-li systémová chyba, návratová hodnota bude -1. */ int count_until( int sock_fd, uint8_t value ) { int count = 0; /* Paměť pro uložení příchozího datagramu. Pozor, je velmi * důležité zde nezaměnit znaménkovost – typ ‹char› může být * znaménkový a implicitní konverze ‹char› na ‹int› při srovnání * pomocí ‹==› pak může vést na záporné číslo. Znaménkovost * ‹buffer› tedy musí souhlasit se znaménkovostí ‹value›. */ uint8_t buffer[ 2 ]; /* Podprogram bude v cyklu přijímat datagramy. Cyklus nemá * v tuto chvíli jasnou ukončovací podmínku – ukončíme ho * podmíněným příkazem v těle. */ while ( 1 ) { /* Čekáme na datagram velikosti 1 s daným obsahem. Větší * datagram «nesmí» podprogram ukončit, proto musíme mít * jistotu, že velikost byla právě 1. Protože ‹recv› vrací * počet bajtů zapsaných do paměti ‹buffer›, kdyby měla tato * velikost 1, nebylo by možné tuto podmínku ověřit. */ int recvd = recv( sock_fd, buffer, 2, 0 ); /* Samozřejmě nezapomeneme ošetřit možné chyby. */ if ( recvd == -1 ) return -1; /* Úspěšně jsme přijali datagram – protože počítáme všechny * datagramy (včetně ukončovacího), toto je ideální místo na * zvýšení počítadla. */ ++ count; /* Konečně ověříme, zda se jedná o ukončovací paket. * Všimněte si pořadí, ve kterém je podmínka zapsaná – je * důležité, protože jinak by mohlo dojít k nedefinovanému * chování (v situaci, kdy má první datagram nulovou délku * by čtení z ‹buffer› přistupovalo k neinicializované * paměti). */ if ( recvd == 1 && buffer[ 0 ] == value ) return count; } /* Na toto místo se program nemůže dostat; cyklus může skončit * pouze příkazem ‹return›. */ } /* ┄┄┄┄┄┄┄ %< ┄┄┄┄┄┄┄┄┄┄ následují testy ┄┄┄┄┄┄┄┄┄┄ %< ┄┄┄┄┄┄┄ */ #include #include static void close_or_warn( int fd, const char *name ) { if ( close( fd ) == -1 ) warn( "closing %s", name ); } 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, "\xa0", 1 ); /* case 2 */ send_or_die( fd, "\xa0\x01", 2 ); send_or_die( fd, "\xa0", 1 ); /* case 3 */ send_or_die( fd, "\x01", 1 ); send_or_die( fd, "\xa0", 1 ); /* case 4 */ send_or_die( fd, "\xef\xee", 2 ); send_or_die( fd, "\xee", 1 ); close( fd ); } int main() { int fds[ 2 ]; if ( socketpair( AF_UNIX, SOCK_DGRAM, 0, fds ) == -1 ) err( 1, "socketpair" ); 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 ); return 0; } close_or_warn( client_fd, "client end of a socket" ); assert( count_until( server_fd, 0xa0 ) == 1 ); assert( count_until( server_fd, 0xa0 ) == 2 ); assert( count_until( server_fd, 0xa0 ) == 2 ); assert( count_until( server_fd, 0xee ) == 2 ); assert( reap( client_pid ) == 0 ); close_or_warn( server_fd, "server end of a socket" ); return 0; }