Winsock

Windows Sockets API (WSA), название которого было укорочено до Winsock. Это техническая спецификация, которая определяет, как сетевое программное обеспечение Windows будет получать доступ к сетевым сервисам, в том числе TCP/IP. Он определяет стандартный интерфейс между клиентским приложением (таким как FTP-клиент или веб-браузер) и внешним стеком протоколов TCP/IP. Он основывается на API модели сокетов Беркли, использующейся в BSD для установки соединения между программами.

Предыстория

Ранние операционные системы Microsoft, такие как MS-DOS и Microsoft Windows, имели ограничения по работе с сетью, которые были связаны с использованием протокола NetBIOS. В частности, Microsoft в то время не поддерживал работу со стеком протоколов TCP/IP. Несколько университетских групп и коммерческих фирм, включая MIT, FTP Software, Sun Microsystems, Ungermann-Bass и Excelan, представляли свои решения для работы с TCP/IP в MS-DOS, часто как часть программно-аппаратного комплекса. После выпуска Microsoft Windows 2.0 к этим разработчикам присоединились и другие, такие как Distinct и NetManage, которые помогли в организации поддержки протоколов TCP/IP для Windows. Недостаток, с которыми столкнулись все вышеперечисленные разработчики, состоял в том, что каждый из них использовал свои собственные API (Application Programming Interface). Без единой стандартной модели программирования трудно было убедить независимых разработчиков программного обеспечения в создании сетевых программ, которые могли бы работать на основе реализации стека протоколов TCP/IP любого из разработчиков. Стало понятно, что необходима стандартизация.

Модель Sockets API Windows была предложена Мартином Холлом из JSB Software (позднее Stardust Technologies) в рамках информационной группы BOF (Birds of a Feather) и согласована в сети CompuServe на BBS в октябре 1991 года. Первое издание спецификации было написано Мартином Холлом, Марком Tовфиком из Microdyne (позднее Sun Microsystems), Джеффом Арнольдом (Sun Microsystems), Генри Сандерс и Дж. Алардом из Microsoft, и при участии многих других разработчиков. Возникли вопросы о том, кому присвоить авторские права, права интеллектуальной собственности. В конце концов было решено, что авторские права на спецификацию будут принадлежать пяти авторам как физическим лицам.

Начиная с Windows 2000 Winsock работает через Transport Driver Interface

Windows 8 включает в себя RIO (Registered IO), расширяющий возможности Winsock

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

Из инструментария, входящего в SDK, в первую очередь хотелось бы выделить утилиту sockeye.exe - это настоящий "тестовый стенд" разработчика. Она позволяет в интерактивном режиме вызывать различные сокет-функции и манипулировать ими по своему усмотрению.

Демонстрационные программы, к сожалению, не лишены ошибок, причем порой довольно грубых и наводящих на мысли - а тестировались ли эти примеры вообще? (Например, в исходном тексте программы simples.c в вызове функций send и sendto вместо strlen стоит sizeof) В то же время, все примеры содержат множество подробных комментариев и раскрывают довольно любопытные приемы нетрадиционного программирования, поэтому ознакомится с ними все-таки стоит.

Из WEB-ресурсов, посвященных программированию сокетов, и всему, что с ними связано, в первую очередь хотелось бы отметить следующие три: sockaddr.com; www.winsock.com и www.sockets.com.

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

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

ОС Windows 3.x поддерживает только асинхронные сокеты, поскольку, в среде с корпоративной многозадачностью захват управления одной задачей "подвешивает" все остальные, включая и саму систему. ОС Windows 9x\NT поддерживают оба вида сокетов, однако, в силу того, что синхронные сокеты программируются более просто, чем асинхронные, последние не получили большого распространения.

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

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

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

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

Все функции сокетов содержаться в wsock32.dll перед тем как написать программу с использование функций сокетов необходимо задать компилятору чтобы он включил в программу файл wsock32.lib. В меню Рroject выберите пункт settings а там укажите раздел Link, wsock32.lib можно ввести в поле Library modules или project options или непосредственно в исходнике вписать #pragma comment(lib,"Ws2_32.lib"). Не забудьте поставить #include "Winsock2.h".

Definitions for WinSock Version 1.1 API (скачать)
Definitions for WinSock Version 2 API API (скачать)

Устаревшее определение структуры 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-адреса узла) и "хвоста" из восьми нулевых байт, который остался от четырнадцати символьного массива 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;
}

Как видно, она состоит из одного IP-адреса, записанного в трех "ипостасях" - четырехбайтовой последовательности (S_un_b), пары двухбайтовых слов (S_un_W) и одного длинного целого (S_addr) - выбирай на вкус Но не все так просто! Во многих программах, технических руководствах и даже демонстрационных примерах, прилагающихся к Winsock SDK, встречается обращение к "таинственному" члену структуры s_addr, который явно не описан в SDK! Например, вот строка из файла "Simples.h": "local.sin_addr.s_addr = (!interface)?INADDR_ANY:inet_addr(interface);"

Это что такое?! Заглянув в файл "winsock2.h" можно обнаружить следующее: "#define s_addr S_un.S_addr". Ага, да ведь это эквивалент s_addr, т.е. IP-адресу, записанному в виде длинного целого!

На практике можно с одинаковым успехом пользоваться как "устаревшей" sockaddr, так и "новомодной" sockaddr_in. Однако, поскольку, прототипы остальных функций не изменились, при использовании sockaddr_in придется постоянно выполнять явные преобразования, например так: "sockaddr_in dest_addr; connect (mysocket, (struct sockaddr*) &dest_addr, sizeof(dest_addr)".

Прежде чем воспользоваться функцией socket необходимо проинициализировать процесс библиотеки wsock32.dll вызвав функцию WSAStartup. Если инициализация состоялась, то вернется нулевое значение. Вобщем-то инициализация заключается в сопоставлении номера версии и реально существуюшей DLL в системе.

int WSAStartup (WORD wVersionRequested, LPWSADATA lpWSAData);

Первый параметр - это версия, которая будет использоваться. Младший байт основная версия, старший байт расширение версии. Если инициализация состоялась, то вернется нулевое значение. Вобщем-то инициализация заключается в сопоставлении номера версии и реально существуюшей DLL в системе.

Второй параметр - это указатель на структуру WSADATA, в которую возвратятся параметры инициализации.

typedef struct WSAData {       
	WORD wVersion;
	WORD wHighVersion;
	char szDescription[WSADESCRIPTION_LEN+1];
	char szSystemStatus[WSASYS_STATUS_LEN+1];
	unsigned short iMaxSockets;
	unsigned short iMaxUdpDg;
	char FAR * lpVendorInfo;
} WSADATA, FAR * LPWSADATA; 
WSADATA WsaData; int err = WSAStartup (0x0101, &WsaData); if (err == SOCKET_ERROR) { printf ("WSAStartup() failed: %ld\n", GetLastError ()); return 1; } или // WinSock1.cpp : Defines the entry point for the console application. // #include "stdafx.h" #include "winsock.h" const int WINSOCK_VERSION = 0x0101; void main() { WSADATA wsaData; if (WSAStartup(WINSOCK_VERSION, &wsaData)) { printf ("winsock not bi initialized !\n"); WSACleanup(); } else printf("Winsock initial OK !!!!\n"); if (WSACleanup()) printf("Error Cleapir\n"); else printf("Cleapir Good !!!!!\n");

Здесь 0х0101 версия библиотеки которую следует использовать.

Функция WSACleanup() завершает использование данного DLL и прерывает обращение к функциям WinSock. При удачном выполнении вернется нуль.


Функция socket()

создает сокет, привязанный к определенному поставщику транспортных услуг
SOCKET WSAAPI socket(
  _In_ int af,
  _In_ int type,
  _In_ int protocol
);

Параметры

af [in]

Спецификация семейства адресов. Возможные значения для семейства адресов определены в заголовочном файле Winsock2.h. В Windows SDK, выпущенном для Windows Vista и более поздних версий, была изменена организация файлов заголовков, а возможные значения для семейства адресов определены в заголовочном файле Ws2def.h. Обратите внимание, что заголовочный файл Ws2def.h автоматически включается в Winsock2.h и никогда не должен использоваться напрямую.

В настоящее время поддерживаются значения AF_INET или AF_INET6, которые являются форматами семейства интернет-адресов для IPv4 и IPv6. Другие варианты для семейства адресов (AF_NETBIOS для использования с NetBIOS, например) поддерживаются, если установлен поставщик услуг Windows Sockets для семейства адресов. Обратите внимание, что значения семейства семейств AF_ и констант семейства PF_ идентичны (например, AF_INET и PF_INET), поэтому можно использовать константу.

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

AF_UNSPEC0Семейство адресов не указано
AF_INET2Семейство адресов интернет-протокола версии 4 (IPv4)
AF_IPX6Семейство адресов IPX / SPX. Это семейство адресов поддерживается только в том случае, если установлен протокол NWLink IPX / SPX NetBIOS Compatible Transport. Это семейство адресов не поддерживается в Windows Vista и более поздних версиях
AF_APPLETALK16Семейство адресов AppleTalk. Это семейство адресов поддерживается только в том случае, если установлен протокол AppleTalk. Это семейство адресов не поддерживается в Windows Vista и более поздних версиях
AF_NETBIOS17Семейство адресов NetBIOS. Это семейство адресов поддерживается только в том случае, если установлен поставщик Windows Sockets для NetBIOS. Поставщик Windows Sockets для NetBIOS поддерживается в 32-разрядных версиях Windows. Этот провайдер устанавливается по умолчанию в 32-разрядных версиях Windows. Поставщик Windows Sockets для NetBIOS не поддерживается в 64-разрядных версиях Windows, включая Windows 7, Windows Server 2008, Windows Vista, Windows Server 2003 или Windows XP. Поставщик Windows Sockets для NetBIOS поддерживает только сокеты, где параметр типа установлен в SOCK_DGRAM. Поставщик Windows Sockets для NetBIOS напрямую не связан с программным интерфейсом NetBIOS. Интерфейс программирования NetBIOS не поддерживается в Windows Vista, Windows Server 2008 и более поздних версиях
AF_INET623Семейство адресов интернет-протокола версии 6 (IPv6)
AF_IRDA26Семейство адресов инфракрасной ассоциации данных (IrDA). Это семейство адресов поддерживается только в том случае, если на компьютере установлен инфракрасный порт и драйвер
AF_BTH32Семейство адресов Bluetooth. Это семейство адресов поддерживается в Windows XP с пакетом обновления 2 (SP2) или более поздней версии, если на компьютере установлен адаптер Bluetooth и драйвер

type [in]
Спецификация типа для нового сокета. Возможные значения типа сокета определены в файле заголовка Winsock2.h. В следующей таблице перечислены возможные значения параметра типа, поддерживаемого для Windows Sockets 2:
SOCK_STREAM1Тип сокета, который обеспечивает последовательные, надежные двухсторонние потоки байтов на основе соединения с механизмом передачи данных OOB. Этот тип сокета использует протокол управления передачей (TCP) для семейства интернет-адресов (AF_INET или AF_INET6)
SOCK_DGRAM2Тип сокета, который поддерживает дейтаграммы, которые являются бесконтактными, ненадежными буферами фиксированной (как правило, малой) максимальной длины. Этот тип сокета использует протокол пользовательских дейтаграмм (UDP) для семейства интернет-адресов (AF_INET или AF_INET6)
SOCK_RAW3Тип сокета, который обеспечивает сырой сокет, который позволяет приложению манипулировать следующим заголовком протокола верхнего уровня. Чтобы управлять заголовком IPv4, в гнезде должна быть установлена ​​опция сокета IP_HDRINCL. Чтобы управлять заголовком IPv6, в гнезде должна быть установлена ​​опция сокета IPV6_HDRINCL
SOCK_RDM4Тип сокета, который обеспечивает надежную дейтаграмму сообщения. Примером такого типа является реализация протокола многоадресной передачи Multigast (PGM) в Windows, часто называемая надежным многоадресным программированием. Это значение типа поддерживается только в том случае, если установлен протокол надежной многоадресной передачи
SOCK_SEQPACKET5Тип сокета, который предоставляет пакет псевдопотоков на основе датаграмм

