dalsi predchozi obsah
Dalsi: Zaver Predchozi: Meziprocesni komunikace

  Sitovani





Uvod

V teto kapitole si popiseme zaklady komunikace v pocitacovych sitich, kde mohou byt zapojeny i jine systemy, nez jen systemy typu unix. Dozvime se take, jak vyuzivat tyto sluzby v unixu, pomoci sluzby, ktera se nazyva sokety.



ISO OSI

Nejdrive trocha teorie:

Aby mohly ruzne systemy spolu komunikovat po sitich, vypracoval mezinarodni normalizacni urad ISO takzvany referencni model OSI (Reference Model for Open System Interconection). Jeho zakladem je rozdeleni vsech cinnosti pri vymene dat do sedmi casti -- vrstev, pocinaje fyzickou definici spoje a konce aplikacnimi programy -- viz. obr. 19. Kazda vrstva definuje vlastnosti svych dvou rozhrani, tj. sluzby, poskytovane vyssi vrstve a sluzby, poskytovane nizsi vrstve. Vrstvy jsou definovany i tvarem prenasenych dat.

Zde ma byt moc hezky obrazek 'isoosi', skoda, ze ho nevidite

Obrazek 19: Vrstvova architektura modelu OSI

Jednotlive vrstvy si ve strucnosti popiseme:

Fyzicka vrstva
(Physical Layer) se zabyva elektrickymi a mechanickymi vlastnostmi prenosoveho media. Definuje druh kabelu, tvary konektoru, atd. Jejim ukolem je prenos jednotlivych bitu po vedeni.

Linkova vrstva
(Data Link Layer) poskytuje vyssim vrstvam sluzby, ktere jsou jiz nezavisle na typu media. K jejim ukolum patri prevod slov nebo znaku pocitace na seriovou posloupnost bitu a naopak. Doplnuje data o synchronizacni posloupnost a cilovou a zdrojovou adresu. Vytvari tzv. ramce.

Sitova vrstva
(Network Layer) zajistuje prepravu paketu mezi jednotlivymi uzly site. Take se v teto vrstve muze rozhodovat, kudy se bude smerovat dany paket. Pro komunikaci se pracuje bud s datagramy nebo s virtualnimi spoji.

Transportni vrstva
(Transport Layer) zajistuje rozkladani zprav na jednotlive pakety a jejich opetovne skladani, navazovani a ruseni transportnich spoju, koncove rizeni toku a adresaci procesu v siti. Pokud nejsou k dispozici v nizsich vrstvach virtualni spoje, pracuje pouze s datagramy a dokaze se vyrovnat i s jejich zamenou. Jsou zde sluzby jako CONNECT -- navazani spoje, LISTEN -- ocekavani navazani spoje, SEND -- vysila zpravu, ...

Relacni vrstva
(Session Layer) definuje relaci jako casovy usek, ve kterem probiha aktivita uzivatele. Relace jsou otevirany, probiha v nich prenos dat a po skonceni jsou uzavirany. Overuje pristupova prava, eviduje provoz.

Prezentacni vrstva
(Presentation Layer) se zabyva tvarem dat, ve kterem jsou predavana uzivateli. Muze data komprimovat, dekomprimovat, utajovat informaci ci jinak kodovat.

Aplikacni vrstva
(Application Layer), to jsou programy provozovane uzivateli jako elektronicka posta, prenos souboru, databazove programy, ...



  TCP/IP -- internetove protokoly

V unixu se pro prenos v sitich pouzivaji casto protokoly tridy TCP/IP. V nazvu slysime sice jen dva protokoly, ale patri k nim jeste dalsi. Jejich vzajemne vazby a analogie k jednotlivym OSI vrstvam lze vysledovat z obr. 20.

Zde ma byt moc hezky obrazek 'tcpip', skoda, ze ho nevidite

Obrazek 20: Vrstvy OSI v protokolech TCP/IP

TCP
(Transmission Control Protocol) zajistuje spolehlive spojeni, s plnym duplexem. Tento protokol pouziva vetsina aplikaci, proto je podle nej nazvana cela tato rodina protokolu.

UDP
(User Datagram Protocol) je protokol bez vytvareni spojeni, neni zaruceno doruceni adresatovi.

ICMP
(Internet Control Message Protocol) prenasi chyby a ridici informace. Tyto informace jsou prenaseny pomoci IP paketu. Aplikacni programy s nimi vetsinou nepracuji, generuje je sam sitovy software.

