У кого-либо есть какие-либо хорошие примеры одноранговых (p2p), объединяющийся в сеть в использовании C++ Winsock? Это - требование, которое я имею для клиента, который конкретно должен использовать эту технологию (бог знает почему). Я должен определить, выполнимо ли это.
Любая справка значительно ценилась бы.
И я хотел бы избегать использования библиотек так, чтобы я мог понять базовый исходный код и далее мое знание.
Для начала прочтите руководство по Winsock из MSDN. Это базовая программа для подключения, отправки сообщения и отключения. Это отлично подходит для знакомства с программированием сокетов.
Итак, приступим:
Во-первых, вам нужно определить, хотите ли вы блокирующую или неблокирующую программу. Большая разница в том, что если у вас есть графический интерфейс, вам нужно будет использовать неблокирующую или поточную обработку, чтобы программа не зависала. Я использовал блокирующие вызовы, но всегда вызывал select
перед вызовом блокирующих функций (подробнее о выборе позже).Таким образом, я избегаю потоков, мьютексов и прочего, но все же использую базовые вызовы accept
, send
и receive
.
Вы тоже не можете повлиять на это. Это была самая большая проблема, с которой я столкнулся, в основном потому, что сетевая карта может решать, какую информацию отправлять и когда ее отправлять. Я решил это сделать, создав networkPackageStruct
, содержащий размер
и данные
, где размер - это общий объем данных в этом пакете. Обратите внимание, что отправляемое вами сообщение может быть разделено на 2 или более сообщений, а также может быть объединено с другим отправляемым вами сообщением.
Учтите следующее: Вы отправляете два сообщения
"Hello"
"World!"
Если вы отправляете эти два сообщения с помощью функции send
, ваша функция recv
может не получить их в таком виде. Это может выглядеть так:
"Hel"
"loWorld!"
или, возможно,
"HelloWorld!"
, какова бы ни была основная сеть…
Отладка сетевой программы трудна, потому что у вас нет полного контроля над ней (поскольку она находится на двух компьютерах). Если вы столкнетесь с операцией блокировки, вы ее тоже не увидите. Это также можно было бы назвать «Знайте свой код блокировки». Когда одна сторона отправляет что-то, вы не знаете, будет ли оно доставлено другой стороне, поэтому следите за тем, что отправлено и что получено.
функции winsock возвращают много информации. Знайте свою функцию WSAGetLastError ()
.Я не буду хранить его в примерах ниже, но учтите, что они, как правило, возвращают много информации. Каждый раз, когда вы получаете SOCKET_ERROR
или INVALID_SOCKET
, проверьте Сообщения об ошибках Winsock , чтобы найти его
Поскольку вы этого не сделали нужен сервер, всем клиентам потребуется прослушивающий сокет для приема новых подключений. Самый простой:
SOCKET s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
sockaddr_in localAddress;
localAddress.sinfamily = AF_INET;
localAddress.sin_port = htons(10000); // or whatever port you'd like to listen to
localAddress.sin_addr.s_addr = INADDR_ANY;
INADDR_ANY великолепен - он фактически заставляет ваш сокет прослушивать все ваши сети, а не только один IP-адрес.
bind(s, (SOCKADDR*)&localAddress, sizeof(localAddress));
listen(s, SOMAXCONN);
А вот и самое интересное. bind
и listen
не будут блокировать, но accept
заблокирует. Уловка состоит в том, чтобы использовать выберите
, чтобы проверить, есть ли входящее соединение. Таким образом, приведенный выше код предназначен только для настройки сокета. в вашем программном цикле вы проверяете наличие новых данных в сокете.
Я решил, что нужно использовать select
много раз. В основном вы видите, есть ли что-нибудь, на что вам нужно ответить на любом из ваших сокетов. Это делается с помощью функций FD_xxx
.
// receiving data
fd_set mySet;
FD_ZERO(&mySet);
FD_SET(s, &mySet);
// loop all your sockets and add to the mySet like the call above
timeval zero = { 0, 0 };
int sel = select(0, &mySet, NULL, NULL, &zero);
if (FD_ISSET(s, &mySet)){
// you have a new caller
sockaddr_in remote;
SOCKET newSocket = accept(s, (SOCKADDR*)&remote, sizeof(remote));
}
// loop through your sockets and check if they have the FD_ISSET() set
в newSocket
теперь у вас есть новый узел. Так что это было для получения данных. Но обратите внимание! send
тоже блокируется! Одна из полученных мною "ошибок чесания головы" заключалась в том, что send
заблокировал меня. Однако это также было решено с помощью select
.
// sending data
// in: SOCKET sender
fd_set mySet;
FD_ZERO(&mySet);
FD_SET(sender, &mySet);
timeval zero = { 0, 0 };
int sel = select(0, NULL, mySet, NULL, &zero);
if (FD_ISSET(sender, &mySet)){
// ok to send data
}
Наконец, есть два способа завершить работу. Вы либо просто отключаетесь, закрыв вашу программу, либо вызываете функцию shutdown
.
выбрать триггер
. recv
, однако, не получит никаких данных, а вместо этого вернет 0. Я не заметил другого случая, когда recv
возвращает 0, поэтому (в некоторой степени) безопасно сказать, что это может считаться кодом выключения.Вызов shutdown
- это самый приятный поступок. shutdown
, поскольку это может быть не ваша программа, которая закрывает соединение. Следует запомнить код ошибки 10054, который представляет собой WSAECONNRESET: сброс соединения одноранговым узлом. . Если вы просто хотите реализовать приложение P2P в Microsoft Windows, вы можете попробовать Одноранговую сеть Windows
. Если вы хотите реализовать новый протокол P2P для самостоятельно, вы можете изучить протокол eMule и исходный код eMule . Вы можете сделать что-то еще, если посмотрите исходный код Shareaza , это eMule / Guntella / Gnutella / BitTorrent.