В Windows Sockets 2 были введены новые типы гнезд. Приложение может динамически обнаруживать атрибуты каждого доступного транспортного протокола через функцию WSAEnumProtocols. Таким образом, приложение может определить возможные типы сокетов и параметры протокола для семейства адресов и использовать эту информацию при указании этого параметра. Определения типов сокетов в файлах заголовков Winsock2.h и Ws2def.h будут периодически обновляться по мере определения новых типов сокетов, семейств адресов и протоколов.

В Windows Sockets 1.1 единственными возможными типами сокетов являются SOCK_DGRAM и SOCK_STREAM.

protocol [in]

Используемый протокол. Возможные параметры параметра протокола зависят от типа семейства адресов и типа сокета. Возможные значения протокола определены в файлах заголовков Winsock2.h и Wsrm.h. В Windows SDK, выпущенном для Windows Vista и более поздних версий, была изменена организация файлов заголовков, и этот параметр может быть одним из значений типа перечисления IPPROTO, определенного в заголовочном файле Ws2def.h. Обратите внимание, что заголовочный файл Ws2def.h автоматически включается в Winsock2.h и никогда не должен использоваться напрямую.

Если указано значение 0, вызывающий абонент не желает указывать протокол, и поставщик услуг выбирает используемый протокол. Когда параметр af является AF_INET или AF_INET6, а тип SOCK_RAW, значение, указанное для протокола, устанавливается в поле протокола заголовка пакета IPv6 или IPv4.

IPPROTO_ICMP1Протокол управляющих сообщений Интернета (ICMP). Это возможное значение, когда параметр af - AF_UNSPEC, AF_INET или AF_INET6, а параметр типа - SOCK_RAW или неуказан. Это значение протокола поддерживается в Windows XP и более поздних версиях
IPPROTO_IGMP2Протокол управления группами Интернета (IGMP). Это возможное значение, когда параметр af - AF_UNSPEC, AF_INET или AF_INET6, а параметр типа - SOCK_RAW или неуказан. Это значение протокола поддерживается в Windows XP и более поздних версиях
BTHPROTO_RFCOMM3Протокол радиочастотной связи Bluetooth (Bluetooth RFCOMM). Это возможное значение, когда параметр af - AF_BTH, а параметр типа - SOCK_STREAM. Это значение протокола поддерживается в Windows XP с пакетом обновления 2 (SP2) или более поздней версии
IPPROTO_TCP6Протокол управления передачей (TCP). Это возможное значение, когда параметр af - AF_INET или AF_INET6, а параметр типа - SOCK_STREAM
IPPROTO_UDP17Протокол пользовательских дейтаграмм (UDP). Это возможное значение, когда параметр af - AF_INET или AF_INET6, а параметр типа - SOCK_DGRAM
IPPROTO_ICMPV658Протокол управления интернет-протоколом версии 6 (ICMPv6). Это возможное значение, когда параметр af - AF_UNSPEC, AF_INET или AF_INET6, а параметр типа - SOCK_RAW или неуказан. Это значение протокола поддерживается в Windows XP и более поздних версиях
IPPROTO_RM113Протокол PGM для надежной многоадресной рассылки. Это возможное значение, когда параметр af является AF_INET, а параметр типа - SOCK_RDM. В Windows SDK, выпущенном для Windows Vista и более поздних версий, этот протокол также называется IPPROTO_PGM. Это значение протокола поддерживается только в том случае, если установлен протокол надежной многоадресной передачи.

Возвращаемое значение

Если ошибка не возникает, сокет возвращает дескриптор, ссылающийся на новый сокет. В противном случае возвращается значение INVALID_SOCKET, и конкретный код ошибки можно получить, вызвав WSAGetLastError.

WSANOTINITIALISEDПеред использованием этой функции должен произойти успешный вызов WSAStartup.
WSAENETDOWNПодсистема сети или связанный с ней поставщик услуг потерпели неудачу.
WSAEAFNOSUPPORTУказанное семейство адресов не поддерживается. Например, приложение попыталось создать сокет для семейства адресов AF_IRDA, но инфракрасный адаптер и драйвер устройства не установлены на локальном компьютере.
WSAEINPROGRESSВыполняется блокировка вызовов Windows Sockets 1.1 или поставщик услуг по-прежнему обрабатывает функцию обратного вызова.
WSAEMFILEБольше дескрипторов сокетов нет.
WSAEINVALБыл указан недопустимый аргумент. Эта ошибка возвращается, если параметр af установлен в AF_UNSPEC, а параметр типа и протокола не задан.
WSAEINVALIDPROVIDERПоставщик услуг вернул версию, отличную от версии 2.2.
WSAEINVALIDPROCTABLEПоставщик услуг вернул неверную или неполную таблицу процедур WSPStartup.
WSAENOBUFSБуферное пространство отсутствует. Сокет не может быть создан.
WSAEPROTONOSUPPORTУказанный протокол не поддерживается.
WSAEPROTOTYPEУказанный протокол является неправильным типом для этого сокета.
WSAEPROVIDERFAILEDINITПоставщик услуг не смог инициализировать. Эта ошибка возвращается, если поставщик одноранговых услуг (LSP) или поставщик пространства имен был неправильно установлен или поставщик не работает правильно.
WSAESOCKTNOSUPPORTУказанный тип сокета не поддерживается в этом семействе адресов.

Замечания

Функция сокета приводит к тому, что дескриптор сокета и любые связанные ресурсы выделяются и привязаны к конкретному поставщику транспортных услуг. Winsock будет использовать первый доступный поставщик услуг, который поддерживает запрошенную комбинацию семейства адресов, типа сокета и параметров протокола. Созданный сокет будет иметь перекрываемый атрибут по умолчанию. Для Windows опция сокета Microsoft, SO_OPENTYPE, определенная в Mswsock.h, может повлиять на это значение по умолчанию. См. Документацию по Microsoft для подробного описания SO_OPENTYPE.

Сокеты без перекрываемого атрибута могут быть созданы с помощью WSASocket. Все функции, которые позволяют перекрывать операции (WSASend, WSARecv, WSASendTo, WSARecvFrom и WSAIoctl), также поддерживают неперекрываемое использование на перекрываемом сокете, если значения параметров, связанных с перекрытием, имеют значение NULL.

При выборе протокола и его поддерживающего поставщика услуг эта процедура будет выбирать только базовый протокол или цепочку протоколов, а не уровень протокола. Слои нерастянутого протокола не считаются частичными совпадениями по типу или af. То есть, они не приводят к коду ошибки WSAEAFNOSUPPORT или WSAEPROTONOSUPPORT, если подходящий протокол не найден. Примечание. Константа манифеста AF_UNSPEC по-прежнему определяется в файле заголовка, но ее использование сильно не рекомендуется, так как это может вызвать неоднозначность при интерпретации значения параметра протокола.

Приложениям рекомендуется использовать AF_INET6 для параметра af и создавать двухрежимный сокет, который можно использовать как с IPv4, так и с IPv6.

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

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

Бесконтактные, ориентированные на сообщения сокеты позволяют отправлять и принимать датаграммы с произвольными одноранговыми узлами, используя sendto и recvfrom. Если такой сокет подключен к определенному одноранговому узлу, датаграммы могут быть отправлены этому партнеру с использованием send и могут быть получены только от этого однорангового узла с использованием recv.

IPv6 и IPv4 работают по-разному при получении сокета с типом SOCK_RAW. Пакет приема IPv4 включает в себя полезную нагрузку пакета, следующий заголовок верхнего уровня (например, заголовок IP для пакета TCP или UDP) и заголовок пакета IPv4. Пакет приема IPv6 включает в себя полезную нагрузку пакета и следующий заголовок верхнего уровня. Пакет приема IPv6 никогда не включает заголовок пакета IPv6.

Примечание. В Windows NT поддержка raw-сокета требует административных привилегий.

Сокет с параметром типа SOCK_SEQPACKET основан на дейтаграммах, но функционирует как протокол псевдопотока. Для пакетов отправки и получения используются отдельные датаграммы. Однако Windows Sockets может объединять несколько пакетов приема в один пакет. Таким образом, приложение может выдавать приемный вызов (например, recv или WSARecvEx) и извлекать данные из нескольких объединенных нескольких пакетов в одном вызове. Семейство адресов AF_NETBIOS поддерживает параметр типа SOCK_SEQPACKET.

Когда параметр af является AF_NETBIOS для NetBIOS через TCP / IP, параметр типа может быть SOCK_DGRAM или SOCK_SEQPACKET. Для семейства адресов AF_NETBIOS параметром протокола является номер адаптера LAN, представленный как отрицательное число.

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

Каталог netsh winsock show

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

Примечания для гнезд IrDA Помните следующее: Файл заголовка Af_irda.h должен быть явно включен. Поддерживается только SOCK_STREAM; Тип SOCK_DGRAM не поддерживается IrDA. Параметр протокола всегда установлен равным 0 для IrDA.

Сокет для использования с семейством адресов AF_IRDA может быть создан только в том случае, если на локальном компьютере установлен инфракрасный порт и драйвер. В противном случае вызов функции сокета с параметром af, установленным в AF_IRDA, завершится с ошибкой, а WSAGetLastError вернет WSAEPROTONOSUPPORT.

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

#ifndef UNICODE
#define UNICODE 1
#endif

// link with Ws2_32.lib
#pragma comment(lib,"Ws2_32.lib")

#include 
#include 
#include 
#include    // Needed for _wtoi


int __cdecl wmain(int argc, wchar_t **argv)
{

    //-----------------------------------------
    // Declare and initialize variables
    WSADATA wsaData = {0};
    int iResult = 0;

//    int i = 1;

    SOCKET sock = INVALID_SOCKET;
    int iFamily = AF_UNSPEC;
    int iType = 0;
    int iProtocol = 0;

    // Validate the parameters
    if (argc != 4) {
        wprintf(L"usage: %s   \n", argv[0]);
        wprintf(L"socket opens a socket for the specified family, type, & protocol\n");
        wprintf(L"%ws example usage\n", argv[0]);
        wprintf(L"   %ws 0 2 17\n", argv[0]);
        wprintf(L"   where AF_UNSPEC=0 SOCK_DGRAM=2 IPPROTO_UDP=17\n", argv[0]);
        return 1;
    }

    iFamily = _wtoi(argv[1]);
    iType = _wtoi(argv[2]);
    iProtocol = _wtoi(argv[3]);
    
    // Initialize Winsock
    iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
    if (iResult != 0) {
        wprintf(L"WSAStartup failed: %d\n", iResult);
        return 1;
    }

    wprintf(L"Calling socket with following parameters:\n");
    wprintf(L"  Address Family = ");
    switch (iFamily) {
    case AF_UNSPEC:
        wprintf(L"Unspecified");
        break;
    case AF_INET:
        wprintf(L"AF_INET (IPv4)");
        break;
    case AF_INET6:
        wprintf(L"AF_INET6 (IPv6)");
        break;
    case AF_NETBIOS:
        wprintf(L"AF_NETBIOS (NetBIOS)");
        break;
    case AF_BTH:
        wprintf(L"AF_BTH (Bluetooth)");
        break;
    default:
        wprintf(L"Other");
        break;
    }
    wprintf(L" (%d)\n", iFamily);
    
    wprintf(L"  Socket type = ");
    switch (iType) {
    case 0:
        wprintf(L"Unspecified");
        break;
    case SOCK_STREAM:
        wprintf(L"SOCK_STREAM (stream)");
        break;
    case SOCK_DGRAM:
        wprintf(L"SOCK_DGRAM (datagram)");
        break;
    case SOCK_RAW:
        wprintf(L"SOCK_RAW (raw)");
        break;
    case SOCK_RDM:
        wprintf(L"SOCK_RDM (reliable message datagram)");
        break;
    case SOCK_SEQPACKET:
        wprintf(L"SOCK_SEQPACKET (pseudo-stream packet)");
        break;
    default:
        wprintf(L"Other");
        break;
    }
    wprintf(L" (%d)\n", iType);

    wprintf(L"  Protocol = %d = ", iProtocol);
    switch (iProtocol) {
    case 0:
        wprintf(L"Unspecified");
        break;
    case IPPROTO_ICMP:
        wprintf(L"IPPROTO_ICMP (ICMP)");
        break;
    case IPPROTO_IGMP:
        wprintf(L"IPPROTO_IGMP (IGMP)");
        break;
    case IPPROTO_TCP:
        wprintf(L"IPPROTO_TCP (TCP)");
        break;
    case IPPROTO_UDP:
        wprintf(L"IPPROTO_UDP (UDP)");
        break;
    case IPPROTO_ICMPV6:
        wprintf(L"IPPROTO_ICMPV6 (ICMP Version 6)");
        break;
    default:
        wprintf(L"Other");
        break;
    }
    wprintf(L" (%d)\n", iProtocol);

    sock = socket(iFamily, iType, iProtocol);
    if (sock == INVALID_SOCKET) 
        wprintf(L"socket function failed with error = %d\n", WSAGetLastError() );
    else {
        wprintf(L"socket function succeeded\n");

        // Close the socket to release the resources associated
        // Normally an application calls shutdown() before closesocket 
        //   to  disables sends or receives on a socket first
        // This isn't needed in this simple sample
        iResult = closesocket(sock);
        if (iResult == SOCKET_ERROR) {
            wprintf(L"closesocket failed with error = %d\n", WSAGetLastError() );
            WSACleanup();
            return 1;
        }    
    }

    WSACleanup();

    return 0;
}

