V treťom bloku cvičení budeme komunikovať naprieč internetom so serverom. Vašou úlohou bude naprogramovať “klientský program” schopný posielať a prijímať textové správy. Jedná sa o jeden rozsiahlejší projekt, ktorý budete riešiť počas celého tretieho bloku. V prvej fáze budete mať za úlohu pripojiť sa na server a identifikovať sa svojím osobným číslom. Ďalej musíte vedieť prijať správu zo servera a zobraziť ju na obrazovke. Ak sa Vám to podarí, tak Vás Morfeus osloví Vaším vlastným menom a zadá Vám úlohy, ktoré musíte splniť. Ak sa Vám podarí všetky splniť, plný počet 12 bodov za tretí blok je Váš :).
NUTNÝ MALÝ TEORETICKÝ ÚVOD
Komunikácia počítačov cez internet je veľmi zjednodušene povedané len posielanie jednotiek a núl z Vášho počítača na počítač na druhej strane a opačne. Je to samozrejme oveľa sofistikovanejšie, no pre potreby tohto projektu Vám stačí vedieť, že budete komunikovať prostredníctvom protokolu TCP/IP, ktorý zabezpečuje, aby sa Vaše jednotky a nuly dostali na počítač na druhej strane v poriadku.
ZÁKLADNÝ POHĽAD NA ZADANIE
- Pripojiť sa na server s IP adresou 147.175.115.34 na port 777 pomocou protokolu TCP.
- Poslať úvodnú správu (zobraziť ju v konzole).
- Prijať odpoveď (zobraziť ju v konzole).
- Postupovať podľa ďalších príkazov prijatých v komunikácii so serverom. Komunikácia funguje tak, že vždy po tom, čo Vám server niečo pošle očakáva nejakú odpoveď od Vás.
SOCKETY
#include <stdio.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#pragma comment(lib, “Ws2_32.lib”)
1. Úvodné nastavenia
Najprv treba nastaviť základné parametre štruktúr socketu, IP adresu servera a port, na ktorom chcete komunikovať. Prejdite si každý riadok kódu, nepracujte len systémom “copy/paste”.
//uvodne nastavovacky
WSADATA wsaData; //struktura WSADATA pre pracu s Winsock
int iResult;
// Initialize Winsock
iResult = WSAStartup(MAKEWORD(2, 2), &wsaData); //zakladna inicializacia
if (iResult != 0) //kontrola, ci nestala chyba
{
printf(“WSAStartup failed: %d\n”, iResult);
return 1;
}
struct addrinfo *result = NULL, *ptr = NULL; //struktura pre pracu s adresami
struct addrinfo hints;
ZeroMemory(&hints, sizeof(hints));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP; //pracujeme s protokolom TCP/IP
// Resolve the server address and port
iResult = getaddrinfo(“tu ma byt IP adresa servera”, “tu ma byt port”, &hints, &result);
if (iResult != 0) //kontrola, ci nenastala chyba
{
printf(“getaddrinfo failed: %d\n”, iResult);
WSACleanup();
return 1;
}
else
printf(“getaddrinfo didn’t fail…\n”);
2. Pripojenie sa
V tejto časti kódu sa snažíme pripojiť na server spôsobom, ktorý sme definovali v časti predošlej.
//vytvorenie socketu a pripojenie sa
SOCKET ConnectSocket = INVALID_SOCKET;
// Attempt to connect to the first address returned by
// the call to getaddrinfo
ptr = result;
// Create a SOCKET for connecting to server => pokus o vytvorenie socketu
ConnectSocket = socket(ptr->ai_family, ptr->ai_socktype,ptr->ai_protocol);
if (ConnectSocket == INVALID_SOCKET) //kontrola, ci nenastala chyba
{
printf(“Error at socket(): %ld\n”, WSAGetLastError());
freeaddrinfo(result);
WSACleanup();
return 1;
}
else
printf(“Error at socket DIDN’T occur…\n”);
// Connect to server. => pokus o pripojenie sa na server
iResult = connect(ConnectSocket, ptr->ai_addr, (int)ptr->ai_addrlen);
if (iResult == SOCKET_ERROR) //kontrola, ci nenastala chyba
printf(“Not connected to server…\n”);
else
printf(“Connected to server!\n”);
if (iResult == SOCKET_ERROR) //osetrenie chyboveho stavu
{
closesocket(ConnectSocket);
ConnectSocket = INVALID_SOCKET;
WSACleanup();
return 1;
}
Sleep(250);
3. Posielanie dát
Pri posielaní dát v zásade pracujeme z reťazcami ako aj v predošlých blokoch. Posielame znaky, čo sú v konečnom dôsledku jednotky a nuly, ktoré server prijme. Musíte si zvoliť vhodnú dĺžku reťazca, za každým ho naplniť znakmi, ktoré chcete poslať a následne “posunút” reťazec vytvorenému socketu, ktorý sa pokúsi dáta poslať.
//posielanie
char sendbuf[/*treba zavolit vhodnu velkost, napr. 4096*/]; //buffer (v zasade retazec), kam sa budu ukladat data, ktore chcete posielat
iResult = send(ConnectSocket, sendbuf, (int)strlen(sendbuf), 0);
if (iResult == SOCKET_ERROR)
{
printf(“send failed: %d\n”, WSAGetLastError());
closesocket(ConnectSocket);
WSACleanup();
return 1;
}
printf(“Bytes Sent: %ld\n”, iResult); //vypisanie poctu odoslanych dat
4. Prijímanie dát
Aby Vám server vedel odpovedať, musíte si nachystať buffer, do ktorého si budete ukladať dáta, ktoré Vám pošle. V našom prípade sa bude takmer vždy jednať o znaky, takže si ich môžete ukladať do reťazca.
//prijimanie
#define DEFAULT_BUFLEN 4096 //makro, kde definujeme velkost prijimacieho buffera
int recvbuflen = DEFAULT_BUFLEN;
char recvbuf[DEFAULT_BUFLEN];
iResult = recv(ConnectSocket, recvbuf, recvbuflen, 0); //funkcia na príjimanie
if ( iResult > 0 )
printf(“Bytes received: %d\n”, iResult); //prisli validne data, vypis poctu
else if ( iResult == 0 )
printf(“Connection closed\n”); //v tomto pripade server ukoncil komunikaciu
else
printf(“recv failed with error: %d\n”, WSAGetLastError()); //ina chyba
5. Zavretie socketu
Po ukončení komunikácie je potrebné socket zavrieť.
//zavretie socketu
closesocket(ConnectSocket);
WSACleanup();
TIPY, TRIKY A PODMIENKY
1. Načítanie vstupu s konzoly aj s medzerami
Funkcia scanf() Vám štandardne ukončí načítanie reťazca zo vstupu po výskyte prvej medzery. Ak chcete uložiť vstup vrátane medzier je lepšie použiť funkciu fgets():
char name[1024];
int size = 1024;
fgets(name, size, stdin);
printf(“%s\n”, name);
2. Práca s prijatými dátami
V jednom špecifickom prípade sa však nebudete môcť spoľahnút ani na znak ‘\n’. Vtedy budete musieť ukončovaciu nulu umiestniť manuálne na základe informácie, ktorá Vám bude včas doručená.
3. Ak vám vo Windows 11 nefunguje umiestňovanie znakov na požadované súradnice
Môžete posúvať písmená doprava o požadovaný počet medzier napríklad takto: printf(“%60cMorpheus:”, ‘ ‘);
BODOVÉ OHODNOTENIE
Na získanie plného počtu bodov sa musíte dostať až po poslednú správu zo servera. Úlohy musíte mať riešené algoritmicky – predovšetkým úlohu s rozdeľovaním textu na ľavú a pravú stranu, a úlohu s prvočíslami (tie sú najvyššie bodovo hodnotené). Úloha s prvočíslami musí fungovať pre akúkoľvek dlhú správu.