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.
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.
Obrazek 19: Vrstvova architektura modelu OSI
Jednotlive vrstvy si ve strucnosti popiseme:
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.
Obrazek 20: Vrstvy OSI v protokolech TCP/IP
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.
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.
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
Obrazek 22: Soketove funkce pro spojovane protokoly
a pro nespojovane je na obr. 23.
Obrazek 23: Soketove funkce pro nespojovane protokoly
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 |
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).
#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 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.
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 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.
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.
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.
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 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.
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.
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.
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.
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 */
}
/* * 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); }
/* * 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); }
/* * 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"); } }
/* * 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"); }
/* * 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"); } }
/* * 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"); }
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;
/* * 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 */ } }
/* * 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); }
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 */
}
/* * 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); }
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;
/* * 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 */ } }
/* * 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); }
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 */
}
/* * 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); }
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 |
Ladislav Dobias