Функция accept()

Функция accept допускает попытку входящего соединения сокета. Синтаксис C ++
SOCKET принимает (
  _In_ SOCKET s,
  _Out_ struct sockaddr * addr,
  _Inout_ int * addrlen
);

Параметры

s[in]Дескриптор, который идентифицирует сокет, который был помещен в состояние прослушивания с функцией прослушивания. Соединение действительно выполняется с помощью сокета, который возвращается приемом.
addr [out]Необязательный указатель на буфер, который принимает адрес соединительного объекта, как известно уровню связи. Точный формат параметра addr определяется семейством адресов, которое было установлено при создании сокета из структуры sockaddr.
addrlen [in, out]Необязательный указатель на целое число, которое содержит длину структуры, на которую указывает параметр addr.

Возвращаемое значение

Если ошибка не возникает, accept возвращает значение типа SOCKET, которое является дескриптором для нового сокета. Это возвращаемое значение является дескриптором для сокета, на котором выполняется фактическое соединение.

В противном случае возвращается значение INVALID_SOCKET, и конкретный код ошибки можно получить, вызвав WSAGetLastError.

Целое число, на которое ссылается addrlen, первоначально содержит объем пространства, на который указывает addr. При возврате он будет содержать фактическую длину в байтах возвращаемого адреса.

Коды ошибок

WSANOTINITIALISEDПеред использованием этой функции должен произойти успешный вызов WSAStartup
WSAECONNRESETПодтверждалось входящее соединение, но затем оно было прекращено удаленным одноранговым узлом до принятия вызова
WSAEFAULTПараметр addrlen слишком мал или addr не является допустимой частью адресного пространства пользователя
WSAEINTRБлокирующий вызов Windows Sockets 1.1 был отменен через WSACancelBlockingCall
WSAEINVALФункция прослушивания не была вызвана до принятия
WSAEINPROGRESSВыполняется блокировка вызовов Windows Sockets 1.1 или поставщик услуг по-прежнему обрабатывает функцию обратного вызова
WSAEMFILEОчередь не пуста при входе, чтобы принять, и нет доступных дескрипторов
WSAENETDOWNСбой сетевой подсистемы
WSAENOBUFSБуферное пространство отсутствует
WSAENOTSOCKДескриптор не является сокетом
WSAEOPNOTSUPPСопряженный сокет не является типом, поддерживающим службу, ориентированную на соединение
WSAEWOULDBLOCKСокет отмечен как неблокирующий, и соединения не принимаются

Замечания

Функция accept извлекает первое соединение в очереди ожидающих соединений в сокете s. Затем он создает и возвращает дескриптор нового сокета. Созданный сокет - это сокет, который будет обрабатывать фактическое соединение; он имеет те же свойства, что и сокет s, включая асинхронные события, зарегистрированные в функциях WSAAsyncSelect или WSAEventSelect.

Функция accept может блокировать вызывающего абонента до тех пор, пока соединение не будет присутствовать, если в очереди нет ожидающих соединений, а сокет помечен как блокирующий. Если сокет отмечен как неблокирующий, а ожидающие подключения не присутствуют в очереди, accept возвращает ошибку, как описано ниже. После успешного завершения accept возвращает новый дескриптор сокета, принятый сокет не может использоваться для приема большего количества подключений. Оригинальный сокет остается открытым и прослушивает новые запросы на подключение.

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

Функция accept используется с типами сокетов, ориентированными на соединение, такими как SOCK_STREAM. Если addr и / или addrlen равны NULL, то информация о удаленном адресе принятого сокета не возвращается.

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

#ifndef UNICODE
#define UNICODE
#endif

#include 
#include 
#include 

// Need to link with Ws2_32.lib
#pragma comment(lib, "Ws2_32.lib")

int wmain(void)
{

    //----------------------
    // Initialize Winsock.
    WSADATA wsaData;
    int iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
    if (iResult != NO_ERROR) {
        wprintf(L"WSAStartup failed with error: %ld\n", iResult);
        return 1;
    }
    //----------------------
    // Create a SOCKET for listening for
    // incoming connection requests.
    SOCKET ListenSocket;
    ListenSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (ListenSocket == INVALID_SOCKET) {
        wprintf(L"socket failed with error: %ld\n", WSAGetLastError());
        WSACleanup();
        return 1;
    }
    //----------------------
    // The sockaddr_in structure specifies the address family,
    // IP address, and port for the socket that is being bound.
    sockaddr_in service;
    service.sin_family = AF_INET;
    service.sin_addr.s_addr = inet_addr("127.0.0.1");
    service.sin_port = htons(27015);

    if (bind(ListenSocket,
             (SOCKADDR *) & service, sizeof (service)) == SOCKET_ERROR) {
        wprintf(L"bind failed with error: %ld\n", WSAGetLastError());
        closesocket(ListenSocket);
        WSACleanup();
        return 1;
    }
    //----------------------
    // Listen for incoming connection requests.
    // on the created socket
    if (listen(ListenSocket, 1) == SOCKET_ERROR) {
        wprintf(L"listen failed with error: %ld\n", WSAGetLastError());
        closesocket(ListenSocket);
        WSACleanup();
        return 1;
    }
    //----------------------
    // Create a SOCKET for accepting incoming requests.
    SOCKET AcceptSocket;
    wprintf(L"Waiting for client to connect...\n");

    //----------------------
    // Accept the connection.
    AcceptSocket = accept(ListenSocket, NULL, NULL);
    if (AcceptSocket == INVALID_SOCKET) {
        wprintf(L"accept failed with error: %ld\n", WSAGetLastError());
        closesocket(ListenSocket);
        WSACleanup();
        return 1;
    } else
        wprintf(L"Client connected.\n");

    // No longer need server socket
    closesocket(ListenSocket);

    WSACleanup();
    return 0;
}

Функция connect()

Функция connect устанавливает соединение с указанным сокетом.

int connect (
  _In_ SOCKET s,
  _In_ const struct sockaddr * имя,
  _In_ int namelen
);

параметры

s [in]Дескриптор, идентифицирующий несвязанный сокет
имя [в]Указатель на структуру sockaddr, к которой должно быть установлено соединение
namelen [in]Длина в байтах структуры sockaddr, на которую указывает параметр имени

Возвращаемое значение

Если ошибка не возникает, соединение возвращает ноль. В противном случае он возвращает SOCKET_ERROR, а конкретный код ошибки можно получить, вызвав WSAGetLastError.

В блокирующем сокете возвращаемое значение указывает на успех или неудачу попытки подключения.

С неблокирующим сокетом попытка подключения не может быть выполнена немедленно. В этом случае соединение вернет SOCKET_ERROR, а WSAGetLastError вернет WSAEWOULDBLOCK. В этом случае существует три возможных сценария:

Пока попытка соединения не завершится в неблокирующем сокете, все последующие вызовы для подключения в одном и том же сокете с кодом ошибки WSAEALREADY и WSAEISCONN завершится успешно, когда соединение завершится успешно. Из-за неоднозначностей в версии 1.1 спецификации Windows Sockets коды ошибок, возвращаемые из соединения, пока соединение уже ожидается, могут различаться в разных реализациях. В результате не рекомендуется, чтобы приложения использовали несколько вызовов для подключения для обнаружения завершения соединения. Если это так, они должны быть готовы обрабатывать значения ошибок WSAEINVAL и WSAEWOULDBLOCK так же, как они обрабатывают WSAEALREADY, чтобы обеспечить надежную работу.

Если возвращенный код ошибки указывает на неудачную попытку подключения (то есть WSAECONNREFUSED, WSAENETUNREACH, WSAETIMEDOUT), приложение может снова вызвать соединение для одного и того же сокета.

Коды ошибок

WSANOTINITIALISEDПеред использованием этой функции должен произойти успешный вызов WSAStartup.
WSAENETDOWNСбой сетевой подсистемы.
WSAEADDRINUSEЛокальный адрес сокета уже используется, и сокет не был помечен для повторного использования адреса с SO_REUSEADDR. Эта ошибка обычно возникает при выполнении привязки, но может быть отложена до функции подключения, если привязка была связана с подстановочным адресом (INADDR_ANY или in6addr_any) для локального IP-адреса. Конкретный адрес должен быть неявно связан функцией подключения.
WSAEINTRБлокирующий вызов Windows Socket 1.1 был отменен через WSACancelBlockingCall.
WSAEINPROGRESSВыполняется блокировка вызовов Windows Sockets 1.1 или поставщик услуг по-прежнему обрабатывает функцию обратного вызова.
WSAEALREADYВ указанном сокете выполняется неблокирующий вызов соединения. Примечание. Чтобы сохранить обратную совместимость, эта ошибка сообщается как WSAEINVAL для приложений Windows Sockets 1.1, которые ссылаются на Winsock.dll или Wsock32.dll.
WSAEADDRNOTAVAILУдаленный адрес не является допустимым адресом (например, INADDR_ANY или in6addr_any).
WSAEAFNOSUPPORTАдреса в указанном семействе не могут использоваться с этим сокетом.
WSAECONNREFUSEDПопытка подключения была решительно отвергнута.
WSAEFAULTСтруктура sockaddr, на которую указывает имя, содержит неправильный формат адреса для связанного семейства адресов или параметр namelen слишком мал. Эта ошибка также возвращается, если структура sockaddr, на которую указывает параметр имени с длиной, указанной в параметре namelen, не находится в допустимой части адресного пространства пользователя.
WSAEINVALПараметр s является сокетом для прослушивания.
WSAEISCONNСокет уже подключен (только для ориентированных на соединение гнезд).
WSAENETUNREACHВ настоящий момент сеть не может быть достигнута с этого хоста.
WSAEHOSTUNREACHОперация сокета была предпринята для недоступного хоста.
WSAENOBUFSПримечание. Буферное пространство отсутствует. Сокет нельзя подключить.
WSAENOTSOCKДескриптор, указанный в параметре s, не является сокетом.
WSAETIMEDOUTПопытка подключения отключена без установления соединения.
WSAEWOULDBLOCKСокет отмечен как неблокирующий, и соединение не может быть выполнено немедленно.
WSAEACCESПопытка подключения сокета датаграммы к широковещательному адресу завершилась неудачно, поскольку опция setockopt SO_BROADCAST не включена.

Замечания

Функция connect используется для создания соединения с указанным пунктом назначения. Если socket s, unbound, уникальные значения назначаются локальной ассоциации системой, а сокет помечен как связанный.

Для сокетов, ориентированных на соединение (например, введите SOCK_STREAM), активное соединение инициируется на чужом хосте, используя имя (адрес в пространстве имен сокета, для подробного описания см. Bind и sockaddr).
Примечание. Если сокет открыт, выполняется вызов setsockopt, а затем выполняется вызов sendto, Windows Sockets выполняет вызов неявной функции функции.

Когда вызов сокета завершается успешно, сокет готов к отправке и приему данных. Если член адреса структуры, заданный параметром name, заполняется нулями, соединение вернет ошибку WSAEADDRNOTAVAIL. Любая попытка повторного подключения активного соединения завершится с кодом ошибки WSAEISCONN.

Для неконтактных сокетов, ориентированных на соединение, часто невозможно сразу же установить соединение. В этом случае эта функция возвращает ошибку WSAEWOULDBLOCK. Однако операция продолжается.

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

