Мета роботи: вивчити роботу з датаграмними сокетами в режимі опиту. Створити сервер котрий буде відповідати на запити клієнтів.

5.1 Теоретичні відомості

Датаграмні сокети використовуються в програмах не так часто, оскільки надійність дуже низька. Корисні вони тоді, коли потрібно постійно передавати звук і відео по мережі. Оскільки для датаграмних сокетів не потрібно встановлювати з’єднання, використовувати їх значно простіше.

Реалізація сервера:

1) підготувати бібліотеку до використання;

2) створити об’єкт типу Socket;

3) зв’язати цей об’єкт з локальною адресою/портом;

4) прийняти/передати дані;

5) закрити сокети, звільнити ресурси.

Реалізація клієнта:

1) підготувати бібліотеку до використання;

2) створити об’єкт типу Socket;

3) прийняти/передати дані;

4) закрити сокети, звільнити ресурси.

Створивши сокет за допомогою socket та bind, ви вже можете використовувати його для відправки і прийому повідомлень за допомогою функцій sendto та recvfrom.

Для відправлення повідомлення використовують функцію int sendto(SOCKET s, const char FAR * buf, int len,int flags, const struct sockaddr FAR * to, int tolen).

Вона майже така як і send, але має 2 додаткових параметри:

- to – вказуємо структуру sockaddr, в котрій є адреса призначення;

- tolen – розмір структури.

Для прийому даних використовують функцію int recvfrom(SOCKET s, char FAR* buf, int len, int flags, struct sockaddr FAR* from, int FAR* fromlen ).

Відповідно і тут є 2 додаткових параметри, один із них структура sockaddr, в котру ми запишемо адресу/порт відправника (на цю ж адресу і будемо відповідати в зворотному направці) і саме розмір цієї структури.

Доречі, можна використовувати і функції send та recv, зробивши connect, але майте на увазі, що ніякого з’єднання не відбувається, ми просто запам’ятовуємо адресу.

 

5.1.1 Приклад роботи WinSock

Реалізація сервера:

#include "stdafx.h"

#include <winsock2.h>

#pragma comment(lib, "wsock32.lib")

 

#define SERVER_SOCKET_ERROR 1

#define SOCKET_OK 0

 

int _tmain(int argc, _TCHAR* argv[])

{

int result;

WORD sockVersion;

WSADATA wsaData;

sockVersion = MAKEWORD(2,2);

WSAStartup(sockVersion, &wsaData);

sockaddr_in sin;

sin.sin_family = AF_INET;

sin.sin_port = htons(8888);

sin.sin_addr.s_addr = INADDR_ANY;

 

SOCKET s = socket(AF_INET, SOCK_DGRAM, 0);

if(s == INVALID_SOCKET)

{

printf ("%s", "ERROR (don't create server)\n");

WSACleanup();

return SERVER_SOCKET_ERROR;

}

else

{

printf ("%s", " >>> Create socket \n");

}

 

result = bind(s, (LPSOCKADDR)&sin, sizeof(sin));

if(result == SOCKET_ERROR)

{

printf ("%s", "ERROR (don't associates a local address with a socket)");

WSACleanup();

return SERVER_SOCKET_ERROR;

}

else

{

printf ("%s", " >>> Associates a local addres with a socket\n");

}

 

while (1)

{

char recv_buf[1024];

char send_buf[1024] = "ANSWER\n";

 

sockaddr_in client_addr;

int client_addr_size = sizeof(client_addr);

 

int result = recvfrom ( s, recv_buf, 1024, 0, (LPSOCKADDR)&client_addr, &client_addr_size);

if (result == SOCKET_ERROR)

printf(" ERROR recvfrom() error: %d\n",WSAGetLastError());

 

HOSTENT *hst;

hst=gethostbyaddr((char *) &client_addr.sin_addr,4,AF_INET);

printf( inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));

recv_buf[result]=0;

printf(" = Message from client: %s\n",&recv_buf[0]);

sendto (s,&recv_buf[0],40,0, (sockaddr *)&client_addr, sizeof(client_addr));

}

closesocket(s);

printf ("%s", " >>> Close main socket \n");

WSACleanup();

return SOCKET_OK;

}

 

Реалізація клієнта:

#include "stdafx.h"

#include <winsock.h>

#pragma comment(lib, "wsock32.lib")

 

