C ++ Winsock P2P

脚本

有没有人有使用Winsock的C ++中的对等(p2p)networking的好例子? 这是我对一个特别需要使用这种技术的客户的要求(上帝知道为什么)。 我需要确定这是否可行。

任何帮助将不胜感激。

编辑

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

由于我不知道你在找什么信息,所以我会试着描述一下如何设置一个套接字程序以及我遇到的问题。

首先, 阅读 MSDN 的Winsock教程 。 这是一个连接,发送消息和断开连接的基本程序。 获得套接字编程的感觉真是太棒了。

这样,让我们​​开始:

注意事项:

阻塞或不阻塞

首先,你需要确定你是否想要一个阻塞或非阻塞的程序。 最大的区别是,如果你有一个GUI,你将需要使用非阻塞或线程来冻结程序。 我这样做的方式是使用阻塞调用,但总是在调用阻塞函数之前调用select (更多select稍后)。 这样我就避免了线程和互斥锁,而且还是使用基本的acceptsendreceive调用。

你不能依靠你的包裹将以你送他们的方式来到!

你也没有影响。 这是我遇到的最大的问题,主要是因为网卡可以决定发送什么信息以及何时发送。 我解决这个问题的方法是创build一个networkPackageStruct ,它包含一个sizedata ,其中size是该数据包中数据的总数。 请注意,您发送的邮件可以分成两个或更多个邮件,也可以与您发送的另一个邮件合并。

考虑以下内容:您发送两条消息

 "Hello" "World!" 

当你用send函数发送这两个消息时,你的recv函数可能不会像这样得到它们。 它可能是这样的:

 "Hel" "loWorld!" 

也许

 "HelloWorld!" 

无论底层networking感觉如何

logging(几乎)一切!

debugging一个networking程序很困难,因为你没有完全控制它(因为它在两台计算机上)。 如果遇到阻塞操作,则无法看到它。 这也可以被称为“了解你的阻止代码”。当一方发送的东西,你不知道它是否会到达另一边,所以跟踪什么是发送和接收。

注意套接字错误

winsock函数返回大量的信息。 知道你的WSAGetLastError()函数。 我不会在下面的例子中保留它,但是请注意,它们倾向于返回大量的信息。 每次你得到一个SOCKET_ERRORINVALID_SOCKET检查Winsock错误消息来查找它

build立连接:

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

 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非常棒 – 它实际上让你的套接字在你所有的networking上监听,而不仅仅是一个ipaddress。

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

这里来了有趣的部分。 bindlisten不会阻止,但accept意志。 诀窍是使用select来检查是否有传入的连接。 所以上面的代码只是设置套接字。 在你的程序循环中你检查socket中的新数据。

交换数据

我解决这个问题的方法是使用select很多。 基本上,你会看到是否有任何你需要回应你的任何sockets。 这是用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 } 

关机

最后有两种关机方式。 您可以通过closures程序来断开连接,也可以调用shutdownfunction。

  • 调用关机将使您的对等select触发器。 然而, recv不会收到任何数据,而是返回0.我还没有注意到recv返回0的其他情况,所以说这可以被认为是closures代码。 调用shutdown是最好的事情..
  • closures连接而不closuresclosures只是冷冰冰的,但当然是有效的。 即使使用shutdown ,仍然需要处理该错误,因为它可能不是closures连接的程序。 要记住一个好的错误代码是10054,它是WSAECONNRESET:由对等方重置连接。

如果您只想在Microsoft Windows上实现P2P应用程序,则可以尝试使用Windows对等networking

如果你想实现你自己的一个新的P2P协议,你可以学习eMule协议和eMule源代码 。 如果你看看Shareaza源代码 ,你可以做更多的事情,它可以做eMule / Guntella / Gnutella / BitTorrent。