Глобальные сети компьютеров. Практическое введение в Internet, E-Mail, FTP, WWW и HTML, программирование для Windows Sockets© Александр Фролов, Григорий ФроловТом 23, М.: Диалог-МИФИ, 1993, 283 стр. 5.4. Создание канала связиЕсли вы собираетесь передавать датаграммные сообщения при помощи протокола негарантированной доставки UDP , канал связи не нужен. Сразу после создания сокетов и их инициализации можно приступать к передаче данных. Но для передачи данных с использованием протокола TCP необходимо создать канал связи. Сторона сервераРассмотрим процедуру создания канала связи со стороны сервера. Прежде всего вы должны переключить сокет в режим приема для выполнения ожидания соединения с клиентом при помощи функции listen: int listen(SOCKET sock, int backlog); Через параметр sock функции необходимо передать дескриптор сокета, который будет использован для создания канала. Параметр backlog задает максимальный размер очереди для ожидания соединения (можно указывать значения от 1 до 5). Очередь содержит запросы на установку соединений для каждой пары значений (адрес IP, порт). Ниже мы привели список возможных кодов ошибок
для функции listen.
Ниже мы привели пример вызов функции listen: if(listen(srv_socket , 1) == SOCKET_ERROR ) { closesocket (srv_socket); MessageBox(NULL, "listen Error", "Error", MB_OK); return; } Далее необходимо выполнить ожидание соединения. Это можно выполнить двумя различными способами. Первый способ заключается в циклическом вызове функции accept до тех пор, пока не будет установлено соединение. Затем можно будет приступать к обмену данными. Функция accept имеет следующий прототип: SOCKET accept (SOCKET sock, struct sockaddr FAR * addr, int FAR * addrlen); Через параметр sock необходимо указать дескриптор сокета, который находится в режиме приема для выполнения ожидания. Параметр addr должен содержать адрес буфера, в который будет записан адрес узла, подключившегося к серверу. Размер этого буфера необходимо указать в переменной типа int, адрес которой передается через параметр addrlen. Если ожидание соединения в цикле не вызывает у вас особого энтузиазма, можно предложить более удобный способ, основанный на использовании расширения программного интерфейса Windows Socket, предназначенного для выполнения асинхронных операций. Приведем список возможных кодов ошибок для
функции accept.
Вместо того чтобы ожидать соединение, вызывая в цикле функцию accept , ваше приложение может вызвать один раз функцию WSAAsyncSelect , указав ей, что при получении запроса на установку соединения функция окна вашего приложения должна получить сообщение: #define WSA_ACCEPT (WM_USER + 1) // При попытке установки соединения главное окно приложения // получит сообщение WSA_ACCEPT rc = WSAAsyncSelect (srv_socket , hWnd, WSA_ACCEPT, FD_ACCEPT ); if(rc > 0) { closesocket (srv_socket); MessageBox(NULL, "WSAAsyncSelect Error", "Error", MB_OK); return; } В данном случае ожидание соединения выполняется для сокета srv_socket . Последний параметр функции имеет значение FD_ACCEPT . Это означает, что при попытке создания канала связи функция окна с идентификатором hWnd получит сообщение WSA_ACCEPT, определенное в вашем приложении. Обработчик этого сообщения может выглядеть, например, следующим образом: void WndProc_OnWSAAccept(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) { int rc; // При ошибке отменяем поступление извещений // в главное окно приложения if(WSAGETSELECTERROR(lParam) != 0) { MessageBox(NULL, "accept Error", "Error", MB_OK); WSAAsyncSelect (srv_socket , hWnd, 0, 0); return; } // Определяем размер адреса сокета acc_sin_len = sizeof(acc_sin); // Разрешаем установку соединения srv_socket = accept (srv_socket, (LPSOCKADDR )&acc_sin, (int FAR *)&acc_sin_len); if(srv_socket == INVALID_SOCKET) { MessageBox(NULL, "accept Error, invalid socket ", "Error", MB_OK); return; } // Если на данном сокете начнется передача данных от // клиента, в главное окно приложения поступит // сообщение WSA_NETEVENT. // Это же сообщение поступит при разрыве соединения rc = WSAAsyncSelect (srv_socket , hWnd, WSA_NETEVENT, FD_READ | FD_CLOSE ); if(rc > 0) { closesocket (srv_socket); MessageBox(NULL, "WSAAsyncSelect Error", "Error", MB_OK); return; } } В данном случае обработчик сообщения вначале вызывает функцию accept , выполняющую создание канала передачи данных. После этого функция WSAAsyncSelect вызывается еще один раз для того чтобы установить асинхронную обработку приема данных от удаленного клиента, а также обработку ситуации разрыва канала связи. Сторона клиентаРассмотрим процедуру установки канала связи со стороны клиента, использованную нами в приложении CLIENT, исходные тексты которого будут приведены ниже. Для установки соединения в приложении используется функция SetConnection: SOCKADDR _IN dest_sin; void SetConnection(HWND hWnd) { PHOSTENT phe; // Создаем сокет srv_socket = socket(AF_INET , SOCK_STREAM, 0); if(srv_socket == INVALID_SOCKET) { MessageBox(NULL, "socket Error", "Error", MB_OK); return; } // Устанавливаем адрес IP и номер порта dest_sin.sin_family = AF_INET ; // Определяем адрес узла phe = gethostbyname ("localhost "); if(phe == NULL) { closesocket (srv_socket); MessageBox(NULL, "gethostbyname Error", "Error", MB_OK); return; } // Копируем адрес узла memcpy((char FAR *)&(dest_sin.sin_addr ), phe->h_addr , phe->h_length); // Копируем номер порта dest_sin.sin_port = htons(SERV_PORT); // Устанавливаем соединение if(connect(srv_socket , (PSOCKADDR )&dest_sin, sizeof(dest_sin)) < 0) { closesocket (srv_socket); MessageBox(NULL, "connect Error", "Error", MB_OK); return; } } Вначале с помощью функции socket эта функция создает сокет. Затем выполняется заполнение адресной информацией структуры dest_sin. Обратите внимание, что для получения адреса IP мы воспользовались функцией gethostbyname , указав ей имя узла localhost . Это имя отображается в файле HOSTS на адрес 127.0.0.1 :
Адрес 127.0.0.1 является локальным. Вы можете использовать его для тестирования приложений, выполняющих обмен данными при помощи протокола TCP/IP, запуская сервер и клиент на одном и том же компьютере. После заполнения структуры с адресной информацией функция connect создает канал связи с сервером. |