Практическое занятие №4. Сетевое взаимодействие в Windows — КиберПедия 

История развития хранилищ для нефти: Первые склады нефти появились в XVII веке. Они представляли собой землянные ямы-амбара глубиной 4…5 м...

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

Практическое занятие №4. Сетевое взаимодействие в Windows

2020-11-19 180
Практическое занятие №4. Сетевое взаимодействие в Windows 0.00 из 5.00 0 оценок
Заказать работу

Цель работы

Изучить механизм сокетов. Научиться разграничивать функциональность ПО между клиентской и серверной частью.

Порядок выполнения практических заданий

1. Рассмотреть представленные примеры, и разработать приложения на их основе.

2. Разработать алгоритм решения третьего задания, с учетом разделения функциональности между клиентом и сервером. Определить формат обмена информацией между клиентом и сервером.

3. Реализовать алгоритм с применением функций WinAPI и протестировать его на нескольких примерах.

Литературные источники

1. Э.Таненбаум. Распределённые системы. Принципы и парадигмы / Э.Таненбаум, Танненбаум, М. ванн Стесн. — СПб.:Питер, 2003. — 877с.

2. Эндрюс Г.Р. Основы многопоточного, параллельного и распределённого программирования/ Эндрюс Г.Р. — М.: «Вильямс», 2003. — 512с.

3. Уолтон Ш. “Создание сетевых приложений в среде Linux” /Уолтон Ш. 2001

Теоретическая часть

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

Отметим, что реализация сокетов в Unix и Windows значительно отличается, что создаёт очевидные проблемы.

Основное подспорье в изучении сокетов - Windows Sockets 2 SDK. SDK - это документация, набор заголовочных файлов и инструментарий разработчика. Большинство книг, имеющиеся на рынке, явно уступают Microsoft в полноте и продуманности описания. Единственный недостаток SDK - он полностью на английском (для некоторых студентов это очень существенно).

Обзор сокетов

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

Сокеты позволяют работать со множеством протоколов и являются удобным средством межпроцессорного взаимодействия, но в данной практической работе речь будет идти только о сокетах семейства протоколов TCP/IP, использующихся для обмена данными между узлами сети Интернет. Все остальные протоколы, такие как IPX/SPX, NetBIOS могут быть изучены студентами самостоятельно.

Независимо от вида, сокеты делятся на два типа - потоковые и дейтаграммные. Потоковые сокеты работают с установкой соединения, обеспечивая надежную идентификацию обоих сторон и гарантируют целостность и успешность доставки данных. Дейтаграмные сокеты работают без установки соединения и не обеспечивают ни идентификации отправителя, ни контроля успешности доставки данных, зато они заметно быстрее потоковых.

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

Замечание: дейтаграммные сокеты опираются на протокол UDP, а потоковые - на TCP.

Первый шаг

Для работы с библиотекой Winsock 2.х в исходный тест программы необходимо включить директиву "# include < winsock 2. h >", а в командной строке линкера указать "ws2_32.lib". В Microsoft Visual Studio для этого достаточно нажать <Alt-F7>, перейти к закладке "Link" и к списку библиотек, перечисленных в строке "Object/Library modules", добавить "ws2_32.lib", отделив ее от остальных символом пробела.

Перед началом использования функций библиотеки Winsock, ее необходимо подготовить к работе вызовом функции " int WSAStartup (WORD wVersionRequested, LPWSADATA lpWSAData)", передав в старшем байта слова wVersionRequested номер требуемой версии, а в младшем - номер подверсии.

Аргумент lpWSAData должен указывать на структуру WSADATA, в которую при успешной инициализации будет занесена информация о производителе библиотеки. Никакого особенного интереса она не представляет и прикладное приложение может ее игнорировать. Если инициализация проваливается, функция возвращает ненулевое значение.

Программирование сокета начинается с создания объекта «сокет». Это осуществляется функцией " SOCKET socket (int af, int type, int protocol)" Первый слева аргумент указывает на семейство используемых протоколов. Для Интернет-приложений он должен иметь значение AF_INET.

Следующий аргумент задает тип создаваемого сокета - потоковый (SOCK _ STREAM) или дейтаграммный (SOCK _ DGRAM) (еще существуют и сырые сокеты, но они не поддерживаются Windows).