Для сокета без установления соединения (например, введите SOCK_DGRAM) операция, выполняемая соединением, - это просто указать целевой адрес назначения по умолчанию, который можно использовать при последующих вызовах send / WSASend и recv / WSARecv. Любые датаграммы, полученные с адреса, отличного от указанного адреса назначения, будут отброшены. Если член адреса структуры, заданный по имени, заполняется нулями, сокет будет отключен. Затем удаленный адрес по умолчанию будет неопределенным, поэтому вызовы send / WSASend и recv / WSARecv вернут код ошибки WSAENOTCONN. Однако sendto / WSASendTo и recvfrom / WSARecvFrom все еще можно использовать. Назначение по умолчанию можно изменить, просто вызвав соединение снова, даже если сокет уже подключен. Любые датаграммы, поставленные в очередь для получения, отбрасываются, если имя отличается от предыдущего подключения.

Для сокетов без установления соединения имя может указывать любой действительный адрес, включая широковещательный адрес. Однако для подключения к широковещательному адресу сокет должен использовать setsockopt для включения опции SO_BROADCAST. В противном случае соединение не будет выполнено с кодом ошибки WSAEACCES.

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

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

#ifndef UNICODE
#define UNICODE
#endif

#define WIN32_LEAN_AND_MEAN

#include 
#include 
#include 

// Need to link with Ws2_32.lib
#pragma comment(lib, "ws2_32.lib")

int wmain()
{
    //----------------------
    // Initialize Winsock
    WSADATA wsaData;
    int iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
    if (iResult != NO_ERROR) {
        wprintf(L"WSAStartup function failed with error: %d\n", iResult);
        return 1;
    }
    //----------------------
    // Create a SOCKET for connecting to server
    SOCKET ConnectSocket;
    ConnectSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (ConnectSocket == INVALID_SOCKET) {
        wprintf(L"socket function failed with error: %ld\n", WSAGetLastError());
        WSACleanup();
        return 1;
    }
    //----------------------
    // The sockaddr_in structure specifies the address family,
    // IP address, and port of the server to be connected to.
    sockaddr_in clientService;
    clientService.sin_family = AF_INET;
    clientService.sin_addr.s_addr = inet_addr("127.0.0.1");
    clientService.sin_port = htons(27015);

    //----------------------
    // Connect to server.
    iResult = connect(ConnectSocket, (SOCKADDR *) & clientService, sizeof (clientService));
    if (iResult == SOCKET_ERROR) {
        wprintf(L"connect function failed with error: %ld\n", WSAGetLastError());
        iResult = closesocket(ConnectSocket);
        if (iResult == SOCKET_ERROR)
            wprintf(L"closesocket function failed with error: %ld\n", WSAGetLastError());
        WSACleanup();
        return 1;
    }

    wprintf(L"Connected to server.\n");

    iResult = closesocket(ConnectSocket);
    if (iResult == SOCKET_ERROR) {
        wprintf(L"closesocket function failed with error: %ld\n", WSAGetLastError());
        WSACleanup();
        return 1;
    }

    WSACleanup();
    return 0;
}

Примечания для гнезд IrDA

IrDA реализует функцию соединения с адресами формы sockaddr_irda. Как правило, клиентское приложение создает сокет с функцией сокета, сканирует ближайшую окрестность устройств IrDA с параметром сокета IRLMP_ENUMDEVICES, выбирает устройство из возвращенного списка, формирует адрес, а затем вызывает соединение. Нет никакой разницы между блокировкой и неблокирующей семантикой.

Функция bind()

Функция bind связывает локальный адрес с сокетом.
int bind (
  _In_ SOCKET s,
  _In_ const struct sockaddr * имя,
  _In_ int namelen
);

Параметры

s [in]Дескриптор, идентифицирующий несвязанный сокет.
имя [в]Указатель на структуру sockaddr локального адреса для назначения связанного сокета.
namelen [in]Длина в байтах значения, на которое указывает параметр имени.

Возвращаемое значение

Если ошибка не возникает, bind возвращает ноль. В противном случае он возвращает SOCKET_ERROR, а конкретный код ошибки можно получить, вызвав WSAGetLastError.

Коды ошибок

WSANOTINITIALISEDПримечание Перед использованием этой функции должен произойти успешный вызов WSAStartup.
WSAENETDOWNСбой сетевой подсистемы.
WSAEACCESБыла предпринята попытка получить доступ к сокету, запрещенным его разрешениями доступа. Эта ошибка возвращается, если nn попытается связать сокет datagram с широковещательным адресом, так как параметр setockopt SO_BROADCAST не включен.
WSAEADDRINUSEОбычно разрешено только одно использование каждого адреса сокета (протокол / сетевой адрес / порт). Эта ошибка возвращается, если процесс на компьютере уже связан с одним и тем же полным адресом, и сокет не был помечен для повторного использования адресов с SO_REUSEADDR. Например, IP-адрес и порт, указанные в параметре имени, уже привязаны к другому сокету, который используется другим приложением. Для получения дополнительной информации см. Параметр сокета SO_REUSEADDR в справке SOL_SOCKET Socket Options, используя SO_REUSEADDR и SO_EXCLUSIVEADDRUSE, и SO_EXCLUSIVEADDRUSE.
WSAEADDRNOTAVAILЗапрошенный адрес недействителен в его контексте. Эта ошибка возвращается, если указанный адрес, на который указывает параметр имени, не является допустимым локальным IP-адресом на этом компьютере.
WSAEFAULTСистема обнаружила недопустимый адрес указателя при попытке использовать аргумент указателя в вызове. Эта ошибка возвращается, если параметр имени равен NULL, имя или параметр namelen не являются допустимой частью адресного пространства пользователя, параметр namelen слишком мал, параметр имени содержит неправильный формат адреса для соответствующего семейства адресов или первые два байта блока памяти, указанные по имени, не соответствуют семейству адресов, связанному с дескриптором сокета.
WSAEINPROGRESSВыполняется блокировка вызовов Windows Sockets 1.1 или поставщик услуг по-прежнему обрабатывает функцию обратного вызова.
WSAEINVALБыл указан недопустимый аргумент. Эта ошибка возвращается сокету s уже привязана к адресу.
WSAENOBUFSОперация сокета не может быть выполнена, потому что в системе недостаточно места для буфера или потому, что очередь заполнена. Эта ошибка возвращается из-за недостаточного количества буферов или слишком много соединений.
WSAENOTSOCKБыла предпринята операция над тем, что не является сокетом. Эта ошибка возвращается, если дескриптор в параметре s не является сокетом.

Замечания

Функция привязки требуется для несвязанного сокета перед последующими вызовами функции прослушивания. Он обычно используется для привязки к сокетам, ориентированным на соединение (поток) или без установления соединения (датаграммы). Функция связывания также может использоваться для привязки к сырым сокетам (сокет был создан путем вызова функции сокета с параметром типа, установленным на SOCK_RAW). Функция связывания также может использоваться в несвязанном сокете перед последующими вызовами функций connect, ConnectEx, WSAConnect, WSAConnectByList или WSAConnectByName перед отправкой.

Когда сокет создается с вызовом функции сокета, он существует в пространстве имен (семейство адресов), но у него нет имени, назначенного ему. Используйте функцию bind, чтобы установить локальную ассоциацию сокета, назначив локальное имя неназванному сокету.

Имя пользователя состоит из трех частей при использовании семейства интернет-адресов:

В Windows Sockets 2 параметр имени строго не интерпретируется как указатель на структуру sockaddr. Он применяется таким образом для совместимости с Windows Sockets 1.1. Поставщики услуг могут расценивать это как указатель на блок памяти размера namelen. Первые 2 байта в этом блоке (соответствующие члену sa_family структуры sockaddr, член sin_family структуры sockaddr_in или член sin6_family структуры sockaddr_in6) должны содержать семейство адресов, которое использовалось для создания сокета. В противном случае возникает ошибка WSAEFAULT.

Если приложение не заботится о том, какой локальный адрес назначен, укажите постоянное значение INADDR_ANY для локального адреса IPv4 или постоянное значение in6addr_any для локального адреса IPv6 в элементе sa_data параметра name. Это позволяет базовому поставщику услуг использовать любой подходящий сетевой адрес, потенциально упрощая прикладное программирование в присутствии многосетевых хостов (то есть хостов, которые имеют более одного сетевого интерфейса и адреса).

Для TCP / IP, если порт указан как ноль, поставщик услуг назначает уникальный порт для приложения из диапазона динамического клиентского порта. В Windows Vista и более поздних версиях диапазон динамического клиентского порта - это значение между 49152 и 65535. Это изменение с Windows Server 2003 и ранее, когда диапазон динамического клиентского порта был равен 1025 и 5000. Максимальное значение для динамического клиента диапазон портов можно изменить, установив значение в следующем разделе реестра:

HKLM \ SYSTEM \ CurrentControlSet \ Services \ Tcpip \ Parameters

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

В Windows Vista и более поздних версиях динамический диапазон клиентских портов можно просматривать и изменять с помощью команд netsh. Диапазон динамического клиентского порта может быть установлен по-разному для UDP и TCP, а также для IPv4 и IPv6. Для получения дополнительной информации см. KB 929851.

Приложение может использовать getsockname после вызова bind, чтобы узнать адрес и порт, который был назначен для сокета. Если интернет-адрес равен INADDR_ANY или in6addr_any, имя getsockname не обязательно может указывать адрес до тех пор, пока сокет не будет подключен, поскольку несколько адресов могут быть действительными, если хост является многосетевым. Связывание с определенным номером порта, отличным от порта 0, не рекомендуется для клиентских приложений, так как существует опасность конфликта с другим сокетом, уже использующим этот номер порта на локальном компьютере.
Примечание. При использовании привязки с опцией SO_EXCLUSIVEADDRUSE или SO_REUSEADDR параметр сокета должен быть установлен перед выполнением привязки, чтобы иметь какое-либо влияние. Для получения дополнительной информации см. SO_EXCLUSIVEADDRUSE и использование SO_REUSEADDR и SO_EXCLUSIVEADDRUSE.

Для многоадресных операций предпочтительным методом является вызов функции связывания для связывания сокета с локальным IP-адресом, а затем присоединение к группе многоадресной рассылки. Хотя этот порядок операций не является обязательным, настоятельно рекомендуется. Таким образом, приложение многоадресной рассылки сначала будет выбирать IPv4 или IPv6-адрес на локальном компьютере, адрес IPv4 подстановки (INADDR_ANY) или адрес IPv6 подстановочного знака (in6addr_any). Затем приложение многоадресной рассылки вызовет функцию связывания с этим адресом в члене sa_data параметра name, чтобы связать локальный IP-адрес с сокетом. Если был задан адрес подстановочного знака, тогда Windows будет выбирать локальный IP-адрес для использования. После завершения функции связывания приложение будет присоединяться к группе многоадресной рассылки. Подробнее о том, как присоединиться к группе многоадресной рассылки, см. Раздел «Многоадресное программирование». Этот сокет затем может использоваться для приема групповых пакетов из группы многоадресной рассылки с использованием функций recv, recvfrom, WSARecv, WSARecvEx, WSARecvFrom или WSARecvMsg.

функция bind обычно не требуется для операций отправки в группу многоадресной рассылки. Функции sendto, WSASendMsg и WSASendTo неявно связывают сокет с подстановочным адресом, если сокет еще не связан. Функция привязки требуется перед использованием функций send или WSASend, которые не выполняют неявное связывание и разрешены только на подключенных сокетах, что означает, что сокет должен быть уже привязан к нему. Функция связывания может использоваться перед отправкой с использованием функций sendto, WSASendMsg или WSASendTo, если приложение хочет выбрать конкретный локальный IP-адрес на локальном компьютере с несколькими сетевыми интерфейсами и локальными IP-адресами. В противном случае неявное связывание с подстановочным адресом с использованием функций sendto, WSASendMsg или WSASendTo может привести к другому локальному IP-адресу, используемому для операций отправки. Примечание. При выпуске блокирующего вызова Winsock, такого как bind, Winsock, возможно, потребуется дождаться сетевого события до завершения вызова. Winsock выполняет аварийное ожидание в этой ситуации, которое может быть прервано асинхронным вызовом процедуры (APC), запланированным в том же потоке. Выпуск другого блокирующего вызова Winsock внутри APC, который прервал текущий блокирующий вызов Winsock в том же потоке, приведет к неопределенному поведению и никогда не должен быть предпринят клиентами Winsock.

Примечания для сокетов IrDA

#ifndef UNICODE
#define UNICODE
#endif

#define WIN32_LEAN_AND_MEAN

#include <winsock2.h>
#include <Ws2tcpip.h>
#include <stdio.h>

