Мета роботи: вивчити роботу з датаграмними сокетами в режимі опиту. Створити сервер котрий буде відповідати на запити клієнтів.
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?