Scenario
有没有人有任何使用 Winsock 在 C++ 中进行点对点 (p2p) 网络的好例子?
这是我对特别需要使用这项技术的客户的要求(天知道为什么)。
我需要确定这是否可行。
任何帮助将不胜感激。
EDIT
我想避免使用库,以便我可以理解底层源代码并进一步加深我的知识。
由于我不知道您在寻找什么信息,因此我将尝试描述如何设置套接字程序以及我遇到的陷阱。
首先,*阅读Winsock教程在 MSDN。这是一个用于连接、发送消息和断开连接的基本程序。这对于了解套接字编程非常有用。
接下来,我们开始:
注意事项:
-
阻塞或非阻塞
首先,您需要决定是否需要阻塞程序或非阻塞程序。如果您有 GUI,则需要使用非阻塞或线程以避免冻结程序。我这样做的方法是使用阻塞调用,但总是调用select
在调用阻塞函数之前(稍后将详细介绍 select)。这样我就避免了线程和互斥体之类的东西,但仍然使用基本的accept
, send
and receive
calls.
-
您不能相信您的包裹会按照您发送的方式到达!
你也无法控制这一点。这是我遇到的最大问题,基本上是因为网卡可以决定发送哪些信息以及何时发送。我解决这个问题的方法是制作一个networkPackageStruct
,包含一个size
and data
,其中 size 是该数据包中的数据总量。请注意,您发送的消息可以分为 2 个或更多数据包,也可以与您发送的另一条消息合并。
考虑以下:
您发送两条消息
"Hello"
"World!"
当您发送这两条消息时send
发挥你的recv
函数可能不会像这样得到它们。它可能看起来像这样:
"Hel"
"loWorld!"
也许
"HelloWorld!"
无论底层网络感觉如何。
-
记录(几乎)所有内容!
调试网络程序很困难,因为您无法完全控制它(因为它位于两台计算机上)。如果您遇到阻塞操作,您也看不到它。这也可以称为“了解您的阻止代码”。当一侧发送某些内容时,您不知道它是否会到达另一侧,因此请跟踪发送的内容和接收的内容。
-
注意socket错误
Winsock 函数返回大量信息。了解你的WSAGetLastError()
功能。我不会将其保留在下面的示例中,但请注意它们往往会返回大量信息。每次你得到一个SOCKET_ERROR
or 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 很棒 - 它使您的套接字侦听所有 IP 地址,而不仅仅是一个 IP 地址。
bind(s, (SOCKADDR*)&localAddress, sizeof(localAddress));
listen(s, SOMAXCONN);
有趣的部分来了。bind
and listen
不会阻止但是accept
将要。诀窍是使用select
检查是否有传入连接。所以上面的代码只是设置socket。在程序循环中,您检查套接字中的新数据。
交换数据
-
我解决这个问题的方法是使用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 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
功能。
- 调用 shutdown 将使你的同伴
select
扳机。recv
但是不会收到任何数据,而是返回 0。我没有注意到任何其他情况recv
返回 0,因此可以(在某种程度上)安全地说这可以被视为关闭代码。呼叫shutdown
是最好的事情。
- 不调用就关闭连接
shutdown
只是冷酷无情,但当然有效。即使您使用,您仍然需要处理错误shutdown
,因为关闭连接的可能不是您的程序。一个值得记住的错误代码是 10054,它是WSAECONNRESET: Connection reset by peer
.
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)