IP
(Internet Protocol) prenasi vlastni pakety protokolu TCP, UDP, ICMP. Jak je videt na obr. 20, uzivatelske programy vetsinou nepotrebuji pracovat s touto vrstvou.

ARP
(Address Resolution Protocol) prevadi internetove adresy na hardwarove adresy. Tento protokol a nasledujici, RARP, je potreba jen v nekterych sitich.

RARP
(Reverse Address Resolution Protocol) prevadi hardwarove adresy na internetove.



Internetove adresy

Internetova adresa je (zatim) 32 bitova a obsahuje jak cislo site (netid), tak i cislo pocitace v siti (hostid). Tyto adresy se deli do nekolika trid, podle velikosti site, viz obr. 21. Nekdy se vyuziva v casti hostid i tzv. podsite.

Zde ma byt moc hezky obrazek 'ipadresy', skoda, ze ho nevidite

Obrazek 21: Tridy IP adres

Internetove adresy se zapisuji v desitkove soustave, po bajtech a jsou oddelene teckou, napr. 147.32.80.1 -- coz je jeden pocitac na CVUT FEL.



Sokety

Berkeleyovske sokety (sockets) jsou jednou z nadstaveb (API -- Application Program Interface) nad komunikacnimi protokoly. Dalsi je napr. System V Transport Layer Interface (TLI).

Sokety slouzi jako nadstavba nad internetovskymi protokoly, dale nad tzv. Unix domain protokoly, ktere slouzi pro komunikaci mezi procesy na jednom pocitaci, a nad protokoly XNS -- Xerox Network Systems.

My se zde zamerime hlavne na protokoly TCP/IP a Unix domain.

Vetsina sitovych systemovych volani pouziva ukazatel na tuto strukturu, popsanou v <sys/socket.h>:

  struct sockaddr {
    u_short  sa_family;   /* rodina adres; jedna z~AF_xxx */
    char     sa_data[14]; /* az 14 bytu; zavisi na protokolu */
  }

Pro internetovskou rodinu jsou tyto definovany struktury v <netinet/in.h>:

  struct in_addr {
    u_long  s_addr;   /* 32 bitova adresa */
  };

  struct sockaddr_in {
    short           sin_family;  /* AF_INET */
    u_short         sin_port;    /* 16 bitove cislo portu */
    struct in_addr  sin_addr;    /* 32 bitova adresa */
    char            sin_zero[8]; /* nepouzito */
  }

