<< home >>
Nesúvislé fragmenty z objavovania OS Linux
<<- ->>


  • Iné

TCP/IP komunikácia (server/klient) v OS Linux

Úvod
Resolve Domain Name
Klávesnica v konzole
TCP/IP Client
TCP/IP Server
Manuálové stránky
Download
Záver
Zdroje


Úvod

Zabezpečiť komunikáciu v linuxáckej homogénnej sieti nie je až taký problém, ako by sa žiakom mohol javiť. Z hľadiska programátorského je takáto komunikácia ešte jednoduchšia, než v sieti na báze M$ Windows. Na úvod však treba uviesť a objasniť niekoľko základných pojmov (ako TCP, IP, socket a i.).

IP

Protokol IP (Internet Protokol) je komunikačný protokol, na ktorom je postavený napríklad Internet. IP protokol zaisťuje komunikáciu dvoch počítačov. Komunikácia prebieha výmenou tak zvaných IP paketov. IP paket obsahuje hlavičku a údaje, ktoré prenáša. Tieto údaje sa prenášajú na sieťové rozhrania s danou IP adresou. IP adresa je súčasťou hlavičky IP paketu. IP adresa (vo verzii protokolu IPv4) má 4 byte. Je běžné používať na Internete ako adresu nie 4 byteové číslo, ale nejaký textový reťazec, napríklad www.linux.sk. Takýto textový reťazec sa lepšie pamätá. Napríklad http://147.175.66.133 by sa asi ťažko pamätal. Dôvod, prečo boli zavedené tieto textové reťazce, doménové mená (Domain Name) je teda čisto praktický. Ak chceme komunikovať, musíme poznať IP adresu. Bežne sa reťazce doménových mien prekladajú na IP adresy pomocou DNS serverov (Domain Name Server), v časti Resolve Domain Name je jednoduchý program, ktorý by to mohol (za pomoci súboru /etc/hosts alebo DNS servera) zvládnuť.

TCP

Protokol TCP (Transmision Control Protocol) je dnes asi najpoužívanejší. Jedná sa o tak zvanú spojovú službu. Znamená to, že pred vlastnou komunikáciou  sa uskutoční (naviaže) spojenie. Všetky odoslané údaje sa potvrdzujú a na koniec je nutné spojenie ukončit (uzavrieť). TCP paket obsahuje svoju hlavičku a vlastné údaje, ktoré prenáša. Súčasťou TCP hlavičky je tak zvaný port. Jedná sa o 2 byte-ové číslo. Každá aplikácia, ktorá komunikuje pomocou TCP má pridelený svoj v rámci počítača jednoznačný port. Ak povieme, že IP protokol zaisťuje komunikáciu dvoch počítačov, tak TCP protokol zaisťuje komunikáciu dvoch aplikácií (procesov) na týchto počítačoch. TCP port možno teda považovať za jednoznačnú "adresu" aplikácie na počítači. Preto pri TCP spojení budeme zadávať vždy IP adresu (alebo doménové meno) a TCP port. Budeme teda určovať, s akým počítačom a s akou aplikáciou (procesom) na ňom chceme komunikovať. Komunikácia pomocou TCP/IP prebieha na princípe klient - server. Server je program, ktorý čaká na pripojenie. Klient je program, který spojenie naväzuje.

TCP/IP komunikácia

Od okamihu, keď používateľ odošle údaje zo svojej sieťovej aplikácie po ich fyzickú prítomnosť v sieti, putujú údaje niekoľkými vrstvami. Celý proces sa podobá z bežného života dobre známemu procesu odosielania a prijímanie listových zásielok. Žiak A najprv správu napíše, potom vloží do obálky, napíše adresu, nalepí známku a odovzdá zásielku na pošte alebo vhodí do schránky. Poštový úrad zásielku preberie, opečiatkuje (doplní dátumom prevzatia a miesta podania), triedi a roznáša ich podľa adresy. Poštový doručovateľ napokon zásielku doručí do poštovej schránky žiaka B. Ten zásielku vyberie, otvorí obálku a prečíta obsah listu.

Protokol TCP/IP