// Link with ws2_32.lib
#pragma comment(lib, "Ws2_32.lib")

int main()
{

    // Declare some variables
    WSADATA wsaData;

    int iResult = 0;            // used to return function results

    // the listening socket to be created
    SOCKET ListenSocket = INVALID_SOCKET;

    // The socket address to be passed to bind
    sockaddr_in service;

    //----------------------
    // Initialize Winsock
    iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
    if (iResult != NO_ERROR) {
        wprintf(L"Error at WSAStartup()\n");
        return 1;
    }
    //----------------------
    // Create a SOCKET for listening for 
    // incoming connection requests
    ListenSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (ListenSocket == INVALID_SOCKET) {
        wprintf(L"socket function failed with error: %u\n", WSAGetLastError());
        WSACleanup();
        return 1;
    }
    //----------------------
    // The sockaddr_in structure specifies the address family,
    // IP address, and port for the socket that is being bound.
    service.sin_family = AF_INET;
    service.sin_addr.s_addr = inet_addr("127.0.0.1");
    service.sin_port = htons(27015);

    //----------------------
    // Bind the socket.
    iResult = bind(ListenSocket, (SOCKADDR *) &service, sizeof (service));
    if (iResult == SOCKET_ERROR) {
        wprintf(L"bind failed with error %u\n", WSAGetLastError());
        closesocket(ListenSocket);
        WSACleanup();
        return 1;
    }
    else
        wprintf(L"bind returned success\n");

    WSACleanup();
    return 0;
}

Функция listen()

Функция listen() помещает сокет в состояние, в котором он прослушивает входящее соединение.
int listen (
  _In_ SOCKET s,
  _In_ int backlog
);

Параметры

s [in]Дескриптор, идентифицирующий связанный несвязанный сокет.
backlog [in]Максимальная длина очереди ожидающих соединений. Если установлено значение SOMAXCONN, базовый поставщик услуг, ответственный за сокет s, установит отставание на максимально допустимое значение. Если установлено значение SOMAXCONN_HINT (N) (где N - число), значение отставания будет равно N, скорректированному в пределах диапазона (200, 65535). Обратите внимание, что SOMAXCONN_HINT может использоваться для установки отставания на большее значение, чем это возможно при использовании SOMAXCONN. SOMAXCONN_HINT поддерживается только поставщиком услуг Microsoft TCP / IP. Стандартного положения для получения фактического значения отставания нет.

Возвращаемое значение

Если ошибка не возникает, прослушивание возвращает ноль. В противном случае возвращается значение SOCKET_ERROR, и конкретный код ошибки можно получить, вызвав WSAGetLastError.

Коды ошибок

WSANOTINITIALISEDПеред использованием этой функции должен произойти успешный вызов WSAStartup.
WSAENETDOWNСбой сетевой подсистемы.
WSAEADDRINUSEЛокальный адрес сокета уже используется, и сокет не был помечен для повторного использования адреса с SO_REUSEADDR. Эта ошибка обычно возникает во время выполнения функции привязки, но может быть отложена до этой функции, если привязка была связана с частично подстановочным адресом (с использованием ADDR_ANY) и если конкретный адрес должен быть зафиксирован во время этой функции.
WSAEINPROGRESSВыполняется блокировка вызовов Windows Sockets 1.1 или поставщик услуг по-прежнему обрабатывает функцию обратного вызова.
WSAEINVALСокет не связан с привязкой.
WSAEISCONNСокет уже подключен.
WSAEMFILEБольше дескрипторов сокетов нет.
WSAENOBUFSБуферное пространство отсутствует.
WSAENOTSOCKДескриптор не является сокетом.
WSAEOPNOTSUPPСсылочный сокет не относится к типу, поддерживающему операцию прослушивания.

Замечания

Чтобы принимать соединения, сокет сначала создается с помощью функции сокета и привязывается к локальному адресу с функцией связывания. Задержка для входящих подключений задается с помощью прослушивания, а затем соединения принимаются с помощью функции accept. Сокеты, ориентированные на соединение, например, типа SOCK_STREAM, используются для прослушивания. Сокет s помещается в пассивный режим, когда запросы на входящий соединение подтверждаются и ставятся в очередь до принятия процесса.

Значение для отставания SOMAXCONN - это специальная константа, которая инструктирует основного поставщика услуг, ответственного за сокет s, установить длину очереди ожидающих соединений на максимально допустимое значение.

В Windows Sockets 2 это максимальное значение по умолчанию имеет большое значение (обычно несколько сотен или больше). При вызове функции прослушивания в приложении Bluetooth настоятельно рекомендуется использовать меньшее значение для параметра backlog (обычно от 2 до 4), поскольку принимаются только несколько клиентских подключений. Это уменьшает системные ресурсы, которые выделяются для использования в сокете для прослушивания. Эта же рекомендация применяется к другим сетевым приложениям, которые ожидают только нескольких клиентских подключений.

Функция прослушивания обычно используется серверами, которые могут иметь более одного запроса соединения за раз. Если приходит запрос на соединение и очередь заполнена, клиент получит сообщение об ошибке WSAECONNREFUSED.

Если нет доступных дескрипторов сокетов, попытка прослушивания продолжает функционировать. Если дескрипторы становятся доступными, более поздний вызов для прослушивания или принятия будет пополнять очередь на текущее или последнее значение, указанное для параметра backlog, если это возможно, и возобновить прослушивание входящих соединений.

Если функция прослушивания вызывается в уже прослушиваемом сокете, она вернет успех без изменения значения параметра backlog. Установка параметра backlog в 0 при последующем вызове для прослушивания в гнезде для прослушивания не считается надлежащим сбросом, особенно если есть соединения в сокете. Примечание. При выпуске блокирующего вызова Winsock, такого как прослушивание, Winsock, возможно, потребуется дождаться сетевого события до завершения вызова. Winsock выполняет аварийное ожидание в этой ситуации, которое может быть прервано асинхронным вызовом процедуры (APC), запланированным в том же потоке. Выпуск другого блокирующего вызова Winsock внутри APC, который прервал текущий блокирующий вызов Winsock в том же потоке, приведет к неопределенному поведению и никогда не должен быть предпринят клиентами Winsock.

#ifndef UNICODE
#define UNICODE
#endif

#define WIN32_LEAN_AND_MEAN

#include 
#include 
#include 

// Need to link with Ws2_32.lib
#pragma comment(lib, "ws2_32.lib")

int wmain()
{

    //----------------------
    // Initialize Winsock
    WSADATA wsaData;
    int iResult = 0;

    SOCKET ListenSocket = INVALID_SOCKET;
    sockaddr_in service;

    iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
    if (iResult != NO_ERROR) {
        wprintf(L"WSAStartup() failed with error: %d\n", iResult);
        return 1;
    }
    //----------------------
    // Create a SOCKET for listening for incoming connection requests.
    ListenSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (ListenSocket == INVALID_SOCKET) {
        wprintf(L"socket function failed with error: %ld\n", WSAGetLastError());
        WSACleanup();
        return 1;
    }
    //----------------------
    // The sockaddr_in structure specifies the address family,
    // IP address, and port for the socket that is being bound.
    service.sin_family = AF_INET;
    service.sin_addr.s_addr = inet_addr("127.0.0.1");
    service.sin_port = htons(27015);

    iResult = bind(ListenSocket, (SOCKADDR *) & service, sizeof (service));
    if (iResult == SOCKET_ERROR) {
        wprintf(L"bind function failed with error %d\n", WSAGetLastError());
        iResult = closesocket(ListenSocket);
        if (iResult == SOCKET_ERROR)
            wprintf(L"closesocket function failed with error %d\n", WSAGetLastError());
        WSACleanup();
        return 1;
    }
    //----------------------
    // Listen for incoming connection requests 
    // on the created socket
    if (listen(ListenSocket, SOMAXCONN) == SOCKET_ERROR)
        wprintf(L"listen function failed with error: %d\n", WSAGetLastError());

    wprintf(L"Listening on socket...\n");

    iResult = closesocket(ListenSocket);
    if (iResult == SOCKET_ERROR) {
        wprintf(L"closesocket function failed with error %d\n", WSAGetLastError());
        WSACleanup();
        return 1;
    }

    WSACleanup();
    return 0;
}

Структура addrinfo

Структура addrinfo используется функцией getaddrinfo для хранения информации адреса хоста.

typedef struct addrinfo {
  int ai_flags;
  int ai_family;
  int ai_socktype;
  int ai_protocol;
  size_t ai_addrlen;
  char * ai_canonname;
  struct sockaddr * ai_addr;
  struct addrinfo * ai_next;
} ADDRINFOA, * PADDRINFOA;

Примеры

В следующем примере кода показано использование структуры addrinfo.

#undef UNICODE

#include <winsock2.h>
#include <ws2tcpip.h>
#include <stdio.h>

// link with Ws2_32.lib
#pragma comment(lib, "Ws2_32.lib")

int __cdecl main(int argc, char **argv)
{

    //-----------------------------------------
    // Declare and initialize variables
    WSADATA wsaData;
    int iResult;
    INT iRetval;

    DWORD dwRetval;

    int i = 1;
    
    struct addrinfo *result = NULL;
    struct addrinfo *ptr = NULL;
    struct addrinfo hints;

    struct sockaddr_in  *sockaddr_ipv4;
//    struct sockaddr_in6 *sockaddr_ipv6;
    LPSOCKADDR sockaddr_ip;

    char ipstringbuffer[46];
    DWORD ipbufferlength = 46;

    // Validate the parameters
    if (argc != 3) {
        printf("usage: %s  \n", argv[0]);
        printf("       provides protocol-independent translation\n");
        printf("       from an ANSI host name to an IP address\n");
        printf("%s example usage\n", argv[0]);
        printf("   %s www.contoso.com 0\n", argv[0]);
        return 1;
    }

    // Initialize Winsock
    iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
    if (iResult != 0) {
        printf("WSAStartup failed: %d\n", iResult);
        return 1;
    }

    //--------------------------------
    // Setup the hints address info structure
    // which is passed to the getaddrinfo() function
    ZeroMemory( &hints, sizeof(hints) );
    hints.ai_family = AF_UNSPEC;
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_protocol = IPPROTO_TCP;

    printf("Calling getaddrinfo with following parameters:\n");
    printf("\tnodename = %s\n", argv[1]);
    printf("\tservname (or port) = %s\n\n", argv[2]);
    
//--------------------------------
// Call getaddrinfo(). If the call succeeds,
// the result variable will hold a linked list
// of addrinfo structures containing response
// information
    dwRetval = getaddrinfo(argv[1], argv[2], &hints, &result);
    if ( dwRetval != 0 ) {
        printf("getaddrinfo failed with error: %d\n", dwRetval);
        WSACleanup();
        return 1;
    }

    printf("getaddrinfo returned success\n");
    
    // Retrieve each address and print out the hex bytes
    for(ptr=result; ptr != NULL ;ptr=ptr->ai_next) {

        printf("getaddrinfo response %d\n", i++);
        printf("\tFlags: 0x%x\n", ptr->ai_flags);
        printf("\tFamily: ");
        switch (ptr->ai_family) {
            case AF_UNSPEC:
                printf("Unspecified\n");
                break;
            case AF_INET:
                printf("AF_INET (IPv4)\n");
                sockaddr_ipv4 = (struct sockaddr_in *) ptr->ai_addr;
                printf("\tIPv4 address %s\n",
                    inet_ntoa(sockaddr_ipv4->sin_addr) );
                break;
            case AF_INET6:
                printf("AF_INET6 (IPv6)\n");
                // the InetNtop function is available on Windows Vista and later
                // sockaddr_ipv6 = (struct sockaddr_in6 *) ptr->ai_addr;
                // printf("\tIPv6 address %s\n",
                //    InetNtop(AF_INET6, &sockaddr_ipv6->sin6_addr, ipstringbuffer, 46) );
                
                // We use WSAAddressToString since it is supported on Windows XP and later
                sockaddr_ip = (LPSOCKADDR) ptr->ai_addr;
                // The buffer length is changed by each call to WSAAddresstoString
                // So we need to set it for each iteration through the loop for safety
                ipbufferlength = 46;
                iRetval = WSAAddressToString(sockaddr_ip, (DWORD) ptr->ai_addrlen, NULL, 
                    ipstringbuffer, &ipbufferlength );
                if (iRetval)
                    printf("WSAAddressToString failed with %u\n", WSAGetLastError() );
                else    
                    printf("\tIPv6 address %s\n", ipstringbuffer);
                break;
            case AF_NETBIOS:
                printf("AF_NETBIOS (NetBIOS)\n");
                break;
            default:
                printf("Other %ld\n", ptr->ai_family);
                break;
        }
        printf("\tSocket type: ");
        switch (ptr->ai_socktype) {
            case 0:
                printf("Unspecified\n");
                break;
            case SOCK_STREAM:
                printf("SOCK_STREAM (stream)\n");
                break;
            case SOCK_DGRAM:
                printf("SOCK_DGRAM (datagram) \n");
                break;
            case SOCK_RAW:
                printf("SOCK_RAW (raw) \n");
                break;
            case SOCK_RDM:
                printf("SOCK_RDM (reliable message datagram)\n");
                break;
            case SOCK_SEQPACKET:
                printf("SOCK_SEQPACKET (pseudo-stream packet)\n");
                break;
            default:
                printf("Other %ld\n", ptr->ai_socktype);
                break;
        }
        printf("\tProtocol: ");
        switch (ptr->ai_protocol) {
            case 0:
                printf("Unspecified\n");
                break;
            case IPPROTO_TCP:
                printf("IPPROTO_TCP (TCP)\n");
                break;
            case IPPROTO_UDP:
                printf("IPPROTO_UDP (UDP) \n");
                break;
            default:
                printf("Other %ld\n", ptr->ai_protocol);
                break;
        }
        printf("\tLength of this sockaddr: %d\n", ptr->ai_addrlen);
        printf("\tCanonical name: %s\n", ptr->ai_canonname);
    }

    freeaddrinfo(result);
    WSACleanup();

    return 0;
}

