シナリオ
Winsockを使用したC++でのピアツーピア(p2p)ネットワーキングの良い例はありますか?これは、特にこのテクノロジーを使用する必要があるクライアントにとっての要件です(神はその理由を知っています)。これが実行可能かどうかを判断する必要があります。
どんな助けでも大歓迎です。
編集
また、基礎となるソースコードを理解し、さらに知識を深めるために、ライブラリの使用は避けたいと思います。
Winsockを使用したC++でのピアツーピア(p2p)ネットワーキングの良い例はありますか?これは、特にこのテクノロジーを使用する必要があるクライアントにとっての要件です(神はその理由を知っています)。これが実行可能かどうかを判断する必要があります。
どんな助けでも大歓迎です。
また、基礎となるソースコードを理解し、さらに知識を深めるために、ライブラリの使用は避けたいと思います。
あなたが探している情報がわからないので、ソケットプログラムのセットアップ方法と、私が遭遇した落とし穴について説明します。
まず、* MSDNのWinsock チュートリアルを読んでください。これは、接続、メッセージの送信、切断を行う基本的なプログラムです。ソケットプログラミングの感触をつかむのに最適です。
それでは、始めましょう:
考慮事項:
ブロッキングまたはノンブロッキング
まず、ブロッキング プログラムとノンブロッキング プログラムのどちらが必要かを決定する必要があります。GUI を使用している場合は、プログラムがフリーズしないようにするために、ノンブロッキングまたはスレッドを使用する必要があります。私が行った方法は、ブロッキング呼び出しを使用することでしたが、常にselect
ブロッキング関数を呼び出す前に呼び出します (select については後で詳しく説明します)。このようにして、スレッドやミューテックスなどを回避しますが、基本的なaccept
,send
およびreceive
呼び出しを使用します。
パケットが送信したとおりに届くとは限りません。
これも制御できません。これは私が遭遇した最大の問題でした。これは基本的に、ネットワーク カードがどの情報をいつ送信するかを決定できるためです。私がそれを解決した方法は、とnetworkPackageStruct
を含むを作成することでした。ここで、サイズはそのパケット内のデータの合計量です。送信するメッセージは 2 つ以上のパケットに分割でき、送信する別のメッセージとマージすることもできます。size
data
次の点を考慮してください。
あなたは2つのメッセージを送信します
"Hello"
"World!"
関数を使用してこれら 2 つのメッセージを送信すると、send
関数recv
はこのようにメッセージを取得できない場合があります。次のようになります。
"Hel"
"loWorld!"
多分
"HelloWorld!"
基盤となるネットワークがどのように感じられても。
(ほとんど)すべてをログに記録してください!
ネットワーク プログラムを完全に制御できないため (2 台のコンピューター上にあるため)、ネットワーク プログラムのデバッグは困難です。ブロッキング操作に遭遇した場合、それも見ることができません。これは、「ブロッキング コードを知る」と呼ぶこともできます。一方が何かを送信した場合、それがもう一方の側に届くかどうかはわかりません。そのため、何が送信され、何が受信されたかを追跡してください。
ソケットエラーに注意
Winsock 関数は多くの情報を返します。あなたのWSAGetLastError()
機能を知ってください。以下の例では省略しますが、多くの情報が返される傾向があることに注意してください。WinsockエラーメッセージSOCKET_ERROR
を取得するか、INVALID_SOCKET
確認するたびに調べてください。
接続の設定:
サーバーが必要ないため、すべてのクライアントには、新しい接続を受け入れるためのリッスン ソケットが必要です。最も簡単なのは次のとおりです。
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 は優れています。これにより、ソケットは 1 つの IP アドレスだけでなく、すべての IP アドレスをリッスンします。
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
もブロック!私が受けた頭を悩ませるエラーの 1 つは、それが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
}
シャットダウン中
最後に、シャットダウンには 2 つの方法があります。プログラムを閉じて切断するか、shutdown
関数を呼び出します。
select
がトリガーされます。recv
ただし、データは受信されませんが、代わりに 0 が返されrecv
ます。0 が返される他のケースに気付いていないため、これはシャットダウン コードと見なすことができると言っても過言ではありません。電話shutdown
をするのが一番いいです。shutdown
は冷淡ですが、もちろん機能します。shutdown
接続を閉じるのはプログラムではない可能性があるため、を使用してもエラーを処理する必要があります。覚えておくとよいエラー コードは 10054 ですWSAECONNRESET: Connection reset by peer
。Microsoft Windows に P2P アプリケーションを実装するだけの場合は、Windows ピアツーピア ネットワークを試すことができます。
独自の新しい P2P プロトコルを実装する場合は、eMule プロトコルとeMule ソース コードを学習できます。Shareaza のソース コードを調べると、eMule/Guntella/Gnutella/BitTorrent を実行できます。