Локальный документ прежних версий windows 7

Provide feedback

Saved searches

Use saved searches to filter your results more quickly

Sign up

The <netinet/in.h> header shall define the following types:

in_port_t
Equivalent to the type uint16_t as described in <inttypes.h>.
in_addr_t
Equivalent to the type uint32_t as described in <inttypes.h>.

The <netinet_in.h> header shall define the sa_family_t type as described in <sys/socket.h>.

The <netinet_in.h> header shall define the uint8_t and uint32_t types as described in <inttypes.h>. Inclusion of the <netinet/in.h> header may also make
visible all symbols from <inttypes.h> and <sys/socket.h>.

The <netinet/in.h> header shall define the in_addr structure, which shall include at least the following
member:

in_addr_t  s_addr

The <netinet/in.h> header shall define the sockaddr_in structure, which shall include at least the following
members:

sa_family_t     sin_family   AF_INET. 
in_port_t       sin_port     Port number. 
struct in_addr  sin_addr     IP address. 

The sin_port and sin_addr members shall be in network byte order.

The sockaddr_in structure is used to store addresses for the Internet address family. Values of this type shall be cast
by applications to struct sockaddr for use with socket functions.

[IP6]
The <netinet/in.h> header shall define the in6_addr structure, which shall include at least the following
member:

uint8_t s6_addr[16]

This array is used to contain a 128-bit IPv6 address, stored in network byte order.

The <netinet/in.h> header shall define the sockaddr_in6 structure, which shall include at least the
following members:

sa_family_t      sin6_family    AF_INET6. 
in_port_t        sin6_port      Port number. 
uint32_t         sin6_flowinfo  IPv6 traffic class and flow information. 
struct in6_addr  sin6_addr      IPv6 address. 
uint32_t         sin6_scope_id  Set of interfaces for a scope. 

The sin6_port and sin6_addr members shall be in network byte order.

The sockaddr_in6 structure shall be set to zero by an application prior to using it, since implementations are free to
have additional, implementation-defined fields in sockaddr_in6.

The sin6_scope_id field is a 32-bit integer that identifies a set of interfaces as appropriate for the scope of the
address carried in the sin6_addr field. For a link scope sin6_addr, the application shall ensure that
sin6_scope_id is a link index. For a site scope sin6_addr, the application shall ensure that sin6_scope_id is
a site index. The mapping of sin6_scope_id to an interface or set of interfaces is implementation-defined.

The <netinet/in.h> header shall declare the following external variable:

const struct in6_addr in6addr_any

This variable is initialized by the system to contain the wildcard IPv6 address. The <netinet/in.h> header also
defines the IN6ADDR_ANY_INIT macro. This macro must be constant at compile time and can be used to initialize a variable of type
struct in6_addr to the IPv6 wildcard address.

The <netinet/in.h> header shall declare the following external variable:

const struct in6_addr in6addr_loopback

This variable is initialized by the system to contain the loopback IPv6 address. The <netinet/in.h> header also
defines the IN6ADDR_LOOPBACK_INIT macro. This macro must be constant at compile time and can be used to initialize a variable of
type struct in6_addr to the IPv6 loopback address.

The <netinet/in.h> header shall define the ipv6_mreq structure, which shall include at least the following
members:

struct in6_addr  ipv6mr_multiaddr  IPv6 multicast address. 
unsigned         ipv6mr_interface  Interface index. 

The <netinet/in.h> header shall define the following symbolic constants for use as values of the level
argument of getsockopt() and setsockopt():

IPPROTO_IP
Internet protocol.
IPPROTO_IPV6
[IP6]
Internet Protocol Version 6.
IPPROTO_ICMP
Control message protocol.
IPPROTO_RAW
[RS]
Raw IP Packets Protocol.
IPPROTO_TCP
Transmission control protocol.
IPPROTO_UDP
User datagram protocol.

The <netinet/in.h> header shall define the following symbolic constants for use as destination addresses for connect(), sendmsg(), and sendto():

INADDR_ANY
IPv4 local host address.
INADDR_BROADCAST
IPv4 broadcast address.

The <netinet/in.h> header shall define the following symbolic constant, with the value specified, to help
applications declare buffers of the proper size to store IPv4 addresses in string form:

INET_ADDRSTRLEN
16. Length of the string form for IP.