Последний аргумент уточняет какой транспортный протокол следует использовать. Нулевое значение соответствует выбору по умолчанию: TCP - для потоковых сокетов и UDP для дейтаграммных. В большинстве случаев нет никакого смысла задавать протокол вручную и обычно полагаются на автоматический выбор по умолчанию.

Если функция завершилась успешно, она возвращает дескриптор сокета, в противном случае - INVALID_SOCKET.

Примечание: дальнейшие шаги зависят от того, является ли приложение сервером или клиентом. Ниже эти два случая будут описаны раздельно.

Клиент: шаг второй

Для установки соединения с удаленным узлом потоковый сокет должен вызвать функцию "int connect (SOCKET s, const struct sockaddr FAR* name, int namelen)". Датаграмные сокеты работают без установки соединения, поэтому обычно не обращаются к функции connect.

Примечание: за словом "обычно" стоит один хитрый примем программирования - вызов connect позволяет дейтаграмному сокету обмениваться данными с узлом не только функциями sendto, recvfrom, но и более удобными и компактными send и recv. Эта тонкость описана в Winsocket SDK и широко используется как самой Microsoft, так и сторонними разработчиками. Поэтому ее использование вполне безопасно.

Первый слева аргумент - дескриптор сокета, возращенный функцией socket; второй - указатель на структуру " sockaddr ", содержащую в себе адрес и порт удаленного узла, с которым устанавливается соединение. Структура sockaddr используется множеством функций, поэтому ее описание вынесено в отдельный раздел "Адрес" теоретической части данной практической работы. Последний аргумент сообщает функции размер структуры sockaddr.

После вызова connect, система предпринимает попытку установить соединение с указанным узлом. Если по каким-то причинам это сделать не удастся (адрес задан неправильно, узел не существует или "висит", компьютер находится не в сети), функция возвратит ненулевое значение.

Сервер: шаг третий

Прежде чем сервер сможет использовать сокет, он должен связать его с локальным адресом. Локальный, как впрочем, и любой другой адрес Интернета, состоит из IP-адреса узла и номера порта. Если сервер имеет несколько IP-адресов, то сокет может быть связан как со всеми ними сразу (для этого вместо IP-адреса следует указать константу INADDR_ANY, равную нулю), так и с каким-то конкретным одним.

Связывание осуществляется вызовом функции "int bind (SOCKET s, const struct sockaddr FAR* name, int namelen)". Первым слева аргументом передается дескриптор сокета, возращенный функций socket, за ним следуют указатель на структуру sockaddr и ее длина (см. раздел "Адрес раз, адрес два...").

Строго говоря, клиент также должен связывать сокет с локальным адресом перед его использованием, однако за него это делает функция connect, ассоциируя сокет с одним из портов, наугад выбранных из диапазона 1024-5000. Сервер же должен "садиться" на заранее определенный порт - например, 21 для FTP, 23 для telnet, 25 для SMTP, 80 для Web, 110 для POP3 и т.д. Поэтому ему приходится осуществлять связывание "вручную".

При успешном выполнении функция возвращает нулевое значение и ненулевое в противном случае.

Сервер: шаг четвертый

Выполнив связывание, потоковый сервер переходит в режим ожидания подключений, вызывая функцию " int listen (SOCKET s, int backlog)", где s - дескриптор сокета, а backlog - максимально допустимый размер очереди сообщений.

Размер очереди ограничивает количество одновременно обрабатываемых соединений, поэтому к его выбору следует подходить "с умом". Если очередь полностью заполнена, очередной клиент при попытке установить соединение получит отказ (TCP пакет с установленным флагом RST). В то же время максимально разумное количество подключений определяется производительностью сервера, объемом оперативной памяти и т.д.

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

Сервер: шаг пятый

Извлечение запросов на соединение из очереди осуществляется функцией " SOCKET accept (SOCKET s, struct sockaddr FAR * addr, int FAR * addrlen)", которая автоматически создает новый сокет, выполняет связывание и возвращает его дескриптор, а в структуру sockaddr заносит сведения о подключившемся клиенте (IP-адрес и порт). Если в момент вызова accept очередь пуста, функция не возвращает управление до тех пор, пока с сервером не будет установлено хотя бы одно соединение. В случае возникновения ошибки функция возвращает отрицательное значение.

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

