Категории
Самые читаемые
💎Читать книги // БЕСПЛАТНО // 📱Online » Компьютеры и Интернет » Программирование » Системное программирование в среде Windows - Джонсон Харт

Читаем без скачивания Системное программирование в среде Windows - Джонсон Харт

Читать онлайн Системное программирование в среде Windows - Джонсон Харт

Шрифт:

-
+

Интервал:

-
+

Закладка:

Сделать
1 ... 94 95 96 97 98 99 100 101 102 ... 142
Перейти на страницу:

Имеются также различия в моделях программирования сервера и клиента.

Сравнение серверов именованных каналов и сокетов

Установка соединения с несколькими клиентами при использовании сокетов требует выполнения повторных вызовов функции accept. Каждый из вызовов возвращает очередной подключенный сокет. По сравнению с именованными каналами имеются следующие отличия:

• В случае именованных каналов требуется, чтобы каждый экземпляр именованного канала и дескриптор типа HANDLE создавались с помощью функции CreateNamedPipe, тогда как для создания экземпляров сокетов применяется функция accept.

• Допустимое количество клиентских сокетов ничем не ограничено (функция listen ограничивает лишь количество клиентов, помещаемых в очередь), в то время как количество экземпляров именованных каналов, в зависимости от того, что было указано при первом вызове функции CreateNamedPipe, может быть ограниченным.

• Не существует вспомогательных функций для работы с сокетами, аналогичных функции TransactNamedPipe.

• Именованные каналы не имеют портов с явно заданными номерами и различаются по именам. 

В случае сервера именованных каналов получение пригодного для работы дескриптора типа HANDLE требует вызова двух функций (CreateNamedPipe и ConnectNamedPipe), тогда как сервер сокета требует вызова четырех функций (socket, bind, listen и accept).

Сравнение клиентов именованных каналов и сокетов

В случае именованных каналов необходимо последовательно вызывать функции WaitNamedPipe и CreateFile. Если же используются сокеты, этот порядок вызовов обращается, поскольку можно считать, что функция socket создает сокет, а функция connect — блокирует.

Дополнительное отличие состоит в том, что функция connect является функцией клиента сокета, в то время как функция ConnectNamedPipe используется сервером именованного канала.

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

Часто оказывается удобным отправлять и получать сообщения в виде единых блоков. Как было показано в главе 11, каналы позволяют это сделать. Однако в случае сокетов требуется создание заголовка, содержащего размер сообщения, за которым следует само сообщение. Для приема таких сообщений предназначена функция ReceiveMessage, которая будет использоваться в примерах. То же самое можно сказать и о функции SendMessage, предназначенной для передачи сообщений.

Обратите внимание, что сообщение принимается в виде двух частей: заголовка и содержимого. Ниже мы предполагаем, что пользовательскому типу MESSAGE соответствует 4-байтовый заголовок. Но даже для 4-байтового заголовка требуются повторные вызовы функции recv, чтобы гарантировать его полное считывание, поскольку функция recv не является атомарной.

Примечание, относящееся к Win64

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

DWORD ReceiveMessage (MESSAGE *pMsg, SOCKET sd) {

 /* Сообщение состоит из 4-байтового поля размера сообщения, за которым следует собственно содержимое. */ 

 DWORD Disconnect = 0;

 LONG32 nRemainRecv, nXfer;

 LPBYTE pBuffer;

 /* Считать сообщение. */

 /* Сначала считывается заголовок, а затем содержимое. */

 nRemainRecv = 4; /* Размер поля заголовка. */

 pBuffer = (LPBYTE)pMsg; /* recv может не передать все запрошенные байты. */

 while (nRemainRecv > 0 && !Disconnect) {

  nXfer = recv(sd, pBuffer, nRemainRecv, 0);

  Disconnect = (nXfer == 0);

  nRemainRecv –=nXfer;

  pBuffer += nXfer;

 }

 /* Считать содержимое сообщения. */

 nRemainRecv = pMsg->RqLen;

 while (nRemainRecv > 0 && !Disconnect) {

  nXfer = recv(sd, pBuffer, nRemainRecv, 0);

  Disconnect = (nXfer == 0);

  nRemainRecv –=nXfer;

  pBuffer += nXfer;

 }

 return Disconnect;

}

Пример: клиент на основе сокета

Программа 12.1 представляет собой переработанный вариант клиентской программы clientNP (программа 11.2), которая использовалась в случае именованных каналов. Преобразование программы осуществляется самым непосредственным образом и требует лишь некоторых пояснений.

• Вместо обнаружения сервера с помощью почтовых ящиков пользователь вводит IP-адрес сервера в командной строке. Если IP-адрес не указан, используется заданный по умолчанию адрес 127.0.0.1, соответствующий локальной системе.

• Для отправки и приема сообщений применяются функции, например, ReceiveMessage, которые здесь не представлены.

• Номер порта, SERVER_PORT, определен в заголовочном файле ClntSrvr.h.

Хотя код написан для выполнения под управлением Windows, единственная зависимость от Windows связана с использованием вызовов функций, имеющих префикс WSA.