Pro Unix doman je definovana struktura v <sys/un.h>:

  struct sockaddr_un {
    short           sun_family;    /* AF_UNIX */
    char            sun_path[108]; /* cesta */

Oproti zvyklostem neni sun_path ukoncena nulou. Je take videt, ze struktura je delsi, nez obecna sockaddr. Nastesti soucasne systemy podporuji az 110 bajtovou strukturu. Ale napr. smerovaci tabulky v jadre podporuji pouze 16 bajtovou strukturu, kterou vsak protokoly Unix domain nepotrebuji...

Typicky scenar komunikace pro spojovane protokoly je videt na obr. 22

Zde ma byt moc hezky obrazek 'spojovan', skoda, ze ho nevidite

Obrazek 22: Soketove funkce pro spojovane protokoly

a pro nespojovane je na obr. 23.

Zde ma byt moc hezky obrazek 'nespojov', skoda, ze ho nevidite

Obrazek 23: Soketove funkce pro nespojovane protokoly



Funkce socket

Prvni veci, kterou proces pri sitovani dela, je volani funkce socket, ktera specifikuje komunikacni protokol.

#include <sys/types.h>
#include <sys/socket.h>
int socket (int family, int type, int protocol);
Vraci: deskriptor soketu kdyz OK, -1 pri chybe

Rodina adres family je jedna z:

AF_UNIX interni unixove protokoly
AF_INET internetove protokoly
AF_NS protokoly Xerox NS
AF_IMPLINK vrstva Interface Message Processor

Typ soketu type muze byt:

SOCK_STREAM proudovy soket (spojovana sluzba)
SOCK_DGRAM datagramovy soket (nespojovana sluzba)
SOCK_RAW surovy soket
SOCK_SEQPACKET soketova posloupnost paketu (pro XNS)
SOCK_RDM soket pro spolehlive dorucitelne zpravy (jeste neimplementovano)

Jsou dovoleny jen nektere kombinace family a type, viz tab. 15.

 

AF_UNIX AF_INET AF_NS
SOCK_STREAM Ano TCP SPP
SOCK_DGRAM Ano UDP IDP
SOCK_RAW IP Ano
SOCK_SEQPACKET SPP
Tabulka 15: Protokoly odpovidajici family a type

Protokoly, v jejichz kolonkach v tabulce 15 je Ano, nemaji specificke jmeno. Prazdne kolonky znamenaji, ze protokol neni implementovan.

Argument protocol byva vetsinou 0, coz nastavi implicitni, ktery je videt z tab. 15. Dalsi internetovske protokoly lze najit v souboru <netinet/in.h> (napr. IPPROTO_ICMP).



  Funkce socketpair

#include <sys/types.h>
#include <sys/socket.h>
int socketpair (int family, int type, int protocol, int sockvec[2]);
Vraci: 0 kdyz OK, -1 pri chybe

Funkce vraci dva nepojmenovane soketove deskriptory v poli sockvec. Je podobna funkci pipe, viz kap. 9. Narozdil od pipe vytvari ale obousmernou, tzv. proudovou rouru.

Jsou dovolene jen dve moznosti volani:

  int     rc, sockfd[2];

  rc = socketpair (AF_UNIX, SOCK_STREAM, 0, sockfd);
nebo
  rc = socketpair (AF_UNIX, SOCK_DGRAM, 0, sockfd);



Funkce bind

Funkce bind priradi jmeno nepojmenovanemu soketu.

#include <sys/types.h>
#include <sys/socket.h>
int bind (int sockfd, struct sockaddr *myaddr, int addrlen);
Vraci: 0 kdyz OK, -1 pri chybe

myaddr obsahuje identifikator zavisejici na prenosovych protokolech.



Funkce connect

K vytvoreni spojeni se serverem pouzije klient funkci connect.

#include <sys/types.h>
#include <sys/socket.h>
int connect (int sockfd, struct sockaddr *servaddr, int addrlen);
Vraci: 0 kdyz OK, -1 pri chybe

sockfd vratila funkce socket. servadrs je adresa serveru. Pro datagramy se spojeni nevytvori.



Funkce listen

Funkce listen je pouzivana serverem pro spojovane sluzby, kdyz si preje, aby se s nim nekdo spojil.

#include <sys/types.h>
#include <sys/socket.h>
int listen (int sockfd, int backlog);
Vraci: 0 kdyz OK, -1 pri chybe

backlog urcuje, kolik pozadavku se ma ulozit do fronty, nez se spusti server. Vetsinou se nastavuje na 5, coz je zatim maximalni povolena hodnota.



Funkce accept

Funkci accept vola server vetsinou po listen.

#include <sys/types.h>
#include <sys/socket.h>
int accept (int sockfd, struct sockaddr *peer, int *addrlen);
Vraci: 0 kdyz OK, -1 pri chybe

accept vezme prvni pozadavek z fronty pozadavku a vytvori dalsi soket se stejnymi vlastnostmi jako sockfd. Pokud nejsou ve fronte zadne pozadavky, funkce ceka. Lze ji nastavit i tak, aby necekala, pomoci funkci v kap. "Nastavovani parametru soketu".

Adresu klienta server zjisti z peer a addrlen.



Funkce send, sendto, recv a recvfrom

Vlastni prenos dat se deje pomoci funkci send, sendto, recv a recvfrom. Tyto funkce jsou podobne standardnim funkcim read (kap. "Funkce read") a write (kap. "Funkce write") s dalsimi argumenty.

#include <sys/types.h>
#include <sys/socket.h>
int send (int sockfd, char *buff, int nbytes, int flags);
int sendto (int sockfd, char *buff, int nbytes, int flags, struct sockaddr *to, int addrlen);
int recv (int sockfd, char *buff, int nbytes, int flags);
int recvfrom (int sockfd, char *buff, int nbytes, int flags, struct sockaddr *from, int *addrlen);
Vraci: pocet poslanych/prijatych znaku kdyz OK, -1 pri chybe

flags jsou bud 0 nebo logicky pricitaji tyto konstanty:

MSG_OOB posila/prijima out-of-band data
MSG_PEEK podiva se na prijata data, aniz je vymaze z fronty
MSG_DONTROUTE nebude smerovat pri posilani

Funkce sendto a recvfrom se pouzivaji pro nespojovane protokoly.



Funkce close

Na zavirani soketu se pouziva standardni funkce close, viz kap. Funkce close.

System se normalne z teto funkce vraci okamzite, ale jadro se snazi poslat data, ktera jsou ve fronte.



Funkce shutdown

Funkce shutdown nabizi vice moznosti, jak uzavrit spojeni, nez close.

#include <sys/socket.h>
int shutdown (int sockfd, int howto);
Vraci: 0 kdyz OK, -1 pri chybe

Pokud je , soket neprijme jiz zadna data, kdyz je , soket jiz nic nevysle, a kdyz je , pak jiz pres soket neprojdou zadna data tam ani zpet.



Funkce na zmenu poradi bajtu

Tyto funkce meni poradi bajtu ve slovech podle architektury pocitace.

#include <sys/types.h>
#include <netinet/in.h>
u_long htonl (u_long hostlong);
u_short htons (u_short hostshort);
u_long ntohl (u_long netlong);
u_short ntohs (u_short netshort);
Vraci: prekonvertovane slovo

Tyto funkce byly navrzeny pro internetove protokoly. Funguji vsak i pro XNS. Na systemech, ktere maji stejne poradi, jako internetove protokoly (Motorola 68000), jsou to jen nulova makra.



Bajtove operace

Protoze se pri komunikaci pouzivaji retezce, ktere nekonci 0, jak je zvykem v C, je nutno nad nimi definovat prenositelne operace.

#include <string.h>
void bcopy (char *src, char *dest, int nbytes);
void bzero (char *dest, int nbytes);
int bcmp (char *ptr1, char *ptr2, int nbytes);
Vraci: 0 kdyz jsou stejne, jinak nenulovou hodnotu

bcopy kopiruje dany pocet bajtu z retezce src do dest. Retezce mohou obsahovat i nuly. bzero plni retezec danym poctem nul. bcmp porovnava dva retezce, ve kterych mohou byt i nuly.



Funkce na konverzi adres

Jak bylo receno v kap. "TCP/IP -- internetove protokoly", internetove adresy se pisi po bajtech, oddeleny teckou. Nasledujici funkce prevadeji tuto formu na strukturu in_addr (viz tez kap. "TCP/IP -- internetove protokoly") a naopak.

#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
unsigned long inet_addr (char *ptr);
char *inet_ntoa (struct in_addr inaddr);
Vraci: adresu

Funkce inet_addr je jiz trochu zastarala, nebot dobre neindikuje chybu. Nova funkce se jmenuje inet_aton.



Jednoduchy priklad

Ted vyzkousime zakladni soketova systemova volani na jednoduchem prikladu:

Nejdrive si predstavime jednotlive podpurne podprogramy, nejdrive pro spojovane sluzby. Prvni cte ze soketoveho deskriptoru.

/*
 * Read "n" bytes from a descriptor.
 * Use in place of read() when fd is a stream socket.
 */

int
readn(fd, ptr, nbytes)
register int    fd;
register char    *ptr;
register int    nbytes;
{
    int    nleft, nread;

    nleft = nbytes;
    while (nleft > 0) {
        nread = read(fd, ptr, nleft);
        if (nread < 0)
            return(nread);        /* error, return < 0 */
        else if (nread == 0)
            break;            /* EOF */

        nleft -= nread;
        ptr   += nread;
    }
    return(nbytes - nleft);        /* return >= 0 */
}

Dalsi pise do soketoveho deskriptoru pres spojovane sluzby.
/*
 * Write "n" bytes to a descriptor.
 * Use in place of write() when fd is a stream socket.
 */

int
writen(fd, ptr, nbytes)
register int    fd;
register char    *ptr;
register int    nbytes;
{
    int    nleft, nwritten;

    nleft = nbytes;
    while (nleft > 0) {
        nwritten = write(fd, ptr, nleft);
        if (nwritten <= 0)
            return(nwritten);        /* error */

        nleft -= nwritten;
        ptr   += nwritten;
    }
    return(nbytes - nleft);
}

Nasledujici funkce cte jeden radek.
/*
 * Read a line from a descriptor.  Read the line one byte at a time,
 * looking for the newline.  We store the newline in the buffer,
 * then follow it with a null (the same as fgets(3)).
 * We return the number of characters up to, but not including,
 * the null (the same as strlen(3)).
 */

int
readline(fd, ptr, maxlen)
register int    fd;
register char    *ptr;
register int    maxlen;
{
    int    n, rc;
    char    c;

    for (n = 1; n < maxlen; n++) {
        if ( (rc = read(fd, &c, 1)) == 1) {
            *ptr++ = c;
            if (c == '\n')
                break;
        } else if (rc == 0) {
            if (n == 1)
                return(0);    /* EOF, no data read */
            else
                break;        /* EOF, some data was read */
        } else
            return(-1);    /* error */
    }

    *ptr = 0;
    return(n);
}

Tato funkce je pouzivana serverem k opakovani radku na vstupu zpet na vystup.
/*
 * Read a stream socket one line at a time, and write each line back
 * to the sender.
 *
 * Return when the connection is terminated.
 */

#define    MAXLINE    512

str_echo(sockfd)
int    sockfd;
{
    int    n;
    char    line[MAXLINE];

    for ( ; ; ) {
        n = readline(sockfd, line, MAXLINE);
        if (n == 0)
            return;        /* connection terminated */
        else if (n < 0)
            err_dump("str_echo: readline error");

        if (writen(sockfd, line, n) != n)
            err_dump("str_echo: writen error");
    }
}

Nasledujici funkci pouziva klient.
/*
 * Read the contents of the FILE *fp, write each line to the
 * stream socket (to the server process), then read a line back from
 * the socket and write it to the standard output.
 *
 * Return to caller when an EOF is encountered on the input file.
 */

#include    <stdio.h>
#define    MAXLINE    512

str_cli(fp, sockfd)
register FILE    *fp;
register int    sockfd;
{
    int    n;
    char    sendline[MAXLINE], recvline[MAXLINE + 1];

    while (fgets(sendline, MAXLINE, fp) != NULL) {
        n = strlen(sendline);
        if (writen(sockfd, sendline, n) != n)
            err_sys("str_cli: writen error on socket");

        /*
         * Now read a line from the socket and write it to
         * our standard output.
         */

        n = readline(sockfd, recvline, MAXLINE);
        if (n < 0)
            err_dump("str_cli: readline error");
        recvline[n] = 0;    /* null terminate */
        fputs(recvline, stdout);
    }

    if (ferror(fp))
        err_sys("str_cli: error reading file");
}

Dalsi funkci pouziva server. Je zde pocitano s vice druhy protokolu.
/*
 * Read a datagram from a connectionless socket and write it back to
 * the sender.
 *
 * We never return, as we never know when a datagram client is done.
 */

#include    <sys/types.h>
#include    <sys/socket.h>

#define    MAXMESG    2048

dg_echo(sockfd, pcli_addr, maxclilen)
int        sockfd;
struct sockaddr    *pcli_addr;
                   /* ptr to appropriate sockaddr_XX structure */
int        maxclilen;    /* sizeof(*pcli_addr) */
{
    int    n, clilen;
    char    mesg[MAXMESG];

    for ( ; ; ) {
        clilen = maxclilen;
        n = recvfrom(sockfd, mesg, MAXMESG, 0, pcli_addr, &clilen);
        if (n < 0)
            err_dump("dg_echo: recvfrom error");

        if (sendto(sockfd, mesg, n, 0, pcli_addr, clilen) != n)
            err_dump("dg_echo: sendto error");
    }
}

Nasledujici funkce je pro klienta, vyuzivajiciho nespojovanych sluzeb.
/*
 * Read the contents of the FILE *fp, write each line to the
 * datagram socket, then read a line back from the datagram
 * socket and write it to the standard output.
 *
 * Return to caller when an EOF is encountered on the input file.
 */

#include    <stdio.h>
#include    <sys/types.h>
#include    <sys/socket.h>

#define    MAXLINE    512

dg_cli(fp, sockfd, pserv_addr, servlen)
FILE        *fp;
int        sockfd;
struct sockaddr    *pserv_addr;
                   /* ptr to appropriate sockaddr_XX structure */
int        servlen;    /* actual sizeof(*pserv_addr) */
{
    int    n;
    char    sendline[MAXLINE], recvline[MAXLINE + 1];

    while (fgets(sendline, MAXLINE, fp) != NULL) {
        n = strlen(sendline);
        if (sendto(sockfd, sendline, n, 0, pserv_addr, servlen) != n)
            err_dump("dg_cli: sendto error on socket");

        /*
         * Now read a message from the socket and write it to
         * our standard output.
         */

        n = recvfrom(sockfd, recvline, MAXLINE, 0,
                (struct sockaddr *) 0, (int *) 0);
        if (n < 0)
            err_dump("dg_cli: recvfrom error");
        recvline[n] = 0;    /* null terminate */
        fputs(recvline, stdout);
    }

    if (ferror(fp))
        err_dump("dg_cli: error reading file");
}


Priklad na TCP

Nejdrive ukazeme hlavickovy soubor inet.h, ktery pouzivame i v prikladu na UDP.

/*
 * Definitions for TCP and UDP client/server programs.
 */

#include    <stdio.h>
#include    <sys/types.h>
#include    <sys/socket.h>
#include    <netinet/in.h>
#include    <arpa/inet.h>

#define    SERV_UDP_PORT    6000
#define    SERV_TCP_PORT    6000
#define    SERV_HOST_ADDR    "192.43.235.6"    /* host addr for server */

char    *pname;

A konecne je tu vlastni server.
/*
 * Example of server using TCP protocol.
 */

#include    "inet.h"

main(argc, argv)
int    argc;
char    *argv[];
{
    int            sockfd, newsockfd, clilen, childpid;
    struct sockaddr_in    cli_addr, serv_addr;

    pname = argv[0];

    /*
     * Open a TCP socket (an Internet stream socket).
     */

    if ( (sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
        err_dump("server: can't open stream socket");

    /*
     * Bind our local address so that the client can send to us.
     */

    bzero((char *) &serv_addr, sizeof(serv_addr));
    serv_addr.sin_family      = AF_INET;
    serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    serv_addr.sin_port        = htons(SERV_TCP_PORT);

    if (bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr))
        < 0)
        err_dump("server: can't bind local address");

    listen(sockfd, 5);

    for ( ; ; ) {
        /*
         * Wait for a connection from a client process.
         * This is an example of a concurrent server.
         */

        clilen = sizeof(cli_addr);
        newsockfd = accept(sockfd, (struct sockaddr *) &cli_addr,
                                     &clilen);
        if (newsockfd < 0)
            err_dump("server: accept error");

        if ( (childpid = fork()) < 0)
            err_dump("server: fork error");

        else if (childpid == 0) {    /* child process */
            close(sockfd);        /* close original socket */
            str_echo(newsockfd);    /* process the request */
            exit(0);
        }

        close(newsockfd);        /* parent process */
    }
}

Nasleduje klient.
/*
 * Example of client using TCP protocol.
 */

#include    "inet.h"

main(argc, argv)
int    argc;
char    *argv[];
{
    int            sockfd;
    struct sockaddr_in    serv_addr;

    pname = argv[0];

    /*
     * Fill in the structure "serv_addr" with the address of the
     * server that we want to connect with.
     */

    bzero((char *) &serv_addr, sizeof(serv_addr));
    serv_addr.sin_family      = AF_INET;
    serv_addr.sin_addr.s_addr = inet_addr(SERV_HOST_ADDR);
    serv_addr.sin_port        = htons(SERV_TCP_PORT);

    /*
     * Open a TCP socket (an Internet stream socket).
     */

    if ( (sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
        err_sys("client: can't open stream socket");

    /*
     * Connect to the server.
     */

    if (connect(sockfd, (struct sockaddr *) &serv_addr,
                            sizeof(serv_addr)) < 0)
        err_sys("client: can't connect to server");

    str_cli(stdin, sockfd);        /* do it all */

    close(sockfd);
    exit(0);
}


Priklad na UDP

Tento nas priklad neni spolehlivy, tj. nezarucuje doruceni paketu. Server vypada takto:

/*
 * Example of server using UDP protocol.
 */

#include    "inet.h"

main(argc, argv)
int    argc;
char    *argv[];
{
    int            sockfd;
    struct sockaddr_in    serv_addr, cli_addr;

    pname = argv[0];

    /*
     * Open a UDP socket (an Internet datagram socket).
     */

    if ( (sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
        err_dump("server: can't open datagram socket");

    /*
     * Bind our local address so that the client can send to us.
     */

    bzero((char *) &serv_addr, sizeof(serv_addr));
    serv_addr.sin_family      = AF_INET;
    serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    serv_addr.sin_port        = htons(SERV_UDP_PORT);

    if (bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr))
        < 0)
        err_dump("server: can't bind local address");

    dg_echo(sockfd, (struct sockaddr *) &cli_addr, sizeof(cli_addr));

        /* NOTREACHED */
}

A klient zase takto:
/*
 * Example of client using UDP protocol.
 */

#include    "inet.h"

main(argc, argv)
int    argc;
char    *argv[];
{
    int            sockfd;
    struct sockaddr_in    cli_addr, serv_addr;

    pname = argv[0];

    /*
     * Fill in the structure "serv_addr" with the address of the
     * server that we want to send to.
     */

    bzero((char *) &serv_addr, sizeof(serv_addr));
    serv_addr.sin_family      = AF_INET;
    serv_addr.sin_addr.s_addr = inet_addr(SERV_HOST_ADDR);
    serv_addr.sin_port        = htons(SERV_UDP_PORT);

    /*
     * Open a UDP socket (an Internet datagram socket).
     */

    if ( (sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
        err_dump("client: can't open datagram socket");

    /*
     * Bind any local address for us.
     */

    bzero((char *) &cli_addr, sizeof(cli_addr));    /* zero out */
    cli_addr.sin_family      = AF_INET;
    cli_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    cli_addr.sin_port        = htons(0);
    if (bind(sockfd, (struct sockaddr *) &cli_addr, sizeof(cli_addr))
        < 0)
        err_dump("client: can't bind local address");

    dg_cli(stdin, sockfd, (struct sockaddr *) &serv_addr,
           sizeof(serv_addr));

    close(sockfd);
    exit(0);
}


Priklad na Unixove protokoly spojovane

Spojovany server je podobny prikladu na TCP. Nejdrive si ukazeme hlavickovy soubor unix.h, vyuzivany v dalsich prikladech.

/*
 * Definitions for UNIX domain stream and datagram client/server
 * programs.
 */

#include    <stdio.h>
#include    <sys/types.h>
#include    <sys/socket.h>
#include    <sys/un.h>

#define    UNIXSTR_PATH    "./s.unixstr"
#define    UNIXDG_PATH    "./s.unixdg"
#define    UNIXDG_TMP    "/tmp/dg.XXXXXX"

char    *pname;

Server je toto:
/*
 * Example of server using UNIX domain stream protocol.
 */

#include    "unix.h"

main(argc, argv)
int    argc;
char    *argv[];
{
    int            sockfd, newsockfd, clilen, childpid, servlen;
    struct sockaddr_un    cli_addr, serv_addr;

    pname = argv[0];

    /*
     * Open a socket (a UNIX domain stream socket).
     */

    if ( (sockfd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0)
        err_dump("server: can't open stream socket");

    /*
     * Bind our local address so that the client can send to us.
     */

    bzero((char *) &serv_addr, sizeof(serv_addr));
    serv_addr.sun_family = AF_UNIX;
    strcpy(serv_addr.sun_path, UNIXSTR_PATH);
    servlen = strlen(serv_addr.sun_path) + sizeof(serv_addr.sun_family);

    if (bind(sockfd, (struct sockaddr *) &serv_addr, servlen) < 0)
        err_dump("server: can't bind local address");

    listen(sockfd, 5);

    for ( ; ; ) {
        /*
         * Wait for a connection from a client process.
         * This is an example of a concurrent server.
         */

        clilen = sizeof(cli_addr);
        newsockfd = accept(sockfd, (struct sockaddr *) &cli_addr,
                                     &clilen);
        if (newsockfd < 0)
            err_dump("server: accept error");

        if ( (childpid = fork()) < 0)
            err_dump("server: fork error");

        else if (childpid == 0) {    /* child process */
            close(sockfd);        /* close original socket */
            str_echo(newsockfd);    /* process the request */
            exit(0);
        }

        close(newsockfd);        /* parent process */
    }
}

Klient je toto:
/*
 * Example of client using UNIX domain stream protocol.
 */

#include    "unix.h"

main(argc, argv)
int    argc;
char    *argv[];
{
    int            sockfd, servlen;
    struct sockaddr_un    serv_addr;

    pname = argv[0];

    /*
     * Fill in the structure "serv_addr" with the address of the
     * server that we want to send to.
     */

    bzero((char *) &serv_addr, sizeof(serv_addr));
    serv_addr.sun_family = AF_UNIX;
    strcpy(serv_addr.sun_path, UNIXSTR_PATH);
    servlen = strlen(serv_addr.sun_path) + sizeof(serv_addr.sun_family);

    /*
     * Open a socket (an UNIX domain stream socket).
     */

    if ( (sockfd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0)
        err_sys("client: can't open stream socket");

    /*
     * Connect to the server.
     */

    if (connect(sockfd, (struct sockaddr *) &serv_addr, servlen) < 0)
        err_sys("client: can't connect to server");

    str_cli(stdin, sockfd);        /* do it all */

    close(sockfd);
    exit(0);
}


Priklad na Unixove protokoly nespojovane

V tomto datagramovem prikladu je videt zajimavy problem, se kterym jsme se jeste nesetkali -- sokety jsou identifikovany jmeny souboru a proto klient musi vyprodukovat pro bind unikatni jmeno souboru.

Server:

/*
 * Example of server using UNIX domain datagram protocol.
 */

#include    "unix.h"

main(argc, argv)
int    argc;
char    *argv[];
{
    int            sockfd, servlen;
    struct sockaddr_un    serv_addr, cli_addr;

    pname = argv[0];

    /*
     * Open a socket (a UNIX domain datagram socket).
     */

    if ( (sockfd = socket(AF_UNIX, SOCK_DGRAM, 0)) < 0)
        err_dump("server: can't open datagram socket");

    /*
     * Bind our local address so that the client can send to us.
     */

    unlink(UNIXDG_PATH);    /* in case it was left from last time */
    bzero((char *) &serv_addr, sizeof(serv_addr));
    serv_addr.sun_family = AF_UNIX;
    strcpy(serv_addr.sun_path, UNIXDG_PATH);
    servlen = sizeof(serv_addr.sun_family) + strlen(serv_addr.sun_path);

    if (bind(sockfd, (struct sockaddr *) &serv_addr, servlen) < 0)
        err_dump("server: can't bind local address");

    dg_echo(sockfd, (struct sockaddr *) &cli_addr, sizeof(cli_addr));
        /* NOTREACHED */
}

Klient:
/*
 * Example of client using UNIX domain datagram protocol.
 */

#include    "unix.h"

main(argc, argv)
int    argc;
char    *argv[];
{
    int            sockfd, clilen, servlen;
    char            *mktemp();
    struct sockaddr_un    cli_addr, serv_addr;

    pname = argv[0];

    /*
     * Fill in the structure "serv_addr" with the address of the
     * server that we want to send to.
     */

    bzero((char *) &serv_addr, sizeof(serv_addr));
    serv_addr.sun_family = AF_UNIX;
    strcpy(serv_addr.sun_path, UNIXDG_PATH);
    servlen = sizeof(serv_addr.sun_family) + strlen(serv_addr.sun_path);

    /*
     * Open a socket (a UNIX domain datagram socket).
     */

    if ( (sockfd = socket(AF_UNIX, SOCK_DGRAM, 0)) < 0)
        err_dump("client: can't open datagram socket");

    /*
     * Bind a local address for us.
     * In the UNIX domain we have to choose our own name (that
     * should be unique).  We'll use mktemp() to create a unique
     * pathname, based on our process id.
     */

    bzero((char *) &cli_addr, sizeof(cli_addr));    /* zero out */
    cli_addr.sun_family = AF_UNIX;
    strcpy(cli_addr.sun_path, UNIXDG_TMP);
    mktemp(cli_addr.sun_path);
    clilen = sizeof(cli_addr.sun_family) + strlen(cli_addr.sun_path);

    if (bind(sockfd, (struct sockaddr *) &cli_addr, clilen) < 0)
        err_dump("client: can't bind local address");

    dg_cli(stdin, sockfd, (struct sockaddr *) &serv_addr, servlen);

    close(sockfd);
    unlink(cli_addr.sun_path);
    exit(0);
}


  Nastavovani parametru soketu

Je nekolik zpusobu, jak menit parametry soketu:

Funkce fcntl a ioctl byly popsany v kapitole "Souborovy vstup a vystup".

Funkci getsockopt lze ziskat informace o soketu.

#include <sys/types.h>
#include <sys/socket.h>
int getsockopt (int sockfd, int level, int optname, char *optval, int *optlen);
int setsockopt (int sockfd, int level, int optname, char *optval, int optlen);
Vraci: 0 kdyz OK, -1 pri chybe



Cviceni

  1. ??


dalsi predchozi obsah
Dalsi: Zaver Predchozi: Meziprocesni komunikace

Ladislav Dobias
Sat Nov 1 15:38:32 MET 1997