Все вместе

После того как соединение установлено, потоковые сокеты могут обмениваться с удаленным узлом данными, вызывая функции " int send (SOCKET s, const char FAR * buf, int len, int flags)" и " int recv (SOCKET s, char FAR * buf, int len, int flags)" для посылки и приема данных соответственно.

Функция send возвращает управление сразу же после ее выполнения, независимо от того, получила ли принимающая сторона наши данные или нет. При успешном завершении функция возвращает количество передаваемых (не переданных!) данных - т.е. успешное завершение еще не свидетельствует от успешной доставке! В общем-то, протокол TCP (на который опираются потоковые сокеты) гарантирует успешную доставку данных получателю, но лишь при условии, что соединение не будет преждевременно разорвано. Если связь прервется до окончания пересылки, данные останутся непереданными, но вызывающий код не получит об этом никакого уведомления! А ошибка возвращается лишь в том случае, если соединение разорвано до вызова функции send!

Функция же recv возвращает управление только после того, как получит хотя бы один байт. Точнее говоря, она ожидает прихода целой дейтаграммы. Дейтаграмма - это совокупность одного или нескольких IP пакетов, посланных вызовом send. Упрощенно говоря, каждый вызов recv за один раз получает столько байтов, сколько их было послано функцией send. При этом подразумевается, что функции recv предоставлен буфер достаточных размеров, в противном случае ее придется вызвать несколько раз. Однако, при всех последующих обращениях данные будут браться из локального буфера, а не приниматься из сети, т.к. TCP-провайдер не может получить "кусочек" дейтаграммы, а только ее всю целиком.

Работой обоих функций можно управлять с помощью флагов, передаваемых в одной переменной типа int третьим слева аргументом. Эта переменная может принимать одно из двух значений: MSG _ PEEK и MSG _ OOB.

Флаг MSG_PEEK заставляет функцию recv просматривать данные вместо их чтения. Просмотр в отличие от чтения не уничтожает просматриваемые данные. Некоторые источники утверждают, что при взведенном флаге MSG_PEEK функция recv не задерживает управления, если в локальном буфере нет данных, доступных для немедленного получения. Это неверно! Аналогично, иногда приходится встречать откровенно ложное утверждение о том, что якобы функция send со взведенным флагом MSG_PEEK возвращает количество уже переданных байт (вызов send не блокирует управления). На самом деле функция send игнорирует этот флаг!

Флаг MSG_OOB предназначен для передачи и приема срочных (Out Of Band) данных. Срочные данные не имеют преимущества перед другими при пересылке по сети, а всего лишь позволяют оторвать клиента от нормальной обработки потока обычных данных и сообщить ему "срочную" информацию. Если данные передавались функцией send с установленным флагом MSG_OOB, для их чтения флаг MSG_OOB функции recv также должен быть установлен.

Замечание: настоятельно рекомендуется воздержаться от использования срочных данных в своих приложениях. Во-первых, они совершенно необязательны - гораздо проще, надежнее и элегантнее вместо них создать отдельное TCP -соединение. Во-вторых, по поводу их реализации нет единого мнения и интерпретации различных производителей очень сильно отличаются друг от друга. Так, разработчики до сих пор не пришли к окончательному соглашению по поводу того, куда должен указывать указатель срочности: на последний байт срочных данных или на байт, следующий за последним байтом срочных данных. В результате отправитель никогда не имеет уверенности, что получатель сможет правильно интерпретировать его запрос.

Еще существует флаг MSG_DONTROUTE, предписывающий передавать данные без маршрутизации, но он не поддерживаться Winsock и поэтому здесь не рассматривается.

Дейтаграммный сокет также может пользоваться функциями send и recv, если предварительно вызовет connect (см. "Клиент: шаг третий"), но у него есть и свои, "персональные", функции: "int sendto (SOCKET s, const char FAR * buf, int len, int flags, const struct sockaddr FAR * to, int tolen)" и "int recvfrom (SOCKET s, char FAR* buf, int len, int flags, struct sockaddr FAR* from, int FAR* fromlen)".