Protokol TCP/IP bol vyvinutý špeciálne pre účel Internetu a používa sa od roku 1983. Je to vlastne skupina protokolov,ktorú netvorí len TCP a IP, ale aj ďalšie, ako napr. UDP, HTTP (Hypertext Transfer Protocol), FTP (File Transfer Protocol), NNTP (Network News Transport Protocol) a iné. Pôvodný model prepojovania otvorených systémov bol štvorvrstvový. Neskoršie bol prepracovaný, doplnený a v roku 1984 prijatý v súčasnosti používaný sedemvrstvový referenčný model označovaný ako ISO/OSI. Obsahuje aj štyri vrstvy predchádzajúceho modelu.


7. aplikačná vrstva
program
6. prezentačná vrstva
prevod do tvaru zrozumiteľného pre príjemcu
5. relačná vrstva
vytvorenie a údržba spojenia s príjemcom
4. transportná vrstva
dozor na spoľahlivý prenos správ a opravy chýb
3. sieťová vrstva
vytvorenie paketov s adresami a ostatnými nutnými časťami
2. spojová vrstva
vytvorenie rámcov a ich vysielanie
1. fyzická vrstva
prenos správ vo forme elektrických impulzov


  • Aplikačná vrstva
Koncoví používatelia využívajú počítačové siete prostredníctvom rôznych sieťových aplikácií - systémov elektronické pošty, prenosu súborov, vzdialeného prihlasovanie (remote login) a pod. Začleňovať všetky tieto rôznorodé aplikácie priamo do aplikačnej vrstvy by pre ich veľkú různorodosť nebolo rozumné. Preto sa do aplikačnej vrstvy včleňujú len časti týchto aplikácií, ktoré realizujú spoločné resp. všeobecne použiteľné mechanizmy. Uvažujme ako príklad emuláciu terminálov, potrebných napríklad pre vzdialené prihlasovanie (remote login). Vo svete dnes existuje veľké množstvo rôznych terminálov, a realizovať potrebné prispôsobenie medzi rôznymi typmi terminálov je v podstate nemožné. Preto se zavádza jeden "referenčný" terminál - tzv. virtuálny terminál - a pre každý konkrétny typ terminálu se vytvorí len jediné prispôsobenie medi týmto virtuálnym terminálom a terminálom skutočným. Prostriedky pre prácu s virtuálnym terminálom pritom sú súčásťou aplikačnej vrstvy (všade sú rovnaké), zatiaľ čo prostriedky pre prispôsobenie konkrétnemu terminálu už súčásťou aplikačnej vrstvy nie sú.

  • Prezentačná vrstva
Údaje, ktoré sa prostredníctvom siete prenášajú, môžu mať povahu textov, čísel či všeobecnejších údajových štruktúr. Jednotlivé uzlové počítače však môžu používať odlišnú vnútornú reprezentáciu týchto údajov - napríklad strediskové počítače firmy IBM používajú znakový kód EBCDIC, kým väčšina ostatných pracuje s kódom ASCII. Podobne jeden počítač môže zobrazovať celé čísla v doplnkovom kóde, druhý počítač v priamom kódu a pod. Potrebné konverzie prenášaných údajov má na starosti práve prezentačná vrstva. V rámci tejto vrstvy býva tiež realizovaná prípadná kompresia prenášaných údajov, eventuélne aj ich šifrovanie.

  • Relačná vrstva
Úlohou tejto vrstvy je uskotočňovanie (naväzovanie), udržovanie a rušenie relácií (sessions) medzi koncovými účastníkmi. V rámci  naväzovania relácie si táto vrstva vyžiada na transportnej vrstve vytvorenie spojenia, prostredníctvom ktorého potom prebieha komunikácia medzi účastníkmi relácie. Pokiaľ je treba túto komunikáciu nejako riadiť (napríklad určovať, kdo má kedy vysielať, ak to nemôžu robiť obidvaja účastníci súčasne), zaisťuje to práve táto vrstva, ktorá má tiež na starosti všetko, čo je potrebné k ukončeniu relácie a zrušeniu existujúceho spojenia.

  • Transportná vrstva
