有人使用Winsock在C ++中有任何很好的对等(p2p)网络示例吗?对于有特殊需要使用此技术的客户,这是我的要求(上帝知道原因)。我需要确定这是否可行。
任何帮助将不胜感激。
而且我想避免使用库,以便我可以理解底层的源代码并进一步了解我的知识。
首先,从MSDN 读取Winsock tutorial。这是连接,发送消息和断开连接的基本程序。很好地了解套接字编程。
有了,让我们开始:
首先,您需要确定是要阻止程序还是非阻止程序。最大的区别是,如果您具有GUI,则需要使用非阻塞或线程化方式才能冻结程序。我这样做的方法是使用阻塞调用,但是总是在调用阻塞函数之前先调用select
(稍后会详细介绍)。这样,我避免使用线程和互斥锁,但仍然使用基本的accept
,send
和receive
调用。
您对此也没有影响。这是我遇到的最大问题,基本上是因为网卡可以决定发送什么信息以及何时发送信息。我解决的方法是制作一个networkPackageStruct
,其中包含size
和data
,其中size是该程序包中数据的总量。请注意,您发送的一条消息可以分为2条或更多条消息,也可以与您发送的另一条消息合并。
请考虑以下内容:您发送了两条消息
"Hello"
"World!"
[使用send
功能发送这两条消息时,recv
功能可能不会像这样得到它们。它可能看起来像这样:
"Hel"
"loWorld!"
或者也许
"HelloWorld!"
基础网络的感觉如何。.
调试网络程序很困难,因为您不能完全控制它(因为它在两台计算机上)。如果您遇到阻止操作,您也将看不到它。这也可以称为“知道您的阻止代码”。当一侧发送某些消息时,您不知道它是否会到达另一侧,因此请跟踪发送的内容和接收的内容。
winsock函数返回大量信息。了解您的WSAGetLastError()
功能。我不会在下面的示例中保留它,但是请注意,它们倾向于返回大量信息。每当您收到SOCKET_ERROR
或INVALID_SOCKET
时,请检查Winsock Error Messages进行查找
由于您不需要服务器,因此所有客户端都需要一个侦听套接字来接受新连接。最简单的是:
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:对等方重置连接。。如果只想在Microsoft Windows上实现P2P应用程序,则可以尝试使用Windows Peer-to-Peer Networking
如果要实现自己的新P2P协议,则可以研究eMule协议,然后单击eMule source code。如果您查看Shareaza source code,则可以做进一步的工作,它可以执行eMule / Guntella / Gnutella / BitTorrent。
我正在使用Visual Studio 19,每当我运行winsock教程中的服务器文件时,命令行完全空白,并且不执行任何操作。客户端可以工作,但是无法连接。帮助使其正常工作?