Они очень похожи на send и recv - разница лишь в том, что sendto и recvfrom требуют явного указания адреса узла, принимаемого или передаваемого данные. Вызов recvfrom не требует предварительного задания адреса передающего узла - функция принимает все пакеты, приходящие на заданный UDP-порт со всех IP-адресов и портов. Напротив, отвечать отправителю следует на тот же самый порт откуда пришло сообщение. Поскольку функция recvfrom заносит IP-адрес и номер порта клиента после получения от него сообщения, программисту фактически ничего не нужно делать - только передать sendto тот же самый указатель на структуру sockaddr, который был ранее передан функции recvfrem, получившей сообщение от клиента.

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

Во всем остальном обе пары функций полностью идентичны и работают с теми самыми флагами - MSG_PEEK и MSG_OOB.

Все четыре функции при возникновении ошибки возвращают значение SOCKET_ERROR (== -1).

Примечание: в UNIX с сокетами можно обращаться точно также, как и с обычными файлами, в частности писать и читать в них функциями write и read. ОС Windows 3.1 не поддерживала такой возможности, поэтому при переносе приложений их UNIX в Windows все вызовы write и read должны были быть заменены на send и recv соответственно. В Windows 95 с установленным Windows 2. x это упущение исправлено, теперь дескрипторы сокетов можно передавать функциям ReadFil, WriteFile, DuplicateHandle и др.

Шаг последний

Для закрытия соединения и уничтожения сокета предназначена функция " int closesocket (SOCKET s)", которая в случае удачного завершения операции возвращает нулевое значение.

Перед выходом из программы необходимо вызвать функцию " int WSACleanup (void)" для деинициализации библиотеки WINSOCK и освобождения используемых этим приложением ресурсов.

Внимание: завершение процесса функцией ExitProcess автоматически не освобождает ресурсы сокетов!

Примечание: более сложные приемы закрытия соединения - протокол TCP позволяет выборочно закрывать соединение любой из сторон, оставляя другую сторону активной. Например, клиент может сообщить серверу, что не будет больше передавать ему никаких данных и закрывает соединение "клиент -> сервер", однако готов продолжать принимать от него данные до тех пор, пока сервер будет их посылать, т.е. хочет оставить соединение "сервер -> клиент" открытым.
Для этого необходимо вызвать функцию "int shutdown (SOCKET s,int how)", передав в аргументе how одно из следующих значений: SD_RECEIVE для закрытия канала "сервер -> клиент", SD_SEND для закрытия канала "клиент -> сервер", и, наконец, SD_BOTH для закрытия обоих каналов.
Последний вариант выгодно отличается от closesocket "мягким" закрытием соединения - удаленному узлу будет послано уведомление о желании разорвать связь, но это желание не будет воплощено в действительность, пока тот узел не возвратит свое подтверждение. Таким образом, можно не волноваться, что соединение будет закрыто в самый неподходящий момент.

Внимание: вызов shutdown не освобождает от необходимости закрытия сокета функцией closesocket!

Адрес

С адресами как раз и наблюдается наибольшая путаница. Прежде всего, структура sockaddr определенная так:

struct sockaddr
{
u _ short sa _ family; // семейство протоколов (как правило, AF _ INET)
char sa _ data [14]; // IP -адрес узла и порт
};

Однако, теперь уже считается устаревшей, и в Winsock 2.x на смену ей пришла структура sockaddr_in, определенная следующим образом:

struct sockaddr _ in
{
short sin _ family; // семейство протоколов (как правило, AF _ INET)
u _ short sin _ port; // порт
struct in _ addr sin _ addr; // IP -адрес
char sin _ zero [8]; // хвост
};

В общем-то, ничего не изменилось - замена беззнакового короткого целого на знаковое короткое целое для представления семейства протоколов ничего не дает. Зато теперь адрес узла представлен в виде трех полей - sin_port (номера порта), sin_addr (IP-адреса узла) и "хвоста" из восьми нулевых байт, который остался от 14-символьного массива sa_data. Для чего он нужен? Дело в том, что структура sockaddr не привязана именно к Интернет и может работать и с другими сетями. Адреса же некоторых сетей требуют для своего представления гораздо больше четырех байт - вот и приходится брать с запасом!

Структура in_addr определяется следующим образом:

struct in_addr {
union {
struct {u_char s_b1, s_b2, s_b3, s_b4;} S_un_b; // IP-адрес
struct {u_short s_w1, s_w2;} S_un_w; // IP-адрес
u_long S_addr; // IP-адрес
} S_un;
};