Sieťová vrstva poskytuje bezprostredne vyššej vrstve služby, zaisťujúce prenos paketov medzi ľubovoľnými dvomi uzlami siete. Transportnú vrstvu preto úplne odtieňuje od skutečnej topológie siete a vytvára jej tak ilúziu, že každý uzol siete má přiame spojenie s ktorýmkoľvek iným uzlom siete. Transportnej vrstve vďaka tomu stačí zaoberať sa už len komunikácou koncových účastníkov (tzv. end-to-end komunikáciou) - teda komunikáciou medzi pôvodným odosielateľom a koncovým príjemcom. Pri odosielaní údajov zaisťuje transportná vrstva zostavovanie jednotlivých paketov, do ktorých rozdeľuje prenášené údaje, a pri príjme ich zase z paketov vyberá a skladá do pôvodného tvaru. Dokáže tak zaistiť prenos ľubovoľne veľkých správ, aj keď jednotlivé pakety majú obmedzenú veľkosť.

  • Sieťová vrstva
Sieťová vrstva zaisťuje prenos celých rámcov, ale iba medzi dvomi uzlami, medzi ktorými vedie priame spojenie. Čo ale robiť, ak spojenie medzi príjemcom a odosielateľom nie je priame, ale vedie cez jeden či viac medziľahlých uzlov. Potom musí nastúpiť sieťová vrstva, ktorá zaistí potrebné smerovanie (routing) prenášaných rámcov, označovaných teraz už ako pakety (packets). Sieťová vrstva teda zaisťuje voľbu vhodnej trasy resp. cesty (route) cez medziľahlé uzly, a tiež postupný prenos jednotlivých paketov po tejto trase od pôvodného odosieľateľa až ku koncovému príjemcovi. Sieťová vrstva si teda musí "uvedomovať" konkrétnu topológiu siete (spôsob vzájomného priameho prepojenia jednotlivých uzlov).

  • Spojová vrstva
Fyzická vrstva poskytuje ako svoje služby prostriedky pre prenos jednotlivých bitov. Bezprostredne vyššia spojová vrstva (niekedy nazývaná aj linková vrstva alebo vrstva dátového spoja) má za úlohu zaistiť pomocou týchto služieb bezchybný prenos celých blokov údajov (rádove stovky bytov), označovaných jako rámce (frames). Keďže fyzická vrstva nijako neinterpretuje jednotlivé prenášané bity, je iba na spojovej vrstve, aby správne rozpoznala začiatok a koniec rámca, i jeho jednotlivých častí.

  • Fyzická vrstva
Úloha tejto vrstvy je zdanlivo veľmi jednoduchá - zaistiť prenos jednotlivých bitov medzi príjemcom a odosielateľom prostredníctvom fyzickej prenosovej cesty, kterú táto vrstva bezprostredne ovláda. Na to je ale treba vyriešiť mnoho otázok prevažne technického charakteru - napr. akou úrovňou napätia bude reprezentovaná logická jednotka a akou logická nula, ako dlho "trvá" jeden bit, koľko kontaktov a aký tvar majú mať konektory káblov, aké signály sú týmito káblami prenášané, aký je ich význam, časový priebeh a podobne.

UDP

Protokol UDP (User Datagram Protocol) je vlastne alternatíva TCP. Jedná se o tak zvanú nespojovanú službu. Nespojovaná služba znamená, že nedochádza k naviazaniu spojenia. Jednoducho odošleme údaje na danú IP adresu a daný UDP port a nevieme, či údaje dorazili a či se nepoškodili. Žiadne potvrdenie ani nič podobného nepríde. UDP protokol je vhodný najmä v situáciách, kde by spojenie pomocou TCP bolo veľkou záťažou pre sieť (napríklad ak sa obraciame na nadradený DNS server). U TCP paketov sa musí posielať potvrdenie. U UDP  paketov nie. Súčasťou UDP hlavičky je tiež port. Ide v rámci počítača tiež o jednoznačné označenie aplikácie. Číslovanie TCP a UDP portov je na sebe nezávislé. Napríklad jedna aplikácia môže mať ppriradený TCP port 1024 ale UDP port 1024 môže byť priradený druhej aplikácií.

Socket