Программа 12.1. clientSK: клиент на основе сокетов 

/* Глава 12. clientSK.с */

/* Однопоточный клиент командной строки. */

/* ВЕРСИЯ НА ОСНОВЕ WINDOWS SOCKETS. */

/* Считывает последовательность команд для пересылки серверному процессу*/

/* через соединение с сокетом. Дожидается ответа и отображает его. */

#define _NOEXCLUSIONS /* Требуется для включения определений сокета. */

#include "EvryThng.h"

#include "ClntSrvr.h" /* Определяет структуры записей запроса и ответа. */

/* Функции сообщения для обслуживания запросов и ответов. */

/* Кроме того, ReceiveResponseMessage отображает полученные сообщения. */

static DWORD SendRequestMessage(REQUEST *, SOCKET);

static DWORD ReceiveResponseMessage(RESPONSE *, SOCKET);

struct sockaddr_in ClientSAddr; /* Адрес сокета клиента. */

int _tmain(DWORD argc, LPTSTR argv[]) {

 SOCKET ClientSock = INVALID_SOCKET;

 REQUEST Request; /* См. ClntSrvr.h. */

 RESPONSE Response; /* См. ClntSrvr.h. */

 WSADATA WSStartData; /* Структура данных библиотеки сокета. */

 BOOL Quit = FALSE;

 DWORD ConVal, j;

 TCHAR PromptMsg[] = _T("nВведите команду> ");

 TCHAR Req[MAX_RQRS_LEN];

 TCHAR QuitMsg[] = _T("$Quit");

 /* Запрос: завершить работу клиента. */

 TCHAR ShutMsg[] = _T("$ShutDownServer"); /* Остановить все потоки. */

 CHAR DefaultIPAddr[] = "127.0.0.1"; /* Локальная система. */

 /* Инициализировать библиотеку WSA; задана версия 2.0, но будет работать и версия 1.1. */

 WSAStartup(MAKEWORD(2, 0), &WSStartData);

 /* Подключиться к серверу. */

 /* Следовать стандартной процедуре вызова последовательности функций socket/connect клиентом. */

 ClientSock = socket(AF_INET, SOCK_STREAM, 0);

 memset(&ClientSAddr, 0, sizeof(ClientSAddr));

 ClientSAddr.sin_family = AF_INET;

 if (argc >= 2) ClientSAddr.sin_addr.s_addr = inet_addr(argv [1]);

 else ClientSAddr.sin_addr.s_addr = inet_addr(DefaultIPAddr);

 ClientSAddr.sin_port = htons(SERVER_PORT);

 /* Номер порта определен равным 1070. */

 connect(ClientSock, (struct sockaddr *)&ClientSAddr, sizeof(ClientSAddr));

 /* Основной цикл для вывода приглашения на ввод команд, посылки запроса и получения ответа. */

 while (!Quit) {

  _tprintf(_T("%s"), PromptMsg);

  /* Ввод в формате обобщенных строк, но команда серверу должна указываться в формате ASCII. */

  _fgetts(Req, MAX_RQRS_LEN-1, stdin); 

  for (j = 0; j <= _tcslen(Req) Request.Record[j] = Req[j];

  /* Избавиться от символа новой строки в конце строки. */

  Request.Record[strlen(Request.Record) – 1] = '';

  if (strcmp(Request.Record, QuitMsg) == 0 || strcmp(Request.Record, ShutMsg) == 0) Quit = TRUE;

  SendRequestMessage(&Request, ClientSock);

  ReceiveResponseMessage(&Response, ClientSock);

 }

 shutdown(ClientSock, 2); /* Запретить посылку и прием сообщений. */

 closesocket(ClientSock);

 WSACleanup();

 _tprintf(_T("n****Выход из клиентской программыn"));

 return 0;

Пример: усовершенствованный сервер на основе сокетов

Программа serverSK (программа 12.2) аналогична программе serverNP (программа 11.3), являясь ее видоизмененным и усовершенствованным вариантом.

• В усовершенствованном варианте программы серверные потоки создаются по требованию (on demand), а не в виде пула потоков фиксированного размера. Каждый раз, когда сервер принимает запрос клиента на соединение, создается серверный рабочий поток, и когда клиент прекращает работу, выполнение потока завершается.

• Сервер создает отдельный поток приема (accept thread), что позволяет основному потоку опрашивать глобальный флаг завершения работы, пока вызов accept остается блокированным. Хотя сокеты и могут определяться как неблокирующиеся, потоки обеспечивают удобное универсальное решение. Следует отметить, что значительная часть расширенных функциональных возможностей Winsock призвана поддерживать асинхронные операции, тогда как потоки Windows дают возможность воспользоваться более простой и близкой к стандартам функциональностью синхронного режима работы сокетов.

1 ... 94 95 96 97 98 99 100 101 102 ... 142
Перейти на страницу:
На этой странице вы можете бесплатно скачать Системное программирование в среде Windows - Джонсон Харт торрент бесплатно.
Комментарии