Структура addrinfoW

Структура addrinfoW используется функцией GetAddrInfoW для хранения информации об адресе хоста.

typedef struct addrinfoW {
   int ai_flags;
   int ai_family;
   int ai_socktype;
   int ai_protocol;
   size_t ai_addrlen;
   PWSTR ai_canonname;
   struct sockaddr * ai_addr;
   struct addrinfoW * ai_next;
} ADDRINFOW, * PADDRINFOW;

Пример

В следующем примере кода показано, как использовать структуру addrinfoW.

#ifndef UNICODE
#define UNICODE
#endif

#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif

#include <windows.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#include <stdio.h>

#pragma comment(lib, "Ws2_32.lib")

int __cdecl wmain(int argc, wchar_t ** argv)
{
//--------------------------------
// Declare and initialize variables.
    WSADATA wsaData;
    int iResult;

    ADDRINFOW *result = NULL;
    ADDRINFOW *ptr = NULL;
    ADDRINFOW hints;

    DWORD dwRetval = 0;
    int i = 1;

    struct sockaddr_in *sockaddr_ipv4;
    struct sockaddr_in6 *sockaddr_ipv6;
//    LPSOCKADDR sockaddr_ip;

    wchar_t ipstringbuffer[46];

    // Validate the parameters
    if (argc != 3) {
        wprintf(L"usage: %ws  \n", argv[0]);
        wprintf(L"       provides protocol-independent translation\n");
        wprintf(L"       from a host name to an IP address\n");
        wprintf(L"%ws example usage\n", argv[0]);
        wprintf(L"   %ws www.contoso.com 0\n", argv[0]);
        return 1;
    }
    // Initialize Winsock
    iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
    if (iResult != 0) {
        wprintf(L"WSAStartup failed: %d\n", iResult);
        return 1;
    }
//--------------------------------
// Setup the hints address info structure
// which is passed to the GetAddrInfoW() function
    memset(&hints, 0, sizeof (hints));
    hints.ai_family = AF_UNSPEC;
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_protocol = IPPROTO_TCP;

    wprintf(L"Calling GetAddrInfoW with following parameters:\n");
    wprintf(L"\tName = %ws\n", argv[1]);
    wprintf(L"\tServiceName (or port) = %ws\n\n", argv[2]);

//--------------------------------
// Call GetAddrInfoW(). If the call succeeds,
// the aiList variable will hold a linked list
// of addrinfo structures containing response
// information about the host
    dwRetval = GetAddrInfoW(argv[1], argv[2], &hints, &result);

    if (dwRetval != 0) {
        wprintf(L"GetAddrInfoW failed with error: %d\n", dwRetval);
        WSACleanup();
        return 1;
    }
    wprintf(L"GetAddrInfoW returned success\n");

    // Retrieve each address and print out the hex bytes
    for (ptr = result; ptr != NULL; ptr = ptr->ai_next) {

        wprintf(L"GetAddrInfoW response %d\n", i++);
        wprintf(L"\tFlags: 0x%x\n", ptr->ai_flags);
        wprintf(L"\tFamily: ");
        switch (ptr->ai_family) {
        case AF_UNSPEC:
            wprintf(L"Unspecified\n");
            break;
        case AF_INET:
            wprintf(L"AF_INET (IPv4)\n");
            // the InetNtop function is available on Windows Vista and later
            sockaddr_ipv4 = (struct sockaddr_in *) ptr->ai_addr;
            wprintf(L"\tIPv4 address %ws\n",
                    InetNtop(AF_INET, &sockaddr_ipv4->sin_addr, ipstringbuffer,
                             46));

            // We could also use the WSAAddressToString function
            // sockaddr_ip = (LPSOCKADDR) ptr->ai_addr;
            // The buffer length is changed by each call to WSAAddresstoString
            // So we need to set it for each iteration through the loop for safety
            // ipbufferlength = 46;
            // iRetval = WSAAddressToString(sockaddr_ip, (DWORD) ptr->ai_addrlen, NULL, 
            //    ipstringbuffer, &ipbufferlength );
            // if (iRetval)
            //    wprintf(L"WSAAddressToString failed with %u\n", WSAGetLastError() );
            // else    
            //    wprintf(L"\tIPv4 address %ws\n", ipstringbuffer);
            break;
        case AF_INET6:
            wprintf(L"AF_INET6 (IPv6)\n");
            // the InetNtop function is available on Windows Vista and later
            sockaddr_ipv6 = (struct sockaddr_in6 *) ptr->ai_addr;
            wprintf(L"\tIPv6 address %ws\n",
                    InetNtop(AF_INET6, &sockaddr_ipv6->sin6_addr,
                             ipstringbuffer, 46));

            // We could also use WSAAddressToString which also returns the scope ID
            // sockaddr_ip = (LPSOCKADDR) ptr->ai_addr;
            // The buffer length is changed by each call to WSAAddresstoString
            // So we need to set it for each iteration through the loop for safety
            // ipbufferlength = 46;
            //iRetval = WSAAddressToString(sockaddr_ip, (DWORD) ptr->ai_addrlen, NULL, 
            //    ipstringbuffer, &ipbufferlength );
            //if (iRetval)
            //    wprintf(L"WSAAddressToString failed with %u\n", WSAGetLastError() );
            //else    
            //    wprintf(L"\tIPv6 address %ws\n", ipstringbuffer);
            break;
        default:
            wprintf(L"Other %ld\n", ptr->ai_family);
            break;
        }
        wprintf(L"\tSocket type: ");
        switch (ptr->ai_socktype) {
        case 0:
            wprintf(L"Unspecified\n");
            break;
        case SOCK_STREAM:
            wprintf(L"SOCK_STREAM (stream)\n");
            break;
        case SOCK_DGRAM:
            wprintf(L"SOCK_DGRAM (datagram) \n");
            break;
        case SOCK_RAW:
            wprintf(L"SOCK_RAW (raw) \n");
            break;
        case SOCK_RDM:
            wprintf(L"SOCK_RDM (reliable message datagram)\n");
            break;
        case SOCK_SEQPACKET:
            wprintf(L"SOCK_SEQPACKET (pseudo-stream packet)\n");
            break;
        default:
            wprintf(L"Other %ld\n", ptr->ai_socktype);
            break;
        }
        wprintf(L"\tProtocol: ");
        switch (ptr->ai_protocol) {
        case 0:
            wprintf(L"Unspecified\n");
            break;
        case IPPROTO_TCP:
            wprintf(L"IPPROTO_TCP (TCP)\n");
            break;
        case IPPROTO_UDP:
            wprintf(L"IPPROTO_UDP (UDP) \n");
            break;
        default:
            wprintf(L"Other %ld\n", ptr->ai_protocol);
            break;
        }
        wprintf(L"\tLength of this sockaddr: %d\n", ptr->ai_addrlen);
        wprintf(L"\tCanonical name: %s\n", ptr->ai_canonname);
    }

    FreeAddrInfo(result);
    WSACleanup();

    return 0;
}

Структура sockaddr

Структура sockaddr изменяется в зависимости от выбранного протокола. За исключением параметра sin * _family, содержимое sockaddr выражается в сетевом порядке байтов.

Функции Winsock, использующие sockaddr, строго не интерпретируются как указатели на структуру sockaddr. Структура интерпретируется по-разному в контексте различных семейств адресов. Единственным требованием является то, что первым u_short является семейство адресов, а общий размер буфера памяти в байтах - это namelen.

Структура SOCKADDR_STORAGE также сохраняет информацию о адресе сокета, и структура достаточно велика для хранения информации адреса IPv4 или IPv6. Использование структуры SOCKADDR_STORAGE способствует независимости семейства протоколов и протоколов и упрощает разработку. Рекомендуется использовать структуру SOCKADDR_STORAGE вместо структуры sockaddr. Структура SOCKADDR_STORAGE поддерживается в Windows Server 2003 и более поздних версиях.

Структура sockaddr и структуры sockaddr_in ниже используются с IPv4. Другие протоколы используют аналогичные структуры.

struct sockaddr {
        ushort sa_family;
        char sa_data [14];
};

struct sockaddr_in {
        короткий sin_family;
        u_short sin_port;
        struct in_addr sin_addr;
        char sin_zero [8];
};

Структуры sockaddr_in6 и sockaddr_in6_old ниже используются с IPv6.

struct sockaddr_in6 {
        короткий sin6_family;
        u_short sin6_port;
        u_long sin6_flowinfo;
        struct in6_addr sin6_addr;
        u_long sin6_scope_id;
};

typedef struct sockaddr_in6 SOCKADDR_IN6;
typedef struct sockaddr_in6 * PSOCKADDR_IN6;
typedef struct sockaddr_in6 FAR * LPSOCKADDR_IN6;


struct sockaddr_in6_old {
        короткий sin6_family;
        u_short sin6_port;
        u_long sin6_flowinfo;
        struct in6_addr sin6_addr;
};

В выпуске Microsoft Windows Software Development Kit (SDK), выпущенном для Windows Vista и более поздних версий, теги typedef SOCKADDR и SOCKADDR_IN определены для структур sockaddr и sockaddr_in следующим образом:

typedef struct sockaddr {
#if (_WIN32_WINNT <0x0600)
    u_short sa_family;
#else
    ADDRESS_FAMILY sa_family;
#endif // (_ WIN32_WINNT <0x0600)
    CHAR sa_data [14];
} SOCKADDR, * PSOCKADDR, FAR * LPSOCKADDR;


typedef struct sockaddr_in {
#if (_WIN32_WINNT <0x0600)
    короткий sin_family;
#else // (_ WIN32_WINNT <0x0600)
    ADDRESS_FAMILY sin_family;
#endif // (_ WIN32_WINNT <0x0600)
    USHORT sin_port;
    IN_ADDR sin_addr;
    CHAR sin_zero [8];
} SOCKADDR_IN, * PSOCKADDR_IN;

В Windows SDK, выпущенном для Windows Vista и более поздних версий, была изменена организация файлов заголовков, а структуры sockaddr и sockaddr_in определены в заголовочном файле Ws2def.h, а не в заголовочном файле Winsock2.h. Заголовочный файл Ws2def.h автоматически включается в заголовочный файл Winsock2.h. Структура sockaddr_in6 определена в заголовочном файле Ws2ipdef.h, а не в файле заголовка Ws2tcpip.h. Заголовочный файл Ws2ipdef.h автоматически включается в заголовочный файл Ws2tcpip.h. Заголовки Ws2def.h и Ws2ipdef.h никогда не должны использоваться напрямую. Пример кода

Следующий пример демонстрирует использование структуры sockaddr.

// Объявлять переменные
SOCKET ListenSocket;
struct sockaddr_in saServer;
hostent * localHost;
char * localIP;

