Основы сетевой передачи данных. Работа с сокетами. Обработка запросов от нескольких клиентов, страница 2

               int h_length – длина адреса;

               char* h_addr – список адресов в байтовом формате.

Сохранение полученного списка байтовых адресов осуществляется так:

if(hstnt = gethostbyname(SERVERADDR))

                              {

                                            dest_addr.sin_addr.s_addr = (u_long)hstnt->h_addr_list[0];

                              }

Или сделать то же самое, указав начало области памяти, содеражащей адрес:

if(hstnt = gethostbyname(SERVERADDR))

                              {

                                            ((u_long*)&dest_addr.sin_addr.s_addr)[0] = (u_long**)hstnt->h_addr_list[0][0];

                              }

Если gethostbyname не может сопоставить SERVERADDR никакого адреса, то она возвращает ноль.

Клиент и сервер

Работа сетевого приложения делится на клиентскую и серверную части. Приложение-клиент отсылает запросы серверу, сервер их обрабатывает и возвращает клиенту результат.

Подключение клиента к серверу осуществляется посредством функции connect:

connect(s, (sockaddr*)&adress, nsize);

Здесь s – дескриптор сокета, который клиент использует для подключения;

 adress – структура типа sockaddr_in, в которой содержится сетевой адрес сервера;

nsize = sizeof(adress) – размер структуры адреса.

В случае ошибки функция возвращает ненулевое значение, код ошибки можено получить через WSAGetLastError().

Теперь посмотрим, какие действия следует совершить серверу, чтобы клиент мог к нему подключиться.

Для начала сервер должен закрепить за собой некоторый порт в сетевом интерфейсе. Во-первых, иначе клиент не сможет знать, по какому порту к нему подключаться, во-вторых, приложение должно сообщить системе (в том числе файрволу) о том, что занимает этот порт и его следует открыть.

bind(s, (sockaddr*)&adress, nsize);

Параметры здесь те же, что и у функции connect, только в структуре адреса содержится адрес самого сервера. В случае ошибки возвращается ненулевое значение.

Функция выполняет привязку сокета s к сетевому адресу, содержащемуся в adress.

Запуск сервера осуществляется командой listen:

listen(s, stack_size);

Здесь s – дескриптор сокета, на котором запускается сервер,

stack_size –максимальный размер очереди клиентов. Когда к серверу пытается присоединиться новый клиент, а сервер в это время занят, клиент вносится в очередь, размер который ограничени stack_size. Если очередь уже заполнена, клиенту отказывается в подключении.

sender_sock = accept(s, (sockaddr*)&sender, &nsize);

Сревер принимает подключение клиента. Предварительно нужно инициализировать структуру адреса sender, в которую функция accept внесет адрес клиента, а так же переменную nsize, соответствующую размеру структуры sender. Возвращает accept дескриптор сокета, с которого клиент производит подключение к серверу.

Прием и передача данных.

Функции, необходимые как клиенту (он отправляет запросы и получает результаты), так и серверу (для получения запросов и отправки ответов).

send(s, &buff[0], sizeof(buff), 0);

Отравка данных, хранящихся в переменной buff через сокет s. Предполагается, что предварительно через этот сокет уже установлено соединение с кем-либо. Последний параметр определяет организацию памяти на отправляющей машине для адекватного представления данных, ноль предполагает значение «по умолчанию».

В случае успешной отправки возвращает объем переданных данных (в байтах), иначе -1.

recv(sender_sock, &buff[0], sizeof(buff), 0);

Запись данных, приходящих от сокета sender_sock, в переменную buff наперед заданного размера. Последний параметр опрделяет организацию памяти на принмиающей машине для адекватного отображения данных, ноль предполагает значение «по умолчанию».

В случае успеха возвращает объем принятых данных (в байтах), иначе -1.

Функции протокола UDP.

Напоминаю, что программа настраивается на работу с UDP через второй параметр функции задания сокета. То есть выглядит это так:

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

Следующее отличие в том, что, в отличие от потоковых сокетов, в датаграммных можно использовать адрес INADDR_BROADCAST – адрес массовой рассылки.

При работе с UDP функции установки соединения и обмена данными объединяются.

sendto(s, &buff[0], strlen(buff)-1, 0, (sockaddr*)&dest_addr, nsize);

recvfrom(s, &buff[0], 1024, 0, (sockaddr*)&serv_addr, &ssize);

Первые четыре параметра обеих функци  аналогичны соответствующим параметрам функций send и recv. Следующие параметры отвечают за установление свойств соединения. Последний параметр – размер структуры соответствующего адреса (так как при приеме сообщения она заранее неизвестна, используется ссылка на целочисленную переменную).