Структура hostent выглядит следующим образом:

struct hostent
{
char FAR * h _ name; // официальное имя узла
char FAR * FAR * h _ aliases; // альтернативные имена узла (массив строк)
short h _ addrtype; // тип адреса
short h _ length; // длина адреса (как правило AF _ INET)
char FAR * FAR * h _ addr _ list; // список указателей на IP -адреса
// ноль - конец списка
};

Определение имени узла по его адресу бывает полезным для серверов, желающих "в лицо" знать своих клиентов.

Для преобразования IP-адреса, записанного в сетевом формате в символьную строку, предусмотрена функция " char FAR * inet _ ntoa (struct in _ addr)", которая принимает на вход структуру in_addr, а возвращает указатель на строку, если преобразование выполнено успешно и ноль в противном случае.

Практическая часть

Задача 1

Наше сетевое программирование начнём с написания программы-клиента, использующей сокеты (sockets).

Итак, начнём. Создадим диалоговое приложение, не забывая поставить галочку около поддержки сокетов во вкладке Advanced. Создадим кнопку Send Request, по нажатию на которую будут происходить все действия.

Напишем функцию, сообщающую нам об ошибках. Раньше мы не писали их за отсутствием острой потребности (хотя это неправильно). Для сокетов же проверки критичны и помогут избежать многих трудностей и неполадок. Чтобы нам было понятно, в чём состоит ошибка, мы её расшифруем на нормальный человеческий язык, заставив систему саму расшифровывать нам коды ошибок. Делается это с помощью функции FormatMessage. В-общем, она принимает код ошибки в третьем параметре и записывает ответ в переменную типа char, переданную ей в качестве пятого параметра. Также ей надо знать размер этой переменной – он передаётся шестым параметром.

void ReportError(HRESULT errorCode, const char *whichFunc)

{

//расшифровываем ошибку

char chErrMsg[1024];

FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, errorCode, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), chErrMsg, sizeof(chErrMsg), NULL);

//форматим для вывода пользователю

CString strErrorMsg;

strErrorMsg. Format ("Вызов функции % s вернул следующую ошибку (код% d): \ n % s ", (char *) whichFunc, errorCode, chErrMsg);

MessageBox(NULL, strErrorMsg, "socketIndication", MB_OK);

//возврат к диалоговому окну:-)

return;

}

Сама функция ReportMessage() принимает в качестве первого параметра код ошибки, в качестве второго параметра она принимает имя функции, из которой сигнализируется об ошибке.

Начнём описывать реакцию программы на нажатие кнопки SendRequest. Реализация начинается с инициализации Winsock’а.

WORD sockVersion;

WSADATA wsaData;

sockVersion = MAKEWORD (1, 1); // используем версию 1.1

WSAStartup (sockVersion, & wsaData); // инициализируем Winsock

Ещё нам понадобится переменная для хранения результата операций

int nret;

и переменная для кода ошибки

HRESULT hr;

Теперь заполним структуру HOSTENT, которая говорит сокету с каким компом и портом связываться. Эта структура обычно фигурирует как переменные типа LPHOSTENT, которые являются попросту указателями на HOSTENT.

LPHOSTENT hostEntry;

in_addr iaHost;

iaHost. s _ addr = inet _ addr (“192.168.0.253”);//это адрес университетского сервера Debian

hostEntry = gethostbyaddr((const char*) &iaHost, sizeof(struct in_addr), AF_INET);

Функция gethostbyaddr заполняет HOSTENT пригодными для дальнейшего использования значениями в случае, когда известен IP-адрес сервера. Иначе

hostEntry = gethostbyname (“ www. uni - protvino. ru ”); /*адрес того же университетского сервера */

и, соответственно, переменная iaHost нам не нужна.

Можно выбрать функцию на свой вкус. Так как мы знаем IP-адрес сервера, то удобнее пользоваться gethostbyaddr.

Далее проверим, что мы получили.

if (hostEntry == NULL)

{

hr = HRESULT_FROM_WIN32(WSAGetLastError());

ReportError(hr, "gethostbyname()");

WSACleanup();

return;

}