// Создание прослушивающего сокета
ListenSocket = сокет (AF_INET, SOCK_STREAM, IPPROTO_TCP);

// Получить информацию локального хоста
localHost = gethostbyname ("");
localIP = inet_ntoa (* (struct in_addr *) * localHost-> h_addr_list);

// Настройка структуры sockaddr
saServer.sin_family = AF_INET;
saServer.sin_addr.s_addr = inet_addr (localIP);
saServer.sin_port = htons (5150);

// Привяжите прослушивающий сокет, используя
// информация в структуре sockaddr
bind (ListenSocket, (SOCKADDR *) & saServer, sizeof (saServer));

Структура SOCKADDR_STORAGE

Структура SOCKADDR_STORAGE хранит информацию о адресе сокета. Поскольку структура SOCKADDR_STORAGE достаточно велика для хранения адресной информации для IPv4, IPv6 или других семейств адресов, ее использование способствует независимости семейства протоколов и протокола и упрощает кросс-платформенную разработку. Используйте структуру SOCKADDR_STORAGE вместо структуры sockaddr.

typedef struct sockaddr_storage {
  короткий ss_family;
  char __ss_pad1 [_SS_PAD1SIZE];
  __int64 __ss_align;
  char __ss_pad2 [_SS_PAD2SIZE];
} SOCKADDR_STORAGE, * PSOCKADDR_STORAGE;

1. Создание сервера

Теперь объявление переменную типа SOCKET например s :

Создание socket'а осуществляется следующим системным вызовом

int socket (domain, type, protocol) int domain; int type; int protocol;

Аргумент domain задает используемый для взаимодействия набор протоколов (вид коммуникационной области), для стека протоколов TCP/IP он должен иметь символьное значение AF_INET.

Аргумент type задает режим взаимодействия:

Аргумент protocolзадает конкретный протокол транспортного уровня (из нескольких возможных в стеке протоколов). Если этот аргумент задан равным 0, то будет использован протокол "по умолчанию" (TCP для SOCK_STREAM и UDP для SOCK_DGRAM при использовании комплекта протоколов TCP/IP).

При удачном завершении своей работы данная функция возвращает дескриптор socket'а - целое неотрицательное число, однозначно его идентифицирующее. Дескриптор socket'а аналогичен дескриптору файла ОС UNIX.

При обнаружении ошибки в ходе своей работы функция возвращает число "-1".

Далее мы задаем параметры для сокета (сервера) для этого нам необходимо объявить структуру SOCKADDR_IN sin далее заполняем параметры для сервера:
SOCKADDR_IN sin; sin.sin_family = AF_INET; sin.sin_port = htons(80); sin.sin_addr.s_addr = INADDR_ANY;

Структура SOCKADDR_IN используется несколькими системными вызовами и функциями socket-интерфейса и определена в include-файле in.h следующим образом:

struct SOCKADDR_IN { short sin_family; u_short sin_port; struct in_addr sin_addr; char sin_zero[8]; };

Поле sin_family определяет используемый формат адреса (набор протоколов), в нашем случае (для TCP/IP) оно должно иметь значение AF_INET.

Поле sin_addr содержит адрес (номер) узла сети.

Поле sin_port содержит номер порта на узле сети.

Поле sin_zero не используется.

Определение структуры in_addr (из того же include-файла) таково:

struct in_addr { union { u_long S_addr; /* другие (не интересующие нас) члены объединения */ } S_un; #define s_addr S_un.S_addr };

Структура SOCKADDR_IN должна быть полностью заполнена перед выдачей системного вызова bind. При этом, если поле sin_addr.s_addr имеет значение INADDR_ANY, то системный вызов будет привязывать к socket'у номер (адрес) локального узла сети.

Для подключения socket'а к коммуникационной среде, образованной вычислительной сетью, необходимо выполнить системный вызов bind, определяющий в принятом для сети формате локальный адрес канала связи со средой. В сетях TCP/IP socket связывается с локальным портом. Системный вызов bind имеет следующий синтаксис:

int bind (s, addr, addrlen) int s; struct SOCKADDR_IN *addr; int addrlen;
Пример:

Аргумент s задает дескриптор связываемого socket'а.

Аргумент addr в общем случае должен указывать на структуру данных, содержащую локальный адрес, приписываемый socket'у. Для сетей TCP/IP такой структурой является SOCKADDR_IN.

Аргумент addrlen задает размер (в байтах) структуры данных, указываемой аргументом addr.

В случае успеха bind возвращает 0, в противном случае - "-1".

Для установления связи "клиент-сервер" используются системные вызовы listen и accept (на стороне сервера), а также connect (на стороне клиента). Для заполнения полей структуры socaddr_in, используемой в вызове connect, обычно используется библиотечная функция gethostbyname, транслирующая символическое имя узла сети в его номер (адрес).

Системный вызов listen выражает желание выдавшей его программы-сервера ожидать запросы к ней от программ-клиентов и имеет следующий вид:

int listen (s, n) int s; int n;
Пример:

Аргумент s задает дескриптор socket'а, через который программа будет ожидать запросы к ней от клиентов. Socket должен быть предварительно создан системным вызовом socketи обеспечен адресом с помощью системного вызова bind.

Аргумент n определяет максимальную длину очереди входящих запросов на установление связи. Если какой-либо клиент выдаст запрос на установление связи при полной очереди, то этот запрос будет отвергнут.

Признаком удачного завершения системного вызова listen служит нулевой код возврата.

Перед тем как воспользоваться функцией accept сначала объявите ещё одну переменную типа SOCKET, например s1 .
SOCKADDR_IN from; int fromlen=sizeof(from); s1 = accept(s,(struct sockaddr*)&from, &fromlen);
Это зделано для того что бы узнать IP адрес и порт удаленного компьютера. Что бы вывести на экран IP адрес и порт удаленного компа просто вставить в программу такие строки:

Для приема запросов от программ-клиентов на установление связи в программах-серверах используется системный вызов accept, имеющий следующий вид:

int accept (s, addr, p_addrlen) int s; struct sockaddr_in *addr; int *p_addrlen;

Аргумент s задает дескриптор socket'а, через который программа-сервер получила запрос на соединение (посредством системного запроса listen ).

Аргумент addr должен указывать на область памяти, размер которой позволял бы разместить в ней структуру данных, содержащую адрес socket'а программы-клиента, сделавшей запрос на соединение. Никакой инициализации этой области не требуется.

Аргумент p_addrlen должен указывать на область памяти в виде целого числа, задающего размер (в байтах) области памяти, указываемой аргументом addr.

Системный вызов accept извлекает из очереди, организованной системным вызовом listen, первый запрос на соединение и возвращает дескриптор нового (автоматически созданного) socket'а с теми же свойствами, что и socket, задаваемый аргументом s. Этот новый дескриптор необходимо использовать во всех последующих операциях обмена данными.

Кроме того после удачного завершения accept:

  1. область памяти, указываемая аргументом addr, будет содержать структуру данных (для сетей TCP/IP это sockaddr_in), описывающую адрес socket'а программы-клиента, через который она сделала свой запрос на соединение;
  2. целое число, на которое указывает аргумент p_addrlen, будет равно размеру этой структуры данных.

Если очередь запросов на момент выполнения accept пуста, то программа переходит в состояние ожидания поступления запросов от клиентов на неопределенное время (хотя такое поведение accept можно и изменить).

Признаком неудачного завершения accept служит отрицательное возвращенное значение (дескриптор socket'а отрицательным быть не может).

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

Вот мы соединились с клиентом, но как получить и передать информацию? Это делается с помощью команд send и recv ко не забывайте что вы теперь работаете с переменной s1:
BYTE RecvBuffer[1]; while(recv(s1,RecvBuffer,sizeof(RecvBuffer),0)!=SOCKET_ERROR) { printf("%c",RecvBuffer[0]); send(s1,MsgText,sizeof(MsgText),MSG_DONTROUTE); }
Мы поставили цикл while, потому что он будет выполнятся пока клиент не отключится

Для получения данных от партнера по сетевому взаимодействию используется системный вызов recv, имеющий следующий вид

int recv (s, buf, len, flags) int s; char *buf; int len; int flags;

Аргумент s задает дескриптор socket'а, через который принимаются данные.

Аргумент buf указывает на область памяти, предназначенную для размещения принимаемых данных.

Аргумент len задает длину (в байтах) этой области.

Аргумент flags модифицирует исполнение системного вызова recv. При нулевом значении этого аргумента вызов recv полностью аналогичен системному вызову read.

При успешном завершении recv возвращает количество принятых в область, указанную аргументом buf, байт данных. Если канал данных, определяемый дескриптором s, оказывается "пустым", то recv переводит программу в состояние ожидания до момента появления в нем данных.

Для посылки данных партнеру по сетевому взаимодействию используется системный вызов send, имеющий следующий вид

int send (s, buf, len, flags) int s; char *buf; int len; int flags;

Аргумент s задает дескриптор socket'а, через который посылаются данные.

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

Аргумент len задает длину (в байтах) передаваемых данных.

Аргумент flags модифицирует исполнение системного вызова send. При нулевом значении этого аргумента вызов send полностью аналогичен системному вызову write.

При успешном завершении send возвращает количество переданных из области, указанной аргументом buf, байт данных. Если канал данных, определяемый дескриптором s, оказывается "переполненным", то send переводит программу в состояние ожидания до момента его освобождения.

Для закрытия ранее созданного socket'а используется обычный системный вызов closesocket, применяемый в ОС UNIX для закрытия ранее открытых файлов и имеющий следующий вид

int closesocket(s) int s;

Аргумент s задает дескриптор ранее созданного socket'а.

Однако в режиме с установлением логического соединения (обеспечивающем, как правило, надежную доставку данных) внутрисистемные механизмы обмена будут пытаться передать/принять данные, оставшиеся в канале передачи на момент закрытия socket'а. На это может потребоваться значительный интервал времени, неприемлемый для некоторых приложений. В такой ситуации необходимо использовать описываемый далее системный вызов shutdown.

2. Создание клиента

Программа клиента делается аналогично до момента создания сокетов. Cоздайте сокет так как описано выше, но не пользуйтесь командой bind:
SOCKADDR_IN anAddr; anAddr.sin_family = AF_INET; anAddr.sin_port = htons(80); anAddr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
Заполнение структуры производится почти также но нeжно указать теперь IP адрес сервера ( пример 127.0.0.1 ) .Дальше сразу можно соединятся:

Для обращения программы-клиента к серверу с запросом на установление логической соединения используется системный вызов connect, имеющий следующий вид

int connect (s, addr, addrlen) int s; struct sockaddr_in *addr; int addrlen;

Аргумент s задает дескриптор socket'а, через который программа обращается к серверу с запросом на соединение. Socket должен быть предварительно создан системным вызовом socketи обеспечен адресом с помощью системного вызова bind.

Аргумент addr должен указывать на структуру данных, содержащую адрес, приписанный socket'у программы-сервера, к которой делается запрос на соединение. Для сетей TCP/IP такой структурой является sockaddr_in. Для формирования значений полей структуры sockaddr_in удобно использовать функцию gethostbyname.

Аргумент addrlen задает размер (в байтах) структуры данных, указываемой аргументом addr.

Для того, чтобы запрос на соединение был успешным, необходимо, по крайней мере, чтобы программа-сервер выполнила к этому моменту системный вызов listen для socket'а с указанным адресом.

При успешном выполнении запроса системный вызов connect возвращает 0, в противном случае - "-1" (устанавливая код причины неуспеха в глобальной переменной errno).

Примечание. Если к моменту выполнения connect используемый им socket не был привязан к адресу посредством bind ,то такая привязка будет выполнена автоматически.

Примечание. В режиме взаимодействия без установления соединения необходимости в выполнении системного вызова connect нет. Однако, его выполнение в таком режиме не является ошибкой - просто меняется смысл выполняемых при этом действий: устанавливается адрес "по умолчанию" для всех последующих посылок дейтаграмм.

Вот наконец установлена долгожданная связь c сервером( не забывайте проверять ошибки). Дальше воспользуемся функциями send и recv по своему усмотрению.

3. Приложение

Для получения адреса узла сети TCP/IP по его символическому имени используется библиотечная функция

struct hostent *gethostbyname (name) char *name;

Аргумент name задает адрес последовательности литер, образующих символическое имя узла сети.

При успешном завершении функция возвращает указатель на структуру hostent, определенную в include-файле netdb.h и имеющую следующий вид

struct hostent { char *h_name; char **h_aliases; int h_addrtype; int h_lenght; char *h_addr; };

Поле h_name указывает на официальное (основное) имя узла.

Поле h_aliases указывает на список дополнительных имен узла (синонимов), если они есть.

Поле h_addrtype содержит идентификатор используемого набора протоколов, для сетей TCP/IP это поле будет иметь значение AF_INET.

Поле h_lenght содержит длину адреса узла.

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

Для "экстренного" закрытия связи с партнером (путем "сброса" еще не переданных данных) используется системный вызов shutdown, выполняемый перед close и имеющий следующий вид

int shutdown (s, how) int s; int how;

Аргумент s задает дескриптор ранее созданного socket'а.

Аргумент how задает действия, выполняемые при очистке системных буферов socket'а:

Аргумент how задает действия, выполняемые при очистке системных буферов socket'а:

В данной статье – примере мне хотелось бы показать работу с простыми(raw) сокетами. Данные сокеты позволяют получить доступ к базовому протоколу передачи данных, это дает нам большие возможности создания таких сетевых утилит как Ping, Traceroute, а также для организации IP Spoofing, DDoS, ICMP-flood, и многого еще =) Рассматривать будем только протокол IP, так как большинство других протоколов вообще не поддерживают простые сокеты, разве только ATM.
Поддержка данного типа сокетов начинается только в Windows Sockets 2-ой версии. Первое, что необходимо сделать для использования данного типа сокета – это его создание. Для этого запускаем Visual C++, далее создаем новый проект(«CTRL+N» или File->New), тип проекта «Win32 Console Application», и пишем следующий код:

#include <Winsock2.h>//Ws2_32.lib
#include <Windows.h>
#include <iostream.h>
 
void ShowError()
{
        LPVOID lpMsgBuf = NULL;
        FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM,NULL,WSAGetLastError(),
                MAKELANGID(LANG_NEUTRAL,SUBLANG_DEFAULT),(LPTSTR)&lpMsgBuf,0,NULL);
        cout<<(LPCTSTR)lpMsgBuf<<endl;
        LocalFree(lpMsgBuf);
}
 
