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;
}

Функция closesocket()

Функция closesocket закрывает существующий сокет.

int closesocket (_In_ SOCKET s);

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

Замечания

Функция closesocket закрывает сокет. Используйте его для освобождения дескриптора сокета, переданного в параметре s. Обратите внимание, что дескриптор сокета, переданный в параметре s, может быть немедленно повторно использован системой, как только будет запущена функция closesocket. В результате недостоверно ожидать, что дальнейшие ссылки на дескриптор сокета, переданные в параметре s, потерпят неудачу с ошибкой WSAENOTSOCK. Клиент Winsock никогда не должен выпускать closesocket одновременно с другим вызовом функции Winsock.

Любые ожидающие перекрывающиеся операции отправки и получения (WSASend / WSASendTo / WSARecv / WSARecvFrom с перекрывающимся сокетом), выпущенные любым потоком в этом процессе, также отменены. Выполняется любое событие, процедура завершения или действие порта завершения, указанное для этих перекрывающихся операций. Ожидающие перекрывающиеся операции завершаются с сообщением об ошибке WSA_OPERATION_ABORTED.

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

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

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

Член l_onoff структуры задержки определяет, должен ли сокет оставаться открытым в течение определенного промежутка времени после вызова функции closesocket, чтобы включить отправку отправляемых в очередь данных. Этот член может быть изменен двумя способами:

Член l_linger структуры задержки определяет время, в секундах, гнездо должно оставаться открытым. Этот член применим только в том случае, если член l_onoff структуры задерживания отличен от нуля.

Параметры по умолчанию для сокета: член l_onoff структуры linger равен нулю, что указывает на то, что сокет не должен оставаться открытым. Значение по умолчанию для элемента l_linger структуры linger равно нулю, но это значение игнорируется, когда член l_onoff установлен на ноль.

Чтобы позволить сокету оставаться открытым, приложение должно установить элемент l_onoff на ненулевое значение и установить член l_linger в требуемый таймаут в секундах. Чтобы отключить сокет, оставшийся открытым, необходимо только установить для элемента l_onoff структуры задержки значение 0.

Если приложение вызывает функцию setsockopt с параметром optname, установленным в SO_DONTLINGER, чтобы установить член l_onoff на ненулевое значение, значение для элемента l_linger не указано. В этом случае используемый тайм-аут зависит от реализации. Если для сокета был установлен предыдущий тайм-аут (ранее вызывающий функцию setsockopt с параметром optname, установленным в SO_LINGER), это значение таймаута должно быть восстановлено поставщиком услуг.

Семантика функции closesocket зависит от параметров сокета, которые устанавливают элементы структуры задержки. l_onoff l_linger Тип закрытия Ожидание закрытия? нуль Не волнует Изящное закрытие Нет ненулевой нулевой Hard Нет отличен от нуля отличен от нуля

Изящный, если все данные отправляются в течение времени ожидания, указанного в элементе l_linger.

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

Если член l_onoff структуры LINGER равен нулю в сокете потока, вызов closesocket немедленно возвращается и не получает WSAEWOULDBLOCK, блокируется или не блокируется сокет. Однако любые данные, помещенные в очередь для передачи, будут отправляться, если это возможно, до закрытия основного сокета. Это также называется изящным отключением или закрытием. В этом случае поставщик Windows Sockets не может освобождать сокет и другие ресурсы в течение произвольного периода времени, что влияет на приложения, которые ожидают использования всех доступных сокетов. Это поведение по умолчанию для сокета.

Если член l_onoff структуры задержки не равен нулю, а член l_linger равен нулю, closesocket не блокируется, даже если данные в очереди еще не отправлены или не подтверждены. Это называется жестким или прерывистым закрытием, потому что виртуальная схема сокета немедленно сбрасывается, и любые неотправленные данные теряются. В Windows любой вызов recv на удаленной стороне схемы завершится с WSAECONNRESET.

Если член l_onoff структуры задержки установлен на ненулевое значение, а член l_linger установлен на ненулевой тайм-аут в блокирующем сокете, вызов closesocket блокируется до тех пор, пока остальные данные не будут отправлены или пока истечет время ожидания. Это называется изящным отключением или закрытием, если все данные отправляются в течение времени ожидания, указанного в элементе l_linger. Если истечение времени ожидания истекает до того, как все данные были отправлены, реализация Windows Sockets завершает соединение до закрытия closesocket, и это называется жестким или неудачным закрытием.

Установка элемента l_onoff структуры задерживания на ненулевое значение, а элемент l_linger с ненулевым интервалом таймаута на неблокирующем сокете не рекомендуется. В этом случае вызов closesocket завершится с ошибкой WSAEWOULDBLOCK, если операция закрытия не может быть завершена немедленно. Если сбой закрытия закрывается с помощью WSAEWOULDBLOCK, дескриптор сокета остается в силе, а разъединение не инициируется. Приложение должно снова вызвать closesocket, чтобы закрыть сокет.

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

Если элемент l_onoff структуры задерживания отличен от нуля, а элемент l_linger - это нулевой интервал тайм-аута блокирующего сокета, тогда вызов closesocket сбрасывает соединение. Сокет не переходит в состояние TIME_WAIT.

Функция getsockopt может быть вызвана с параметром optname, установленным в SO_LINGER, для получения текущего значения структуры задержки, связанной с сокетом.

Примечание. Чтобы гарантировать, что все данные отправляются и принимаются по соединению, приложение должно вызывать выключение перед вызовом closesocket (см. «Извлеченное завершение работы, параметры задержки и закрытие сокета» для получения дополнительной информации). Также обратите внимание, что сетевое событие FD_CLOSE не отправляется после вызова closesocket.