Совместно с этой структурой (как в Windows, так и в Unix) используется структура sockaddr_in. В ней хранятся пары (сетевой адрес, порт). Последнее поле sin_zero служит для выравнивания размера структуры (так чтобы она могла быть преобразована в структуру sockaddr).
struct sockaddr_in
{
short sin_family; // Address family. Оно должно быть равно AF_INET
unsigned short sin_port; // IP port
struct in_addr sin_addr; // IP address
char sin_zero[8]; // Padding to make structure the same size as SOCKADDR
};
С помощью sockaddr_in любой (локальный или удаленный) адрес (а точнее, пара типа endpoint) могут быть зааданы или переданы в какую-либо из функций Windows Sockets. Поле sin_addr в свою очередь является структурой типа in_addr. Она определена следующим образом:
struct in_addr
{
union
{
struct { unsigned char s_b1, s_b2, s_b3, s_b4; } S_un_b;
struct { unsigned short s_w1, s_w2; } S_un_w;
unsigned long S_addr;
} S_un;
};
Этот трюк означает, что одна и та же сущность (структура типа in_addr) может быть интерпретирована либо как четыре отдельных байта, либо как две переменных по 2 байта, либо как одна переменная 4 байта. Вот пример использования рассмотренных структур.
sockaddr_in addr; // Объявление структуры
addr.sin_family = AF_INET; // Установка типа
addr.sin_addr.s_addr = inet_addr("127.0.0.1"); // Установка адреса
addr.sin_port = htons (27015); // Установка номера порта
Как вы догадались, вспомогательная функция inet_addr преобразовывает текстовое представление адреса в формате Ipv4 (IP-адрес, разделяемый точками) в адрес, отдельные поля которого записаны в структуре in_addr. Функция htons также служит для преобразования 16-битового числа в одном формате в такое же число, но в другом формате. Дело в том, что протокол TCP/IP использует порядок следования байт: big-endian (старший справа), и он не соответствует порядку следования байт при задании номера порта (старший слева).
Этот курьез—результат невнимания к проблемам стандартизации. Например, hex-код латинской буквы 'A' (U+0041) в процессоре Intel представлен в виде: 4100 (Little-endian), а в протоколе TCP в виде: 0041 (Big-endian).
При передаче данных между узлами сети надо учитывать тип разъема (SOCK_STREAM или SOCK_DGRAM). Первый тип, как указано ранее, ориентирован на соединение (connection-oriented socket), а второй—нет (сonnectionless socket). Следующая диаграмма показывает последовательность вызова функций на стороне клиента и сервера, используемая в первом типе разъемов.
После создания разъема серверное приложение привязывает (bind) к нему локальный IP-адрес и номер порта, затем оно переходит в режим ожидания соединения с другим (клиентским) приложением. Этот переход представляет собой двухшаговую процедуру:
¨ Сначала вызывается функция listen, перводящая разъем в режим прослушивания сообщений из порта,
¨ Затем циклически вызывается функция accept, которая выявляет попытку клиента получить соединение и прекращает цикл прослушивания.
После этого приложение входит в цикл обмена сообщениями, который есть не что иное, как попеременный вызов функций recv (получи) и send (отошли). Алгоритм обработки получаемых данных и генерации ответов целиком определяется разработчиком серверного приложения. Алгоритм клиентской части диаграммы достаточно прост и вы без труда поймете смысл вызываемых функций.
Рассмотрим, как осуществить соединение и обмен данными между двумя приложениями средствами Winsock. В нашем примере алгоритм обработки получаемых данных будет примитивным.
¨ Создайте новое пустое решение (Blank Solution) с именем WinSock,
¨ Добавьте в него проект типа Visual C++ Win32 с именем WinSockServer.
¨ Добавьте в решение еще один проект типа Visual C++ Win32 с именем WinSockClient.
¨ Замените коды файлов stdafx.h (в обоих проектах) на тот, что приведен ниже.
#pragma once
Уважаемый посетитель!
Чтобы распечатать файл, скачайте его (в формате Word).
Ссылка на скачивание - внизу страницы.