Socket je mechanizmus pre komunikáciu. Prvý krát sa objavil v OS BSD. Socket je veľmi všeobecný nástroj, možno sním pracovať ako so súborom, má pridelený vlastný jedinečný deskriptor. Rovnaké funkcie môžeme používať pre komunikáciu pomocou rôznych protokolov.




Resolve Domain Name


/* resolve_domain_name */

#include <stdio.h>
#include <netdb.h>
#include <netinet/in.h>
#include <arpa/inet.h>

int main(int argc, char **argv)
{
struct hostent *host;
char **aliases;
register int i;

/* osetrenie poctu argumentov */
if (argc !=
2) {
printf(
"Pouzitie: %s domain_name\n", argv[0]);
printf(
"Napríklad: %s localhost\n", argv[0]);
return(-
1);
}

/* osetrenie existencie adresy (domain_name) */
if (!(host=gethostbyname(argv[1]))) {
printf(
"Chyba: nepodarilo sa najst %s\n", argv[1]);
return(-
1);
}

/* udaje o stroji s adresou domain_name */
printf("Meno: %s\n", host->h_name);

printf(
"Aliasy:");
aliases=host->h_aliases;
while (*aliases) {
printf(
" %s", *(aliases++));
}

i=
0;
printf(
"\nIP adresy:");
while (host->h_addr_list[i]) {
/* inet_ntoa() prevod do "bodkoveho" tvaru */
printf(
" %s", inet_ntoa(*(struct in_addr *)host->h_addr_list[i++]));
}

printf(
"\nPocet IP adries: %i\n", i);
printf(
"Velkost IP adries: %iB\n", host->h_length);

return
0;
}



Klávesnica v konzole

Žiaci, ktorí programovali v jazyku Pascal, dobre poznajú funkciu KeyPressed. Žiaci, ktorí programovali v jazyku C v OS M$ DOS zase dobre poznajú funkciu kbhit(). Linuxový (unixový) ekvivalent funkcie, ktorá by zistila, či bola alebo nebola stlačená nejaká klávesa (bez toho, že by ju čítala) neexistuje. V súbore kbhit.c sú funkcie emulujúce DOSovskú funkciu kbhit(). Funkcia init_kbhit() mení nastavenie terminálu na čítanie jedného znaku, funkcia kbhit() upraví jeho chovanie, čaká na vstup a ihneď vráti riadenie programu a funkcia close_kbhit() obnoví pôvodné nastavenie terminálu.
/* void init_kbhit(void) inicializuje kbhit */
/* int kbhit(void) vracia ascii kod stlacenej klavesy alebo nulu ak nebolo nic stlacene */
/* void close_kbhit(void) konci pracu s kbhit */

#include <termios.h>
#include <term.h>
#include <curses.h>

/* deklaracia struktur pre nastavenie terminalu */
struct termios initial_settings;
struct termios new_settings;
/* premenna pre detekciu stlacenej klavesy */
int peek_character=-1;

/*******************************/
int kbhit(void)
{
int key=0;

if (kbhit_on()) key=readkey_on();
return(key);
}

/*******************************/
void init_kbhit(void)
{
tcgetattr(
0, &initial_settings);
new_settings=initial_settings;
new_settings.c_lflag &= ~ICANON;
new_settings.c_lflag &= ~ECHO;
new_settings.c_lflag &= ~ISIG;
new_settings.c_cc[VMIN]=
1;
new_settings.c_cc[VTIME]=
0;
tcsetattr(
0, TCSANOW, &new_settings);
}

/*******************************/
void close_kbhit(void)
{
tcsetattr(
0, TCSANOW, &initial_settings);
}


int kbhit_on(void)
{
char ch;
int nread;

if (peek_character != -
1) return(1);

new_settings.c_cc[VMIN]=
0;
tcsetattr(
0, TCSANOW, &new_settings);
nread=read(
0, &ch, 1);
new_settings.c_cc[VMIN]=
1;
tcsetattr(
0, TCSANOW, &new_settings);

if (nread==
1) {
peek_character=ch;
return(
1);
}

return(
0);
}


int readkey_on(void)
{
char ch;

if (peek_character!=-
1) {
ch=peek_character;
peek_character=-
1;
return(ch);
}

read(
0, &ch, 1);
return(ch);
}


TCP/IP Client