The htonl(), htons(), ntohl(), and ntohs() functions shall be available
as described in <arpa/inet.h>. Inclusion of the <netinet/in.h>
header may also make visible all symbols from <arpa/inet.h>.

[IP6]
The <netinet/in.h> header shall define the following symbolic constant, with the value specified, to help applications
declare buffers of the proper size to store IPv6 addresses in string form:

INET6_ADDRSTRLEN
46. Length of the string form for IPv6.

[IP6]
The <netinet/in.h> header shall define the following symbolic constants, with distinct integer values, for use in the
option_name argument in the getsockopt() or setsockopt() functions at protocol level IPPROTO_IPV6:

IPV6_JOIN_GROUP
Join a multicast group.
IPV6_LEAVE_GROUP
Quit a multicast group.
IPV6_MULTICAST_HOPS
Multicast hop limit.
IPV6_MULTICAST_IF
Interface to use for outgoing multicast packets.
IPV6_MULTICAST_LOOP
Multicast packets are delivered back to the local application.
IPV6_UNICAST_HOPS
Unicast hop limit.
IPV6_V6ONLY
Restrict AF_INET6 socket to IPv6 communications only.

The <netinet/in.h> header shall define the following macros that test for special IPv6 addresses. Each macro is of
type int and takes a single argument of type const struct in6_addr *:

IN6_IS_ADDR_UNSPECIFIED
Unspecified address.
IN6_IS_ADDR_LOOPBACK
Loopback address.
IN6_IS_ADDR_MULTICAST
Multicast address.
IN6_IS_ADDR_LINKLOCAL
Unicast link-local address.
IN6_IS_ADDR_SITELOCAL
Unicast site-local address.
IN6_IS_ADDR_V4MAPPED
IPv4 mapped address.
IN6_IS_ADDR_V4COMPAT
IPv4-compatible address.
IN6_IS_ADDR_MC_NODELOCAL
Multicast node-local address.
IN6_IS_ADDR_MC_LINKLOCAL
Multicast link-local address.
IN6_IS_ADDR_MC_SITELOCAL
Multicast site-local address.
IN6_IS_ADDR_MC_ORGLOCAL
Multicast organization-local address.
IN6_IS_ADDR_MC_GLOBAL
Multicast global address.

The following sections are informative.

End of informative text.

    //

    // A simple Internet server application.

    // It listens to the port written in command line (default 1234),

    // accepts a connection, and sends the «Hello!\r\n» message

    // to a client. Then it receives the answer from a client and terminates.

    //

    // Usage:

    //      server [port_to_listen]

    // Default is the port 1234.

    //

    #include <stdio.h>

    #include <string.h>

    #include <stdlib.h>

    #include <io.h>

    #include <winsock2.h>

    static void usage();

    int main(int argc, char *argv[]) {

        if (argc > 1 && *(argv[1]) == ‘-‘) {

            usage(); exit(1);

        }

        int listenPort = 1234;

        if (argc > 1)

            listenPort = atoi(argv[1]);

        // Create a socket

        int s0 = socket(AF_INET, SOCK_STREAM, 0);

        if (s0 < 0) {

            perror(«Cannot create a socket»); exit(1);

        }

        // Fill in the address structure containing self address

        struct sockaddr_in myaddr;

        memset(&myaddr, 0, sizeof(struct sockaddr_in));

        myaddr.sin_family = AF_INET;

        myaddr.sin_port = htons(listenPort);        // Port to listen

        myaddr.sin_addr.s_addr = htonl(INADDR_ANY);

        // Bind a socket to the address

        int res = bind(s0, (struct sockaddr*) &myaddr, sizeof(myaddr));

        if (res < 0) {

            perror(«Cannot bind a socket»); exit(1);

        }

        // Set the «LINGER» timeout to zero, to close the listen socket

        // immediately at program termination.

        struct linger linger_opt = { 1, 0 }; // Linger active, timeout 0

        setsockopt(s0, SOL_SOCKET, SO_LINGER, &linger_opt, sizeof(linger_opt));

        // Now, listen for a connection

        res = listen(s0, 1);    // «1» is the maximal length of the queue

        if (res < 0) {

            perror(«Cannot listen»); exit(1);

        }

        // Accept a connection (the «accept» command waits for a connection with

        // no timeout limit…)

        struct sockaddr_in peeraddr;

        socklen_t peeraddr_len;

        int s1 = accept(s0, (struct sockaddr*) &peeraddr, &peeraddr_len);

        if (s1 < 0) {

            perror(«Cannot accept»); exit(1);

        }

        // A connection is accepted. The new socket «s1» is created

        // for data input/output. The peeraddr structure is filled in with

        // the address of connected entity, print it.

        printf(

            «Connection from IP %d.%d.%d.%d, port %d\n»,

            (ntohl(peeraddr.sin_addr.s_addr) >> 24) & 0xff, // High byte of address

            (ntohl(peeraddr.sin_addr.s_addr) >> 16) & 0xff, // . . .

            (ntohl(peeraddr.sin_addr.s_addr) >> 8) & 0xff,  // . . .

            ntohl(peeraddr.sin_addr.s_addr) & 0xff,         // Low byte of addr

            ntohs(peeraddr.sin_port)

        );

        res = close(s0);    // Close the listen socket

        write(s1, «Hello!\r\n», 8);

        char buffer[1024];

        res = read(s1, buffer, 1023);

        if (res < 0) {

            perror(«Read error»); exit(1);

        }

        buffer[res] = 0;

        printf(«Received %d bytes:\n%s», res, buffer);

        close(s1);          // Close the data socket

        return 0;

    }

    static void usage() {

        printf(

            «A simple Internet server application.\n»

            «It listens to the port written in command line (default 1234),\n»

            «accepts a connection, and sends the \»Hello!\» message to a client.\n»

            «Then it receives the answer from a client and terminates.\n\n»

            «Usage:\n»

            »     server [port_to_listen]\n»

            «Default is the port 1234.\n»

        );

    }

