C++ Winsock P2P

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2843277/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-28 11:14:57  来源:igfitidea点击:

C++ Winsock P2P

c++winsockp2pdistributed-computingwinsockets

提问by Goober

Scenario

设想

Does anyone have any good examples of peer-to-peer (p2p) networking in C++ using Winsock? It's a requirement I have for a client who specifically needs to use this technology (god knows why). I need to determine whether this is feasible.

有没有人有使用 Winsock 在 C++ 中进行点对点 (p2p) 网络的好例子?这是我对特别需要使用这项技术的客户的要求(天知道为什么)。我需要确定这是否可行。

Any help would be greatly appreciated.

任何帮助将不胜感激。

EDIT

编辑

And I would like to avoid using libraries so that I can understand the underlying source code and further my knoweldge.

我想避免使用库,以便我可以理解底层源代码并进一步了解我的知识。

回答by Default

Since I don't know what information you are looking for, I'll try to describe how to set up a socket program and what pitfalls I've run into.

由于我不知道您要查找什么信息,我将尝试描述如何设置套接字程序以及我遇到的陷阱。

To start with, read the Winsock tutorialfrom MSDN. This is a basic program to connect, send a message and disconnect. It's great for getting a feel for socket programming.

首先,阅读MSDN 上Winsock 教程。这是连接、发送消息和断开连接的基本程序。非常适合了解套接字编程。

With that, lets start:

有了这个,让我们开始:

Considerations:

注意事项:

blocking or non-blocking

阻塞或非阻塞

First of, you would need to determine if you want a blocking or non-blocking program. The big difference is that if you have a GUI you would need to use non-blocking or threading in order to not freeze the program. The way I did it was to use the blocking calls, but always calling selectbefore calling the blocking functions (more on select later). This way I avoid threading and mutex's and whatnot but still use the basic accept, sendand receivecalls.

首先,您需要确定您想要阻塞程序还是非阻塞程序。最大的区别在于,如果您有一个 GUI,您将需要使用非阻塞或线程,以免冻结程序。我这样做的方法是使用阻塞调用,但总是select在调用阻塞函数之前调用(稍后将详细介绍 select)。通过这种方式,我避免了线程和互斥锁等等,但仍然使用基本的accept,sendreceive调用。

You cannot rely on that your packages will arrive the way you send them!

您不能指望您的包裹会以您发送的方式到达!

You have no impact on this either. This was the biggest issue I ran into, basically because the network card can decide what information to send and when to send it. The way I solved it was to make a networkPackageStruct, containing a sizeand data, where size is the total amound of data in that package. Note that a message that you send can be split into 2- or more messages and can also be merged with another message you send.

你对此也没有影响。这是我遇到的最大问题,主要是因为网卡可以决定发送什么信息以及何时发送。我解决它的方法是制作一个networkPackageStruct,包含一个sizeand data,其中 size 是该包中的数据总量。请注意,您发送的一条消息可以拆分为 2 条或更多条消息,也可以与您发送的另一条消息合并。

Consider the following: You send two messages

考虑以下事项: 您发送了两条消息

"Hello"
"World!"

When you send these two messages with the sendfunction your recvfunction might not get them like this. It could look like this:

当您使用send函数发送这两条消息时,您的recv函数可能不会像这样获取它们。它可能看起来像这样:

"Hel"
"loWorld!"

or perhaps

也许

"HelloWorld!"

whatever the underlying network feels like..

无论底层网络感觉如何..

Log (almost) everything!

记录(几乎)一切!

Debugging a network program is hard because you don't have full control over it (since it's on two computers). If you run into a blocking operation you can't see it either. This could as well be called "Know your blocking code".. When one side sends something you don't know if it will arrive on the other side, so keep track of what is sent and what is received.

调试网络程序很困难,因为您无法完全控制它(因为它在两台计算机上)。如果您遇到阻塞操作,您也看不到它。这也可以称为“了解您的阻塞代码”。当一侧发送某些内容时,您不知道它是否会到达另一侧,因此请跟踪发送的内容和接收的内容。