int main()
{
        WSADATA wsaData;
        if(WSAStartup(0x0202,&wsaData)){ShowError();}
        else
        {
                cout<<"WSAStartup - OK"<<endl;
                SOCKET sckt;
                sckt = socket(AF_INET,SOCK_RAW,IPPROTO_ICMP);
                if(sckt == INVALID_SOCKET){ShowError();}
                {
                        cout<<"Raw scoket is created"<<endl;
                        if(closesocket(sckt)== SOCKET_ERROR){ShowError();}
                }
                
                if(WSACleanup()){ShowError();}
                else{cout<<"WSACleanup - OK"<<endl;}
        }
        
        return 0;
}

Как видно из приведенного кода, для создания простого сокета достаточно указать вторым аргументом функции socket, протокол SOCK_RAW, но самое интересное представляет третий аргумент, в приведенной выше коде мы использовали IPPROTO_ICMP, но так же можно использовать IPPROTO_IGMP, IPPROTO_IP, IPPROTO_UDP, IPPROTO_RAW, IPPROTO_TCP, все эти параметры позволяют нам манипулировать заголовками представленных протоколов. При самостоятельном заполнение IP заголовка, необходимо добавить в код следующие строки:

BOOL opt = TRUE;
if(setsockopt (sckt,IPPROTO_IP,IP_HDRINCL,(char*)&opt,sizeof(opt))==SOCKET_ERROR)
{ShowError();}

Это позволит функции отправки(send, sendto) отправить заголовок IP перед посылаемыми данными, а функция приема вернет этот заголовок вместе с данными. На рисунке представленном ниже показан заголовок IP.

Raw сокеты  WinSocket C++

Теперь нам надо определить структуру, которая будет представлять IP заголовок:

typedef struct ip_hdr
{
        unsigned char    ip_verlen;
        unsigned char    ip_tos;
        unsigned short   ip_total_len;
        unsigned short   ip_id;
        unsigned short   ip_offset;
        unsigned char    ip_ttl;
        unsigned char    ip_protocol;
        unsigned short   ip_checksum;
        unsigned int     sourceIP;
        unsigned int     destIP;
}IP_HDR;

Для начальных опытов мы попробуем создать простой UDP сокет, так как он проще, его величина всего 8 байт, содержит 4 поля, более наглядно показано на рисунке ниже.
Raw сокеты  WinSocket C++

Определение структуры UDP заголовка.

typedef struct udp_hdr
{
        unsigned short   source_port;
        unsigned short   dest_port;
        unsigned short   udp_len;
        unsigned short   udp_sum;
}UDP_HDR;

Как Вы знаете протокол UDP ненадежный и не гарантирует доставку данных, значит вычисление контрольной суммы не обязательно, хотя и можно это сделать. Для этого используется псевдозаголовок представленный на рисунке:
Raw сокеты  WinSocket C++
Контрольная сумма UDP рассчитывается точно так же, как контрольная сумма IP заголовка, сумма 16-битных слов с переполнением. Если UDP датаграмма состоит из нечетного количества байт то необходимо в конце датаграммы добавить нулевые байты заполнения(который не передаются). Для расчета контрольной суммы в коде программы мы будем использовать следующею функцию, которая одинаковая для всех протоколов TCP/IP:

USHORT checksum(USHORT *buffer, int size)
{
    unsigned long cksum=0;
    while (size > 1){
        cksum += *buffer++;
        size  -= sizeof(USHORT);   
    }
    if (size){
        cksum += *(UCHAR*)buffer;   
    }
    cksum = (cksum >> 16) + (cksum & 0xffff);
    cksum += (cksum >>16);
    return (USHORT)(~cksum);
}

Вот полный вариант программы:

#include <Winsock2.h>//Ws2_32.lib
#include <ws2tcpip.h>
//#include <Windows.h>
#include <iostream.h>
#include <stdio.h>
#include <stdlib.h>
/*****************************************************************/
void ShowError()
{
        LPVOID lpMsgBuf = NULL;
        FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM,NULL,WSAGetLastError(),
                MAKELANGID(LANG_NEUTRAL,SUBLANG_DEFAULT),(LPTSTR)&lpMsgBuf,0,NULL);
        CharToOem((char*)lpMsgBuf,(char*)lpMsgBuf);
        cout<<(LPCTSTR)lpMsgBuf<<endl;
        LocalFree(lpMsgBuf);
}
 
USHORT checksum(USHORT *buffer, int size)
{
    unsigned long cksum=0;
    while (size > 1){
        cksum += *buffer++;
        size  -= sizeof(USHORT);   
    }
    if (size){
        cksum += *(UCHAR*)buffer;   
    }
    cksum = (cksum >> 16) + (cksum & 0xffff);
    cksum += (cksum >>16);
    return (USHORT)(~cksum);
}
/*****************************************************************/
typedef struct ip_hdr
{
        unsigned char    ip_verlen;
        unsigned char    ip_tos;
        unsigned short   ip_total_len;
        unsigned short   ip_id;
        unsigned short   ip_offset;
        unsigned char    ip_ttl;
        unsigned char    ip_protocol;
        unsigned short   ip_checksum;
        unsigned int     sourceIP;
        unsigned int     destIP;
}IP_HDR;
 
typedef struct udp_hdr
{
        unsigned short   source_port;
        unsigned short   dest_port;
        unsigned short   udp_len;
        unsigned short   udp_sum;
}UDP_HDR;
 
/*****************************************************************/
 
int main()
{
        WSADATA            wsaData;
        struct sockaddr_in remote;
        IP_HDR                 ipHdr;
        UDP_HDR                udpHdr;
        unsigned short     iTotalSize,iIPSize,iUdpSize, iUdpChecksumSize,ver;
        char                       buf[4096],*ptr,szMessage[4068];
        strcpy(szMessage,"Code by Lazy_elf");
 
 
        if(WSAStartup(0x0202,&wsaData)){ShowError();}
        else
        {
                cout<<"WSAStartup - OK"<<endl;
                SOCKET sckt;
                sckt = WSASocket (AF_INET,SOCK_RAW,IPPROTO_UDP,NULL,0,0);
                if(sckt == INVALID_SOCKET){ShowError();}
                {
                        cout<<"Raw scoket is created"<<endl;
 
                        BOOL opt = TRUE;
                        if(setsockopt (sckt,IPPROTO_IP,IP_HDRINCL,(char*)&opt,sizeof(opt))==SOCKET_ERROR)
                        {ShowError();}
                        else
                        {
                                cout<<"setsockopt - OK"<<endl;
                                iTotalSize = sizeof(ipHdr)+sizeof(udpHdr)+strlen(szMessage);
                                iIPSize    = sizeof(ipHdr)/sizeof(unsigned long);
                                ver = 4;
                                ipHdr.ip_verlen    = (ver<<4) | iIPSize;
                                ipHdr.ip_tos       = 0;
                                ipHdr.ip_total_len = htons(iTotalSize);
                                ipHdr.ip_id        = 0;
                                ipHdr.ip_offset    = 0;
                                ipHdr.ip_ttl       = 128;
                                ipHdr.ip_protocol  = IPPROTO_UDP;
                                ipHdr.ip_checksum  = 0;
                                ipHdr.sourceIP     = inet_addr("10.10.10.1");
                                ipHdr.destIP       = inet_addr("10.10.10.2");
                                //-------------------------------------------//
                                iUdpSize = sizeof(udpHdr)+strlen(szMessage);
                                udpHdr.source_port = htons(3004);
                                udpHdr.dest_port   = htons(4004);
                                udpHdr.udp_len     = htons(iUdpSize);
                                udpHdr.udp_sum     = 0;
                                //-------------------------------------------//
                                iUdpChecksumSize = 0;
                                ptr = buf;ZeroMemory(buf,4096);
                                memcpy(ptr,&ipHdr.sourceIP, sizeof(ipHdr.sourceIP));  
                                ptr              += sizeof(ipHdr.sourceIP);
                                iUdpChecksumSize += sizeof(ipHdr.sourceIP);
                                memcpy(ptr,&ipHdr.destIP,sizeof(ipHdr.destIP));
                                ptr              += sizeof(ipHdr.destIP);
                                iUdpChecksumSize += sizeof(ipHdr.destIP);
                                ptr++;iUdpChecksumSize += 1;
                            memcpy(ptr,&ipHdr.ip_protocol,sizeof(ipHdr.ip_protocol));
                                ptr              += sizeof(ipHdr.ip_protocol);
                                iUdpChecksumSize += sizeof(ipHdr.ip_protocol);
                                memcpy(ptr,&udpHdr.udp_len,sizeof(udpHdr.udp_len));
                                ptr              += sizeof(udpHdr.udp_len);
                                iUdpChecksumSize += sizeof(udpHdr.udp_len);
                        memcpy(ptr,&udpHdr,sizeof(udpHdr));
                                ptr              += sizeof(udpHdr);
                                iUdpChecksumSize += sizeof(udpHdr);
                                for(unsigned int i = 0; i < strlen(szMessage); i++, ptr++)
                                        *ptr = szMessage[i];
                                iUdpChecksumSize += strlen(szMessage);
                                udpHdr.udp_sum =  checksum((USHORT *)buf, iUdpChecksumSize);
                                
                                ZeroMemory(buf,4096);ptr = buf;
 
                                memcpy(ptr,&ipHdr, sizeof(ipHdr)); ptr += sizeof(ipHdr);
                                memcpy(ptr,&udpHdr,sizeof(udpHdr));ptr += sizeof(udpHdr);
                                memcpy(ptr,szMessage,strlen(szMessage));
 
                                
                                remote.sin_family      = AF_INET;
                                remote.sin_port        = htons(4004);
                                remote.sin_addr.s_addr = inet_addr("10.10.10.2");
                                if(sendto(sckt,buf,iTotalSize,0,(SOCKADDR *)&remote,sizeof(remote))==SOCKET_ERROR){ShowError();}
                                else{cout<<"sendto - OK"<<endl;}
                        }
 
                        if(closesocket(sckt)== SOCKET_ERROR){ShowError();}
                        else{cout<<"closesocket - OK"<<endl;}
                }
                
                if(WSACleanup()){ShowError();}
                else{cout<<"WSACleanup - OK"<<endl;}
        }
        return 0;
}

Raw сокеты  WinSocket C++

Источник