Когда пишут про сокетное программирование, конечно
же подразумевается TCP/IP. Вот тут мы и отступимся от правил поговорим про
IPX/SPX. Многие забыли (а то и не знали) про существование данных протоколов, но надо отдать ему должное, так как еще многие программные модули и системы используют его. Подробнее ознакомится и прочитать о протоколе можно здесь
http://www.sources.ru/protocols/bsp08/index.html, в данном материале дана низкоуровневая реализация протокола с примерами под ДОС.
Заранее хочу извиниться за допущенные мной аберрации и
ошибки в статье, т.к. это мое первое творение
(надеюсь не последнее :).

А все начинается как всегда, а именно с инициализации WINSOCK библиотеки,
обработка ошибок упускается для упрощения кода:

#include <winsock.h> // прототипы функции библиотеки
#include <wsipx.h> //
IPX/SPX структуры
#include <wsnwlink.h> //
IPX/SPX структуры и константы для NT платформ

void main ()
{
WSADATA wsaData;

// версия winsock.dll библиотеки
WORD wVersionRequested = MAKEWORD( 1, 1 );

// инициализация winsock.dll
WSAStartup( wVersionRequested, &wsaData );

// работа с сокетами
………
………

WSACleanup( ); // не забываем сообщить системе, что мы закончили работу с winsock.dll
}

Ну и собственно сокет, тут я дам только кусок отличный от «нормальных» сокетов:

WORD SPX_SOCKET = 0x5647

char localNetNum[IPX_NET_SIZE]; 
char localNodeNum[IPX_NODE_SIZE];

// открытие сокета
SOCKET spx_skt = socket (PF_IPX, SOCK_STREAM, NSPROTO_SPX));

// проверим если открылся 
if (spx ==INVALID_SOCKET) MessageBox (NULL, “Ошибка открытия сокета.”, «Error», MB_OK);
//
структура для адреса
нашего сокета

sockaddr_ipx addr_ipx;

/* вот так выглядит структура sockaddr_ipx в WSIPX.H
typedef struct sockaddr_ipx {
u_short sa_family;
u_char sa_netnum[4];
u_char sa_nodenum[6];
unsigned short sa_socket;

} SOCKADDR_IPX, *PSOCKADDR_IPX, FAR *LPSOCKADDR_IPX
*/

int sz = sizeof (addr_ipx);

// обнулим её
memset (&addr_ipx, 0, sz);

addr_ipx.sa_family = AF_IPX; // тип протокола
addr_ipx.sa_socket = htons(SPX_SOCKET); //
номер сокета

// биндим сокет (привязываем его к номеру сокета)
bind(spx_skt, (sockaddr*) &addr_ipx, sz);
//
узнаем наш адрес
getsockname (spx_skt, (sockaddr*) &addr_ipx, &sz); 

// наш номер сети
memcpy (localNetNum, addr_ipx.sa_netnum, IPX_NET_SIZE);
//
наш номер узла
memcpy (localNodeNum, addr_ipx.sa_nodenum, IPX_NODE_SIZE);

В остальном работа с SPX идентична работе TCP сокетов, все выше написанное справедливо и для IPX сокетов, только не забудьте, что последние не могут быть законекчены.
Открываются они следующим образом:

SOCKET ipx_skt = socket (PF_IPX, SOCK_DGRAM, NSPROTO_IPX);

Передача данных происходит следующим образом:

// структура для хранения
удалённого адреса

sockaddr_ipx addr_ipx;

// обнуляем её, хотя это не всегда нужно, но и никогда не мешает
memset (&addr_ipx, 0, sizeof (addr_ipx));

// тип протокола
addr_ipx.sa_family = AF_IPX;

// номер сокета
addr_ipx.sa_socket = htons(SOCKET_NR);

// удалённый номер сети
memcpy (addr_ipx.sa_netnum, remoteNetNum, IPX_NET_SIZE);
//
удалённый номер узла
memcpy (addr_ipx.sa_nodenum, remoteNodeNum, IPX_NODE_SIZE);

char* buff = “Test string”;

// вот и собственно передача данных
sendto (ipx_skt, buff, strlen (buff), 0, (sockaddr*)&addr_ipx, sizeof (addr_ipx));

Дальше я дам несколько, на мой взгляд, полезных вещей при работе с данными протоколами.