/* test_tcp_client */

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <netdb.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <string.h>
#include <errno.h>
#include "kbhit.c"

#define SIZEMSG 1024

extern int errno;
extern
int h_errno;

int main(int argc, char *argv[])
{
char recv_msg[SIZEMSG]; // prijata sprava
char send_msg[SIZEMSG]; // odoslana sprava
struct hostent *server_host; // struct servera
struct sockaddr_in server_sock; // struct serveroveho socketu
int my_sock_fd ; // file deskriptor mojho client socketu
int server_port; // cislo portu
int count_byte; // pocet prijatych a odoslanych byte
int i;
int key=0; // stlacena klavesa (kbhit)

system("clear");
printf(
"TCP/IP socketova komunikacia [CLIENT]\n");
system(
"date");
printf(
"\n");

/* osetrenie poctu argumentov */
if (argc != 3) {
printf(
"Pouzitie: %s adresa_servera port_servera\n", argv[0]);
printf(
"Napriklad: %s localhost 1024\n\n", argv[0]);
return(-
1);
}

/* osetrenie existencie adresy servera (domain_name) a vypis infa o nom */
if (!(server_host=gethostbyname(argv[1]))) {
printf(
"Chyba: Nepodarilo sa najst server %s [%i]\n", argv[1], h_errno);
herror(
"Chyba"); // textovy vypis chyby h_errno
switch (h_errno) {
case HOST_NOT_FOUND:
printf(
" Specifikovaný server je neznamy [HOST_NOT_FOUND]\n");
break;
case NO_ADDRESS:
printf(
" Meno servera je platne, ale nema ziadnu IP adresu [NO_ADDRESS]\n");
break;
case NO_RECOVERY:
printf(
" Neodstranitelna chyba menneho servera [NO_RECOVERY]\n");
break;
case TRY_AGAIN:
printf(
" Docasna chyba menneho servera [TRY_AGAIN]\n");
break;
default:
printf(
" Radsej sa chod prejst po lese\n");
break;
}
printf(
"\n");
return(-
1);
}
printf(
"... server %s najdeny [OK]\n", argv[1]);
printf(
" meno servera: %s\n", server_host->h_name);
i=
0;
printf(
" IP adresa servera:");
while (server_host->h_addr_list[i]) {
/* inet_ntoa() prevod do "bodkoveho" tvaru */
printf(" %s", inet_ntoa(*(struct in_addr *)server_host->h_addr_list[i++]));
}
printf(
"\n\n");

/* vytvorenie mojho lokalneho client socketu (socket) */
if ((my_sock_fd=socket(AF_INET, SOCK_STREAM, IPPROTO_TCP))==-1) {
printf(
"Chyba: nepodarilo sa vytvorit socket\n\n");
return(-
1);
}
printf(
"... socket [%i] vytvoreny [OK]\n\n", my_sock_fd);

/* naplnenie struktury server_sock (sockaddr_in) */
server_port = atoi(argv[2]);
server_sock.sin_family = AF_INET;
// rodina protokolu
server_sock.sin_port = htons(server_port); // cislo portu na serveri
// IP adresa servera ako kopia polozky struktury server_host
memcpy(&(server_sock.sin_addr), server_host->h_addr, server_host->h_length);

/* pripojenie (connect) */
if (connect(my_sock_fd, (struct sockaddr *)&server_sock, sizeof(server_sock))==-1) {
printf(
"Chyba: nepodarilo sa naviazat spojenie so serverom %s [%i]\n", argv[1], errno);
herror(
"Chyba"); // textovy vypis chyby errno
switch (errno) {
case EBADF:
printf(
" Zly deskriptor socketu [EBADF]\n");
break;
case EFAULT:
printf(
" Adresa socketu je mimo adresovy priestor procesu [EFAULT]\n");
break;
case ENOTSOCK:
printf(
" Deskriptor nie je platnym deskriptorom socketu [ENOTSOCK]\n");
break;
case EISCONN:
printf(
" Socket je uz spojeny [EISCONN]\n");
break;
case ECONNREFUSED:
printf(
" Spojeníe bolo odmietnute serverom [ECONNREFUSED]\n");
break;
case ETIMEDOUT:
printf(
" Casovy limit pre spojenie vyprsal [ETIMEDOUT]\n");
break;
case ENETUNREACH:
printf(
" Síet nie je dosiahnutelna [ENETUNREACH]\n");
break;
case EADDRINUSE:
printf(
" Adresa je uz pouzivana [EADDRINUSE]\n");
break;
default:
printf(
" Radsej sa chod prejst po lese\n");
break;
}
printf(
"\n");
return(-
1);
}
printf(
"... spojenie so serverom naviazane [OK]\n\n");

/* ---------------------------------------------------------------- */

printf("[q]-quit (ukoncenie klienta)\n[s]-send (posli spravu serveru)\n\n");
init_kbhit();

/* cyklus odosielania a prijimania sprav */
while (key!='q') {
key=kbhit();
if (key==
's') {
strcpy(send_msg,
"");
/* nacitanie spravy z klavesnice */
printf("<send> ");
close_kbhit();
fgets(send_msg, SIZEMSG, stdin);
// printf("\n");
init_kbhit();
/* odoslanie spravy (send) */
count_byte=0;
if ((count_byte=send(my_sock_fd, send_msg, SIZEMSG,
0))==-1) {
printf(
"Chyba: nepodarilo sa odoslat spravu\n\n");
}
}

/* prijem spravy (recv) */
strcpy(recv_msg, "");
count_byte=
0;
if ((count_byte=recv(my_sock_fd, recv_msg, SIZEMSG, MSG_DONTWAIT))>
0) {
printf(
"\a<recv> %s", recv_msg);
// printf("\n\n");
}

}
// koniec whileho

/* uzatvorenie spojenia (close) */
close_kbhit();
close(my_sock_fd);
printf(
"\n... socket uzatvoreny [OK]\n");
printf(
"... spojenie ukoncene [OK]\n");
printf(
"... cinnost klienta ukoncena [OK]\n\n");

return(
0);
}