1.5. Note for Windows Programmers
I have a particular dislike for Windows, and encourage you to try Linux, BSD, or Unix instead. That being said, you can still use this stuff under Windows.

First, ignore pretty much all of the system header files I mention in here. All you need to include is:

#include <winsock.h>

Wait! You also have to make a call to WSAStartup() before doing anything else with the sockets library. The code to do that looks something like this:

#include <winsock.h>

{
WSADATA wsaData; // if this doesn’t work
//WSAData wsaData; // then try this instead

if (WSAStartup(MAKEWORD(1, 1), &wsaData) != 0) {
fprintf(stderr, «WSAStartup failed.\n»);
exit(1);
}

You also have to tell your compiler to link in the Winsock library, usually called wsock32.lib or winsock32.lib or somesuch. Under VC++, this can be done through the Project menu, under Settings…. Click the Link tab, and look for the box titled «Object/library modules». Add «wsock32.lib» to that list.

Or so I hear.

Finally, you need to call WSACleanup() when you’re all through with the sockets library. See your online help for details.

Once you do that, the rest of the examples in this tutorial should generally apply, with a few exceptions. For one thing, you can’t use close() to close a socket—you need to use closesocket(), instead. Also, select() only works with socket descriptors, not file descriptors (like 0 for stdin).

There is also a socket class that you can use, CSocket. Check your compilers help pages for more information.

To get more information about Winsock, read the Winsock FAQ and go from there.

Finally, I hear that Windows has no fork() system call which is, unfortunately, used in some of my examples. Maybe you have to link in a POSIX library or something to get it to work, or you can use CreateProcess() instead. fork() takes no arguments, and CreateProcess() takes about 48 billion arguments. If you’re not up to that, the CreateThread() is a little easier to digest…unfortunately a discussion about multithreading is beyond the scope of this document. I can only talk about so much, you know!

От переводчика: Это перевод второй статьи из цикла «Networking for game programmers». Мне очень нравится весь цикл статей, плюс всегда хотелось попробовать себя в качестве переводчика. Возможно, опытным разработчикам статья покажется слишком очевидной, но, как мне кажется, польза от нее в любом случае будет.
Первая статья — http://habrahabr.ru/post/209144/


Прием и передача пакетов данных

Введение

Привет, меня зовут Гленн Фидлер и я приветствую вас в своей второй статье из цикла “Сетевое программирование для разработчиков игр”.

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

А сейчас я собираюсь рассказать вам, как на практике использовать UDP для отправки и приема пакетов.

BSD сокеты

В большинстве современных ОС имеется какая-нибудь реализация сокетов, основанная на BSD сокетах (сокетах Беркли).

Сокеты BSD оперируют простыми функциями, такими, как “socket”, “bind”, “sendto” и “recvfrom”. Конечно, вы можете обращаться к этим функциями напрямую, но в таком случае ваш код будет зависим от платформы, так как их реализации в разных ОС могут немного отличаться.

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

Особенности разных ОС

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

    // platform detection

    #define PLATFORM_WINDOWS  1
    #define PLATFORM_MAC      2
    #define PLATFORM_UNIX     3

    #if defined(_WIN32)
    #define PLATFORM PLATFORM_WINDOWS
    #elif defined(__APPLE__)
    #define PLATFORM PLATFORM_MAC
    #else
    #define PLATFORM PLATFORM_UNIX
    #endif

Теперь подключим заголовочные файлы, нужные для работы с сокетами. Так как набор необходимых заголовочных файлов зависит от текущей ОС, здесь мы используем код #define, написанный выше, чтобы определить, какие файлы нужно подключать.

    #if PLATFORM == PLATFORM_WINDOWS

        #include <winsock2.h>

    #elif PLATFORM == PLATFORM_MAC || PLATFORM == PLATFORM_UNIX

        #include <sys/socket.h>
        #include <netinet/in.h>
        #include <fcntl.h>

    #endif

В UNIX системах функции работы с сокетами входят в стандартные системные библиотеки, поэтому никакие сторонние библиотеки нам в этом случае не нужны. Однако в Windows для этих целей нам нужно подключить библиотеку winsock.

Вот небольшая хитрость, как можно это сделать без изменения проекта или makefile’а:

    #if PLATFORM == PLATFORM_WINDOWS
    #pragma comment( lib, "wsock32.lib" )
    #endif

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

Инициализация сокетов

В большинстве unix-like операционных систем (включая macosx) не требуется никаких особых действий для инициализации функционала работы с сокетами, но в Windows нужно сначала сделать пару па — нужно вызвать функцию “WSAStartup” перед использованием любых функций работы с сокетами, а после окончания работы — вызвать “WSACleanup”.

Давайте добавим две новые функции:

    inline bool InitializeSockets()
    {
        #if PLATFORM == PLATFORM_WINDOWS
        WSADATA WsaData;
        return WSAStartup( MAKEWORD(2,2), &WsaData ) == NO_ERROR;
        #else
        return true;
        #endif
    }

    inline void ShutdownSockets()
    {
        #if PLATFORM == PLATFORM_WINDOWS
        WSACleanup();
        #endif
    }

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

Создаем сокет

Теперь мы можем создать UDP сокет. Это делается так:

    int handle = socket( AF_INET, SOCK_DGRAM, IPPROTO_UDP );

    if ( handle <= 0 )
    {
        printf( "failed to create socket\n" );
        return false;
    }

Далее мы должны привязать сокет к определенному номеру порта (к примеру, 30000). У каждого сокета должен быть свой уникальный порт, так как, когда приходит новый пакет, номер порта определяет, какому сокету его передать. Не используйте номера портов меньшие, чем 1024 — они зарезервированы системой.

Если вам все равно, какой номер порта использовать для сокета, вы можете просто передать в функцию “0”, и тогда система сама выделит вам какой-нибудь незанятый порт.

    sockaddr_in address;
    address.sin_family = AF_INET;
    address.sin_addr.s_addr = INADDR_ANY;
    address.sin_port = htons( (unsigned short) port );

    if ( bind( handle, (const sockaddr*) &address, sizeof(sockaddr_in) ) < 0 )
    {
        printf( "failed to bind socket\n" );
        return false;
    }

Теперь наш сокет готов для передачи и приема пакетов данных.

Но что это за таинственная функция “htons” вызывается в коде? Это просто небольшая вспомогательная функция, которая переводит порядок следования байтов в 16-битном целом числе — из текущего (little- или big-endian) в big-endian, который используется при сетевом взаимодействии. Ее нужно вызывать каждый раз, когда вы используете целые числа при работе с сокетами напрямую.

Вы встретите функцию “htons” и ее 32-битного двойника — “htonl” в этой статье еще несколько раз, так что будьте внимательны.

Перевод сокета в неблокирующий режим

По умолчанию сокеты находится в так называемом “блокирующем режиме”. Это означает, что если вы попытаетесь прочитать из него данные с помощью “recvfrom”, функция не вернет значение, пока не сокет не получит пакет с данными, которые можно прочитать. Такое поведение нам совсем не подходит. Игры — это приложения, работающие в реальном времени, со скоростью от 30 до 60 кадров в секунду, и игра не может просто остановиться и ждать, пока не придет пакет с данными!

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

Перевести сокет в неблокирующий режим можно следующим образом:

    #if PLATFORM == PLATFORM_MAC || PLATFORM == PLATFORM_UNIX

        int nonBlocking = 1;
        if ( fcntl( handle, F_SETFL, O_NONBLOCK, nonBlocking ) == -1 )
        {
            printf( "failed to set non-blocking socket\n" );
            return false;
        }

    #elif PLATFORM == PLATFORM_WINDOWS

        DWORD nonBlocking = 1;
        if ( ioctlsocket( handle, FIONBIO, &nonBlocking ) != 0 )
        {
            printf( "failed to set non-blocking socket\n" );
            return false;
        }

    #endif

Как вы можете видеть, в Windows нет функции “fcntl”, поэтому вместе нее мы используем “ioctlsocket”.

Отправка пакетов

UDP — это протокол без поддержки соединений, поэтому при каждой отправке пакета нам нужно указывать адрес получателя. Можно использовать один и тот же UDP сокет для отправки пакетов на разные IP адреса — на другом конце сокета не обязательно должен быть один компьютер.

Переслать пакет на определенный адрес можно следующим образом:

    int sent_bytes = sendto( handle, (const char*)packet_data, packet_size,
                             0, (sockaddr*)&address, sizeof(sockaddr_in) );

    if ( sent_bytes != packet_size )
    {
        printf( "failed to send packet: return value = %d\n", sent_bytes );
        return false;
    }

Обратите внимание — возвращаемое функцией “sendto” значение показывает только, был ли пакет успешно отправлен с локального компьютера. Но оно не показывает, был ли пакет принят адресатом! В UDP нет средств для определения, дошел ли пакет по назначению или нет.

В коде, приведенном выше, мы передаем структуру “sockaddr_in” в качестве адреса назначения. Как нам получить эту структуру?

Допустим, мы хотим отправить пакет по адресу 207.45.186.98:30000.

Запишем адрес в следующей форме:

    unsigned int a = 207;
    unsigned int b = 45;
    unsigned int c = 186;
    unsigned int d = 98;
    unsigned short port = 30000;

И нужно сделать еще пару преобразований, чтобы привести его к форме, которую понимает “sendto”:

    unsigned int destination_address = ( a << 24 ) | ( b << 16 ) | ( c << 8 ) | d;
    unsigned short destination_port = port;

    sockaddr_in address;
    address.sin_family = AF_INET;
    address.sin_addr.s_addr = htonl( destination_address );
    address.sin_port = htons( destination_port );

Как видно, сначала мы объединяем числа a, b, c, d (которые лежат в диапазоне [0, 255]) в одно целое число, в котором каждый байт — это одно из исходных чисел. Затем мы инициализируем структуру “sockaddr_in” нашими адресом назначения и портом, при этом не забыв конвертировать порядок байтов с помощью функций “htonl” и “htons”.

Отдельно стоит выделить случай, когда нужно передать пакет самому себе: при этом не нужно выяснять IP адрес локальной машины, а можно просто использовать 127.0.0.1 в качестве адреса (адрес локальной петли), и пакет будет отправлен на локальный компьютер.

Прием пакетов

После того, как мы привязали UDP сокет к порту, все UDP пакеты, приходящие на IP адрес и порт нашего сокета, будут ставиться в очередь. Поэтому для приема пакетов мы просто в цикле вызываем “recvfrom”, пока он не выдаст ошибку, означающую, что пакетов для чтения в очерели не осталось.

Так как протокол UDP не поддерживает соединения, пакеты могут приходить с множества различных компьютеров сети. Каждый раз, когда мы принимаем пакет, функция “recvfrom” выдает нам IP адрес и порт отправителя, и поэтому мы знаем, кто отправил этот пакет.

Код приема пакетов в цикле:

    while ( true )
    {
        unsigned char packet_data[256];
        unsigned int maximum_packet_size = sizeof( packet_data );

        #if PLATFORM == PLATFORM_WINDOWS
        typedef int socklen_t;
        #endif

        sockaddr_in from;
        socklen_t fromLength = sizeof( from );

        int received_bytes = recvfrom( socket, (char*)packet_data, maximum_packet_size,
                                   0, (sockaddr*)&from, &fromLength );

        if ( received_bytes <= 0 )
            break;

        unsigned int from_address = ntohl( from.sin_addr.s_addr );
        unsigned int from_port = ntohs( from.sin_port );

        // process received packet
    }

Пакеты, размер которых больше, чем размер буфера приема, будут просто втихую удалены из очереди. Так что, если вы используете буфер размером 256 байтов, как в примере выше, и кто-то присылает вам пакет в 300 байт, он будет отброшен. Вы не получите просто первые 256 байтов из пакета.

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

Закрытие сокета

На большинстве unix-like систем, сокеты представляют собой файловые дескрипторы, поэтому для того, чтобы закрыть сокеты после использования, можно использовать стандартную функцию “close”. Однако, Windows, как всегда, выделяется, и в ней нам нужно использовать “closesocket”.

    #if PLATFORM == PLATFORM_MAC || PLATFORM == PLATFORM_UNIX
    close( socket );
    #elif PLATFORM == PLATFORM_WINDOWS
    closesocket( socket );
    #endif

Так держать, Windows!

Класс сокета

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

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

Поэтому мы сделаем класс-обертку “Socket” для всех этих операций. Также мы создадим класс “Address”, чтобы было проще работать с IP адресами. Он позволит не проводить все манипуляции с “sockaddr_in” каждый раз, когда мы захотим отправить или принять пакет.

Итак, наш класс Socket:

    class Socket
    {
    public:

        Socket();
        ~Socket();
        bool Open( unsigned short port );
        void Close();
        bool IsOpen() const;
        bool Send( const Address & destination, const void * data, int size );
        int Receive( Address & sender, void * data, int size );

    private:

        int handle;
    };

И класс Address:

    class Address
    {
    public:

        Address();
        Address( unsigned char a, unsigned char b, unsigned char c, unsigned char d, unsigned short port );
        Address( unsigned int address, unsigned short port );
        unsigned int GetAddress() const;
        unsigned char GetA() const;
        unsigned char GetB() const;
        unsigned char GetC() const;
        unsigned char GetD() const;
        unsigned short GetPort() const;
        bool operator == ( const Address & other ) const;
        bool operator != ( const Address & other ) const;

    private:

        unsigned int address;
        unsigned short port;
    };

Использовать их для приема и передачи нужно следующим образом:

    // create socket

    const int port = 30000;
    Socket socket;
    if ( !socket.Open( port ) )
    {
        printf( "failed to create socket!\n" );
        return false;
    }

    // send a packet

    const char data[] = "hello world!";
    socket.Send( Address(127,0,0,1,port), data, sizeof( data ) );

    // receive packets

    while ( true )
    {
        Address sender;
        unsigned char buffer[256];
        int bytes_read = socket.Receive( sender, buffer, sizeof( buffer ) );
        if ( !bytes_read )
            break;
        // process packet
    }

Как видите, это намного проще, чем работать с BSD сокетами напрямую. И также этот код будет одинаков для всех ОС, потому весь платформозависимый функционал находится внутри классов Socket и Address.

Заключение

Теперь у нас есть независимый от платформы инструмент для отправки и према UDP пакетов.

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

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

> Node 30000
> Node 30001
> Node 30002
И т.д…

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

Я разрабатывал эту программу на MacOSX, но она должна компилироваться на любой unix-like ОС и на Windows, однако если вам для этого потребуется делать какие-либо доработки, сообщите мне.

Понравилась статья? Поделить с друзьями:
0 0 голоса
Рейтинг статьи
Подписаться
Уведомить о
guest

0 комментариев
Старые
Новые Популярные
Межтекстовые Отзывы
Посмотреть все комментарии
  • Find your windows product key
  • Videopad for windows xp
  • Сделать видео из фото с музыкой windows 10
  • Как установить windows семь на ноутбук
  • Синий экран при переустановке windows 10