C ++ Winsock P2P - PullRequest
       44

C ++ Winsock P2P

10 голосов
/ 16 мая 2010

Сценарий

Есть ли у кого-нибудь хорошие примеры одноранговой (p2p) сети в C ++ с использованием Winsock? Это требование, предъявляемое к клиенту, которому необходимо использовать эту технологию (бог знает почему). Мне нужно определить, возможно ли это.

Любая помощь будет принята с благодарностью.

EDIT

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

Ответы [ 2 ]

37 голосов
/ 27 мая 2010

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

Для начала прочитайте учебник Winsock из MSDN. Это базовая программа для подключения, отправки сообщения и отключения. Отлично подходит для программирования сокетов.

Итак, давайте начнем:

Вопросы:

блокировка или неблокировка

Прежде всего вам необходимо определить, хотите ли вы блокирующую или неблокирующую программу. Большая разница в том, что если у вас есть графический интерфейс, вам нужно будет использовать неблокирование или многопоточность, чтобы не заморозить программу. Я сделал это, используя блокирующие вызовы, но всегда вызывая select перед вызовом блокирующих функций (подробнее о выборе позже). Таким образом, я избегаю многопоточности, мьютекса и прочего, но все же использую базовые вызовы accept, send и receive.

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

Вы тоже не оказывает на это никакого влияния. Это была самая большая проблема, с которой я столкнулся, в основном потому, что сетевая карта может решать, какую информацию отправлять и когда отправлять. Я решил, что нужно создать networkPackageStruct, содержащий size и data, где size - это общее количество данных в этом пакете. Обратите внимание, что отправленное вами сообщение может быть разбито на 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 великолепен - он действительно заставляет ваш сокет прослушивать все ваши сети, а не только один ipaddress.

bind(s, (SOCKADDR*)&localAddress, sizeof(localAddress));
listen(s, SOMAXCONN);

здесь начинается интересная часть. bind и listen не будут блокироваться, но accept будут. Хитрость заключается в том, чтобы использовать select для проверки наличия входящего соединения. Таким образом, приведенный выше код просто для установки сокета. в цикле программы вы проверяете наличие новых данных в сокете.

Обмен данными

Способ, который я решил, это использовать 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.

  • Вызов выключения сделает ваш равноправный select триггер. Однако recv не получит никаких данных, но вместо этого вернет 0. Я не заметил ни одного другого случая, когда recv возвращает 0, поэтому (несколько) можно с уверенностью сказать, что это можно считать кодом завершения работы. звонить shutdown - это самое приятное занятие ..
  • Завершение соединения без вызова отключения просто хладнокровно, но, конечно, работает. Вам все еще нужно обработать ошибку, даже если вы используете shutdown, так как это может быть не ваша программа, которая закрывает соединение. Хороший код ошибки, который нужно запомнить, это 10054, который WSAECONNRESET: сброс соединения по пиру. .
2 голосов
/ 18 мая 2010

Если вы просто хотите реализовать приложение P2P в Microsoft Windows, вы можете попробовать Одноранговая сеть Windows

Если вы хотите реализовать новый собственный протокол P2P, вы можете изучить протокол eMule и исходный код eMule . Вы можете сделать дальше, если посмотрите на исходный код Shareaza , это делают eMule / Guntella / Gnutella / BitTorrent.

...