Здесь мы получаем код ошибки (в случае, если ошибка есть) с помощью конструкции HRESULT_FROM_WIN32(WSAGetLastError()). Функция FormatMessage требует для работы переменной типа HRESULT – для этого код, полученный с помощью функции WSAGetLastError, мы преобразуем с помощью макроса HRESULT_FROM_WIN32.

Затем создаём сокет и проверяем на правильность создания.

SOCKET theSocket;

theSocket = socket(AF_INET, //go over TCP/IP

SOCK_STREAM, //stream-oriented socket

IPPROTO_TCP); //TCP

if (theSocket == INVALID_SOCKET)

{

hr = HRESULT_FROM_WIN32(WSAGetLastError());

ReportError(hr, "socket()");

WSACleanup();

return;

}

Для дальнейшей работы заполним структуру SOCKADDR_IN:

SOCKADDR_IN serverInfo;

serverInfo.sin_family = AF_INET;

serverInfo.sin_addr = *((LPIN_ADDR)*hostEntry->h_addr_list);

serverInfo. sin _ port = htons (80);

Остаётся добавить, что функция htons() переводит прямой порядок байт в порядок, используемый в сети.

Мы заполнили все нужные структуры, знаем порт и IP-адрес, теперь пора связываться с сервером. Проверка того же типа, что и раньше.

nret = connect(theSocket, (LPSOCKADDR) &serverInfo, sizeof(struct sockaddr));

if (nret!= 0)

{

hr = HRESULT_FROM_WIN32(WSAGetLastError());

ReportError(hr, "connect()");

WSACleanup();

return;

}

Начнём процедуру общения с сервером. Вопросы он понимает не все, а только правильно сформулированные. Мы попросим дать нам страницу index.htm.

//в этом буфере будет запрос серверу

char bufferSend [128] = "";

/*Сформируем запрос. Большинство серверов воспринимают пустой запрос GET / как запрос на страничку index. htm. В конце запроса ОБЯЗАТЕЛЬНО должна идти пустая строка, иначе сервер не прекратит нас слушать.*/

strcpy(bufferSend, "GET / HTTP/1.0\r\n");

strcat(bufferSend, "\r\n");

//спрашиваем

nret = send(theSocket, bufferSend, strlen(bufferSend), 0);

//проверяем на ошибки

if (nret == SOCKET_ERROR)

{

hr = HRESULT_FROM_WIN32(WSAGetLastError());

ReportError(hr, "send()");

WSACleanup();

return;

}

Мы задали вопрос и должны ждать и принимать ответ. Сначала создадим на диалоговом окне два поля EditBox - одно побольше, другое поменьше. Добавим большому полю переменную m_strSimpleDisplay – здесь будет выдаваться полученная информация. А маленькому полю добавим переменную m_strReturned – тут будем выдавать количество полученных байт.

m_strSimpleDisplay = "";

m_strReturned = "";

UpdateData(FALSE);

Начнём принимать информацию

while (1)

{

nret = recv(theSocket, bufferRecv, sizeof(bufferRecv), 0);

//заполняем поле m_strReturned

char charRet[128] = ””;

itoa(nret, charRet, 10);

strcat(charRet, " bytes\r\n");

m_strReturned += ret;

//заполняем поле m_strSimpleDisplay

m_strSimpleDisplay += bufferRecv;

UpdateData(FALSE);

if (nret == 0 || nret == -1)

break;

}

Компилируем, запускаем и нажимаем кнопку SendRequest. Смотрим, каким образом присылаются данные.

Пример 2


Поделиться с друзьями:

Общие условия выбора системы дренажа: Система дренажа выбирается в зависимости от характера защищаемого...

Эмиссия газов от очистных сооружений канализации: В последние годы внимание мирового сообщества сосредоточено на экологических проблемах...

История развития хранилищ для нефти: Первые склады нефти появились в XVII веке. Они представляли собой землянные ямы-амбара глубиной 4…5 м...

Опора деревянной одностоечной и способы укрепление угловых опор: Опоры ВЛ - конструкции, предназначен­ные для поддерживания проводов на необходимой высоте над землей, водой...



© cyberpedia.su 2017-2024 - Не является автором материалов. Исключительное право сохранено за автором текста.
Если вы не хотите, чтобы данный материал был у нас на сайте, перейдите по ссылке: Нарушение авторских прав. Мы поможем в написании вашей работы!

0.12 с.