C++ Winsock P2P
сценарий
У кого-нибудь есть хорошие примеры одноранговой (p2p) сети в C++ с использованием Winsock? Это требование, предъявляемое к клиенту, которому необходимо использовать эту технологию (бог знает почему). Мне нужно определить, возможно ли это.
Любая помощь будет принята с благодарностью.
РЕДАКТИРОВАТЬ
И я хотел бы избежать использования библиотек, чтобы я мог понять исходный код и углубить свои знания.
2 ответа
Поскольку я не знаю, какую информацию вы ищете, я постараюсь описать, как настроить программу сокетов и с какими подводными камнями я столкнулся.
Для начала прочитайте учебник по Winsock от MSDN. Это базовая программа для подключения, отправки сообщения и отключения. Это отлично подходит для понимания программирования сокетов.
С этого давайте начнем:
Соображения:
блокирующий или неблокирующий
Прежде всего вам необходимо определить, хотите ли вы блокирующую или неблокирующую программу. Большая разница в том, что если у вас есть графический интерфейс, вам нужно будет использовать неблокирование или многопоточность, чтобы не заморозить программу. Я сделал так, чтобы использовать блокирующие вызовы, но всегда вызывать select
перед вызовом функций блокировки (подробнее об этом позже). Таким образом, я избегаю многопоточности, мьютекса и еще много чего, но все же использую основные accept
, send
а также receive
звонки.
Вы не можете полагаться на то, что ваши посылки будут доставлены так, как вы их отправили!
Вы тоже не имеете никакого влияния на это. Это была самая большая проблема, с которой я столкнулся, в основном потому, что сетевая карта может решать, какую информацию отправлять и когда отправлять. То, как я решил это было сделать networkPackageStruct
, содержащий size
а также data
где размер - это общее количество данных в этом пакете. Обратите внимание, что отправленное вами сообщение может быть разделено на 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: Соединение сброшено узлом.,
Если вы просто хотите внедрить приложение P2P в Microsoft Windows, вы можете попробовать использовать одноранговую сеть Windows.
Если вы хотите реализовать новый собственный протокол P2P, вы можете изучить протокол eMule и исходный код eMule. Вы могли бы сделать дальше, если вы посмотрите на исходный код Shareaza, это сделать eMule/Guntella/Gnutella/BitTorrent.