Приём заголовка пакета данных

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

// первый вариант нужен wsnwlink.h
int rcv_header = 1;

setsockopt (spx_skt, NSPROTO_IPX, IPX_RECVHDR, (char*)&rcv_header, sizeof (rcv_header));

// второй вариант, только для SPX протокола
int rcv_header = 1;

setsockopt (spx_skt, NSPROTO_SPX, SPX_RAWSPX, (char*)&rcv_header, sizeof (rcv_header));

А вот вам и структура заголовка SPX пакета, взято из WSIPX.H

/* WSSpxHeader — SPX Header structure when in SPXL_SPXRAW mode. */

typedef struct WSSpxHeaderStruc
{
WSIpxHeader IpxHdr; // 0x00
u_char ConnCtrl; // 0x1E
u_char DataStreamType; // 0x1F
u_short SrcConnId; // 0x20
u_short DstConnId; // 0x22
u_short SendSeq; // 0x24
u_short AckSeq; // 0x26
u_short AllocNum; // 0x28
} WSSpxHeader, *PWSSpxHeader,FAR *LPWSSpxHeader; // 0x2A (42)

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

Широковещательные пакеты

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

sockaddr_ipx addr_ipx;

char* broadcast_msg = “Some broadcast stuff”;

SOCKET ipx_skt = socket (PF_IPX, SOCK_DGRAM, NSPROTO_IPX);

addr_ipx.sa_family = AF_IPX;

// номер сокета, данный номер используется
при посылке SAP пакетов в нетваре

addr_ipx.sa_socket = htons (0x0452); // = IPXSKT_SAP

// для широковещательных пакетов
memset (addr_ipx.sa_netnum, 0, IPX_NET_SIZE);
memset (addr_ipx.sa_nodenum, 0xff, IPX_NODE_SIZE);

// устанавливаем флаг для посылки широковещательных пакетов
int set_broadcast = 1;
setsockopt (ipx_skt, SOL_SOCKET, SO_BROADCAST, (char*)& set_broadcast, sizeof (set_broadcast));

// ну и собственно само вещание
sendto (ipx_skt, broadcast_msg, strlen (broadcast_msg), MSG_DONTROUTE, (sockaddr*)&addr_ipx, sizeof
(addr_ipx));

Установка, изменение DataStreamType в заголовке SPX пакета

Это в принципе может быть использовано в собственных целях, например для искусственной сегментации своих данных для совместимости разных реализаций протокола. Например, некоторые реализации протокола для ДОС
поддерживают максимальную длину пакета в 512 байт, либо принудительно ограниченную сетевыми модулями, вот они и используют DataStreamType, чтобы указать последнюю порцию данных.

Устанавливается следующим образом:

// Можно использовать любое значение между 0 — 0xfd
//
следующие значения зарезервированы
// #define SPX_HANG_UP 0xFE
// #define SPX_HANG_UP_ACK 0xFF
int stream_type = 0x05;

setsockopt (spx_skt, NSPROTO_IPX, IPX_DSTYPE, (char*)&stream_type, sizeof(stream_type);

Причём данную установку надо делать перед каждым send. Работает всё ОК когда посылаются данные ДОС клиенту, ну а при приеме пакетов WIN клиентом от ДОС клиента DataStreamType не
устанавливается, т.е. мы не получим установленное значение DataStreamType ДОС клиентом. Я
обошел данную проблему при помощи следующего куска кода:

int rcv; // количество
принятых байтов за один recv

int rcv_total = 0; //
общее количество принятых
байт

do{
//
прием данных
rcv = recv (spx_socket, rd_buffer + rcv_total, MAX_BUF_SIZE — rcv_total, 0);

if (rcv > 0) rcv_total += rcv; // если мы
что-то получили

}while (rcv > 0); // пока принимаются данные

Данный метод хорош еще тем, что WIN клиент может принять один пакет вместо нескольких посланных ДОС клиентом. 

Другие специфические расширения для данных протоколов используемые getsockopt/setsockopt можно найти в файле wsnwlink.h, но
(как упоминалось выше) данные расширения для NT платформ
могут не работать для других реализаций данных протоколов.

Оставить мнение

Check Also

LUKS container vs Border Patrol Agent. Как уберечь свои данные, пересекая границу

Не секрет, что если ты собрался посетить такие страны как США или Великобританию то, прежд…