Pay attention to socket errors

注意socket错误

winsock functions return alot of information. Know your WSAGetLastError()function. I'll won't keep it in the examples below, but note that they tend to return alot of information. Everytime you get a SOCKET_ERRORor INVALID_SOCKETcheck the Winsock Error Messagesto look it up

winsock 函数返回大量信息。了解你的WSAGetLastError()功能。我不会在下面的示例中保留它,但请注意,它们往往会返回大量信息。每次您收到SOCKET_ERRORINVALID_SOCKET检查Winsock 错误消息以进行查找时

Setting up the connection:

设置连接:

Since you don't want a server, all clients would need a listening socket to accept new connections. The easiest is:

由于您不需要服务器,因此所有客户端都需要一个侦听套接字来接受新连接。最简单的是:

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;

The INADDR_ANY is great - it actually makes your socket listen on all your networks instead of just one ipaddress.

INADDR_ANY 很棒 - 它实际上使您的套接字侦听所有网络,而不仅仅是一个 IP 地址。

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

here comes the interesting part. bindand listenwon't block but acceptwill. The trick is to use selectto check if there is an incoming connection. So the above code is just to set the socket up. in your program loop you check for new data in socket.

有趣的部分来了。bind并且listen不会阻止但accept会。诀窍是用来select检查是否有传入连接。所以上面的代码只是为了设置socket。在您的程序循环中,您检查套接字中的新数据。

Exchanging data

交换数据

The way I solved it is was to use selectalot. Basically you see if there are anything you need to respond to on any of your sockets. This is done with the FD_xxxfunctions.

我解决它的方法是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

in the newSocketyou now have a new peer. So that was for receiving data. But note!sendis also blocking! One of the "head scratching errors" I got was that sendblocked me. This was however also solved with select.

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
 }

Shutting down

关闭

Finally, there are two ways to shutdown. You either just disconnect by closing your program, or you call the shutdownfunction.

最后,有两种关闭方式。您要么通过关闭程序来断开连接,要么调用该shutdown函数。

  • Calling shutdown will make your peer selecttrigger. recvwill however not receive any data, but will instead return 0. I have not noticed any other case where recvreturns 0, so it is (somewhat) safe to say that this can be considered a shutdown-code. calling shutdownis the nicest thing to do..
  • Shutting down the connection without calling shutdown just is cold-hearted, but of course works. You still need to handle the error even if you use shutdown, since it might not be your program that closes the connection. A good error code to remember is 10054 which is WSAECONNRESET: Connection reset by peer..
  • 调用 shutdown 将使您的对等方select触发。recv然而,不会接收任何数据,而是返回 0。我没有注意到任何其他recv返回 0 的情况,因此(有点)可以肯定地说这可以被视为关闭代码。打电话shutdown是最好的事情..
  • 在不调用 shutdown 的情况下关闭连接是冷酷的,但当然有效。即使您使用shutdown,您仍然需要处理错误,因为它可能不是关闭连接的程序。要记住的一个很好的错误代码是 10054,它是WSAECONNRESET: Connection reset by peer。.

回答by Yi Zhao

If you just want to implement a P2P application on Microsoft Windows, you can try with Windows Peer-to-Peer Networking

如果您只想在 Microsoft Windows 上实现 P2P 应用程序,可以尝试使用Windows Peer-to-Peer Networking

If you want to implement a new P2P protocol of your own, you can study the eMule protocol, and eMule source code. You could do further if you look into Shareaza source code, it do eMule/Guntella/Gnutella/BitTorrent.

如果你想自己实现一个新的P2P协议,可以研究一下电骡协议,还有电骡源码。如果你查看Shareaza 源代码,你可以做进一步的工作,它可以做 eMule/Guntella/Gnutella/BitTorrent。