#define CS_ERROR 1

#define CS_OK 0

 

int _tmain(int argc, _TCHAR* argv[])

{

WORD version;

WSADATA wsaData;

int result;

version = MAKEWORD(2,2);

WSAStartup(version,(LPWSADATA)&wsaData);

 

LPHOSTENT hostEntry;

hostEntry = gethostbyname("127.0.0.1");

if(!hostEntry)

{

printf ("%s", " >>> ERROR (hostEntry NULL)\n");

WSACleanup();

return CS_ERROR;

}

 

SOCKET theSocket = socket(AF_INET, SOCK_DGRAM, 0);

if(theSocket == SOCKET_ERROR)

{

printf ("%s", " ERROR (don't create socket)\n");

return CS_ERROR;

}

else

{

printf ("%s", " >>> Create socket \n");

}

 

SOCKADDR_IN serverInfo;

serverInfo.sin_family = AF_INET;

serverInfo.sin_port = htons(8888);

serverInfo.sin_addr.s_addr=inet_addr("127.0.0.1");

 

while(1)

{

char send_buf[1000] = "";

char recv_buf[400] = "";

printf("\nWrite message: ");

scanf ("%s", send_buf);

if (!strcmp(&send_buf[0],"quit")) break;

 

sendto(theSocket,&send_buf[0], strlen(&send_buf[0]),0,

(sockaddr *) &serverInfo,sizeof(serverInfo));

 

sockaddr_in server_addr;

int server_addr_size=sizeof(server_addr);

int n=recvfrom (theSocket,&recv_buf[0], 999, 0 ,

(sockaddr *) &server_addr, &server_addr_size);

if (n==SOCKET_ERROR)

{

printf("recvfrom() error: %d\n",WSAGetLastError());

closesocket(theSocket);

WSACleanup();

}

recv_buf[n]=0;

printf(" Answer from Server: %s",&recv_buf[0]);

}

 

closesocket(theSocket);

printf ("%s", " >>> Close socket\n");

WSACleanup();

char a[100];

scanf ("%s", a);

return CS_OK;

}

5.1.2 Приклад роботи (для UNIX подібних систем)

Программа sender:

#include <sys/types.h>

#include <sys/socket.h>

#include <netinet/in.h>

 

char msg1[] = "Hello there!\n";

char msg2[] = "Bye bye!\n";

 

int main()

{

int sock;

struct sockaddr_in addr;

 

sock = socket(AF_INET, SOCK_DGRAM, 0);

if(sock < 0)

{

perror("socket");

exit(1);

}

addr.sin_family = AF_INET;

addr.sin_port = htons(3425);

addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);

sendto(sock, msg1, sizeof(msg1), 0,

(struct sockaddr *)&addr, sizeof(addr));

 

connect(sock, (struct sockaddr *)&addr, sizeof(addr));

send(sock, msg2, sizeof(msg2), 0);

 

close(sock);

 

return 0;

}

 

Программа receiver:

#include <sys/types.h>

#include <sys/socket.h>

#include <netinet/in.h>

#include <stdio.h>

 

int main()

{

int sock;

struct sockaddr_in addr;

char buf[1024];

int bytes_read;

 

sock = socket(AF_INET, SOCK_DGRAM, 0);

if(sock < 0)

{

perror("socket");

exit(1);

}

 

addr.sin_family = AF_INET;

addr.sin_port = htons(3425);

addr.sin_addr.s_addr = htonl(INADDR_ANY);

if(bind(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0)

{

perror("bind");

exit(2);

}

 

while(1)

{

bytes_read = recvfrom(sock, buf, 1024, 0, NULL, NULL);

buf[bytes_read] = '\0';

printf(buf);

}

 

return 0;

}

5.2 Завдання

Написати клієнт-серверний застосунок, котрий буде працювати з датаграмними сокетами.

Для студентів котрі хочуть отримати відмінну оцінку: клієнт повинен надіслати число від 1 до 10, а у відповідь від сервера отримати стільки повідомлень, скільки було вказано клієнтом;

5.3 Питання

 

1. Опишіть принцип роботи датаграмних сокетів?

2. В чому полягає різниця між датаграмними сокетами і потоковими?

3. Назвіть функції для отримання і відправлення повідомлень?

4. Які поля містить структура sockaddr?