TCP/IP Server


/* test_tcp_server */

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <netdb.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <string.h>
#include "kbhit.c"

#define SIZEMSG 1024
#define SIZEFRONT 1

extern int h_errno;

int main(int argc, char *argv[])
{
char recv_msg[SIZEMSG]; // prijata sprava
char send_msg[SIZEMSG]; // odoslana sprava
struct sockaddr_in server_sock; // struct serveroveho socketu
struct sockaddr_in client_sock; // struct klientskeho socketu
socklen_t client_len; // dlzka adresy klienta
int server_port; // cislo portu
int my_sock_fd; // file deskriptor mojho server socketu
int client_sock_fd; // file deskriptor klientskeho socketu
int count_byte; // pocet prijatych a odoslanych byte
int key=0; // stlacena klavesa (kbhit)

unlink(
"server_socket");
system(
"clear");
printf(
"TCP/IP socketova komunikacia [SERVER]\n");
system(
"date");
printf(
"\n");

/* osetrenie poctu argumentov */
if (argc != 2) {
printf(
"Pouzitie: %s port_servera\n", argv[0]);
printf(
"Napriklad: %s 1024\n\n", argv[0]);
return(-
1);
}

/* vytvorenie mojho lokalneho server socketu (socket) */
if ((my_sock_fd=socket(AF_INET, SOCK_STREAM, IPPROTO_TCP))==-1) {
printf(
"Chyba: nepodarilo sa vytvorit socket\n\n");
return(-
1);
}
printf(
"... socket [%i] vytvoreny [OK]\n\n", my_sock_fd);

/* naplnenie struktury server_sock (sockaddr_in) */
server_port = atoi(argv[1]);
server_sock.sin_family = AF_INET;
// rodina protokolu
server_sock.sin_port = htons(server_port); // cislo portu na serveri
server_sock.sin_addr.s_addr = INADDR_ANY; // moze sa pripojit kazdy (INADDR_ANY)

/* pomenovanie socketu (bind) */
if (bind(my_sock_fd, (struct sockaddr *)&server_sock, sizeof(server_sock))==-1) {
printf(
"Chyba: nepodarilo sa pomenovat socket\n\n");
return(-
1);
}
printf(
"... socket [%i] pomenovany [OK]\n\n", my_sock_fd);

/* vytvorenie fronty ziadosti o pripojenie (listen) */
if (listen(my_sock_fd, SIZEFRONT)==-
1) {
printf(
"Chyba: nepodarilo sa vytvorit frontu ziadosti o pripojenie\n\n");
return(-
1);
}
printf(
"... fronta ziadosti o pripojenie vytvorena [OK]\n\n");

/* prijatie klienta (accept) */
client_len=sizeof(client_sock);
if ((client_sock_fd=accept(my_sock_fd, (struct sockaddr *)&client_sock, &client_len))==-
1) {
printf(
"Chyba: nepodarilo sa akceptovat ziadost o pripojenie");
return(-
1);
}
printf(
"... ziadost o pripojenie akceptovana [OK]\n");
printf(
" IP adresa klienta: %s\n", inet_ntoa((struct in_addr)client_sock.sin_addr));
printf(
" spojenie so serverom naviazane [OK]\n\n");

/* ---------------------------------------------------------------- */

printf("[q]-quit (ukoncenie servera)\n[s]-send (posli spravu klientovi)\n\n");
init_kbhit();

/* cyklus odosielania a prijimania sprav */
while (key!='q') {
key=kbhit();
if (key==
's') {
strcpy(send_msg,
"");
/* nacitanie spravy z klavesnice */
printf("<send> ");
close_kbhit();
fgets(send_msg, SIZEMSG, stdin);
// printf("\n");
init_kbhit();
/* odoslanie spravy (send) */
count_byte=0;
if ((count_byte=send(client_sock_fd, send_msg, SIZEMSG,
0))==-1) {
printf(
"Chyba: nepodarilo sa odoslat spravu\n\n");
}
}

/* prijem spravy (recv) */
strcpy(recv_msg, "");
count_byte=
0;
if ((count_byte=recv(client_sock_fd, recv_msg, SIZEMSG, MSG_DONTWAIT))>
0) {
printf(
"\a<recv> %s", recv_msg);
// printf("\n\n);
}

}
// koniec whileho

/* uzatvorenie spojenia (close) */
close_kbhit();
close(my_sock_fd);
close(client_sock_fd);
printf(
"\n... socket uzatvoreny [OK]\n");
printf(
"... spojenie ukoncene [OK]\n");
printf(
"... cinnost servera ukoncena [OK]\n\n");

return(
0);
}


Manuálové stránky

resolve_domain_name

header:
<netdb.h>
<netinet/in.h>
<arpa/inet.h>

struct:
hostent
in_addr

function:
gethostbyname()
inet_ntoa()


tcp_client

header:
<unistd.h>
<netdb.h>
<netinet/in.h>
<sys/types.h>
<sys/socket.h>
<arpa/inet.h>
<errno.h>

struct:
hostent
sockaddr_in
sockaddr

function:
herror()
perror()
socket()
connect()
send()
recv()
close()


tcp_server

header:
<unistd.h>
<sys/types.h>
<sys/socket.h>
<netinet/in.h>
<arpa/inet.h>
<unistd.h>

struct:
sockaddr_in
sockaddr

function:
socket()
bind()
listen()
accept()
inet_ntoa()
recv()
send()
close()


Download

 sockety.tgz


Záver

Toľko úvod do oblasti socketov. Námetom pre ďalšiu činnosť žiakov môže byť obslúženie viacerých klientov (funkcie fork(), select() a pod.) prípadne programovanie viacerých samostatných vlákien a následné porovnanie s predchádzajúcou realizáciou. Tiež je možné pracovať so socketmi na "low-level" úrovni prostredníctvom file deskriptorov (funkcie write(), read() a pod.). Nemenej dôležitou oblasťou je otázka bezpečnosti komunikácie, šifrovania.


Zdroje

  • manuálové stránky (RedHat 7.3)
  • Neil Mathew, Richard Stones - Linux Začínáme programovat
  • seriál článkov o socketoch na http://www.builder.cz
  • Príručka správcu siete pre školy podporované projektom Infovek (LŠOG Zvolen 2002)
Najmä kniha N.Mathew, R.Stones - Linux Začínáme programovat by nemala chýbať na žiadnej základnej škole.
<<- ->>