windows 找出IP地址是否可访问的最佳和快速方法
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/1955420/
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
Best & Fast way to find out if an ip address is reachable
提问by EBAG
I need the fastest way to see if an ip address is reachable. On each ip address theres a server listening on a specific port so let me know if your method is about to find if a server is listening on a port.
我需要最快的方法来查看 IP 地址是否可达。在每个 IP 地址上都有一个服务器侦听特定端口,因此如果您的方法即将查找服务器是否正在侦听某个端口,请告诉我。
The reason for this is that suppose I have 10 ip addresses with 10 server listening on port 101 on each ip address. I want my client be able to find a Reachable ip address and connect to it as fast as he can(I don't want him to wait 30 seconds to find out if a ip address is reachable and then try the next ip address in the list)
这样做的原因是假设我有 10 个 ip 地址,其中 10 个服务器在每个 ip 地址的端口 101 上侦听。我希望我的客户能够找到一个可访问的 ip 地址并尽可能快地连接到它(我不希望他等待 30 秒来确定一个 ip 地址是否可访问,然后尝试下一个 ip 地址列表)
May be it has to be done in treads simultaneously.
可能它必须同时进行。
回答by R Samuel Klatchko
While you can quickly determine that an IP is reachable, your problem is determining that an IP is not reachable. The reason why is that you can't always definitively determine that an IP is not reachable. While there are some conditions where you will be given an affirmative notice that the IP is not reachable, usually your code will just not hear an answer and after waiting for some amount of time, your code will assume the IP is not reachable.
虽然您可以快速确定 IP 可访问,但您的问题是确定 IP 不可访问。原因是您不能总是明确地确定 IP 不可访问。虽然在某些情况下,您会收到无法访问 IP 的肯定通知,但通常您的代码不会听到答复,并且在等待一段时间后,您的代码将假定 IP 无法访问。
The problem in deciding the timeout is network topology. If you have a large topology (such as the Internet), you will need a large timeout to deal with potentially high latencies if you try to connect to an IP that is 'far' away.
决定超时的问题是网络拓扑。如果您有一个大型拓扑结构(例如 Internet),并且您尝试连接到“远”的 IP,您将需要一个较大的超时时间来处理潜在的高延迟。
From your description, the best idea would be to try to connect to all servers at the same time and use the first one that accepts the connection. You can use threads or you can use non-blocking sockets. In a non-blocking connect, the connect call returns immediately and you then use selectto efficiently determine when the connect call has completed (either successfully or with an error).
根据您的描述,最好的办法是尝试同时连接到所有服务器并使用第一个接受连接的服务器。您可以使用线程,也可以使用非阻塞套接字。在非阻塞连接中,connect 调用会立即返回,然后您可以使用select来有效地确定 connect 调用何时完成(成功或出错)。
回答by Frunsi
You could use threads, but it would introduce unnecessary overhead for this task.
您可以使用线程,但它会为此任务引入不必要的开销。
Use non-blocking sockets here (and avoid non-blocking sockets wherever you can! really, but they make sense in this case):
在这里使用非阻塞套接字(并尽可能避免非阻塞套接字!真的,但在这种情况下它们是有意义的):
// initiate tcp connects...
for( each of your target host+port pairs ) {
int socket = socket( AF_INET, SOCK_STREAM );
...
#ifdef WIN32
unsigned long mode = 1;
ioctlsocket( socket, FIONBIO, &mode );
#else
int value = fcntl( socket, F_GETFL, 0 );
fcntl( socket, F_SETFL, value | O_NONBLOCK );
#endif
...
int code = connect( s, target, ... );
if( code == 0 ) { /* ok, this one did connect, DONE */ }
// now remember the socket in a list ...
}
// now find the first socket that was successfully connected
while( still not found ) {
struct timeval tval;
memset( &tval, 0, sizeof(tval) );
fd_set write_set, error_set;
int largest_socket = 0;
// add sockets to write and error set, and determine largest socket no.
for( each socket that did not fail until now ) {
FD_SET( socket, write_set );
FD_SET( socket, error_set );
if( socket > largest_socket ) largest_socket = socket;
}
// now use select to wait until something happens on the sockets
int code = select( largest_socket+1, NULL, &write_set, &error_set, &tval );
if( code < 0 ) { something went terribly wrong }
else {
for( each socket that did not fail until now ) {
if( FD_ISSET( socket, write_set ) ) { you've found it! DONE }
if( FD_ISSET( socket, error_set ) ) { error, remove this socket from list (for next iteration) }
}
}
}
Check documentation for connect
and select
for more details!
检查文档connect
,并select
了解更多详情!
回答by Guvante
Typically randomly trying to connect with a short time out is sufficient.
通常,在短时间内随机尝试连接就足够了。
Reachability is not very important, the fact that a route exists from you to the server isn't what matters, whether you can connected to said server is. Typically your own code will run just as fast as any other reachability method you can devise.
可达性不是很重要,从你到服务器的路由存在的事实并不重要,你是否可以连接到所述服务器才是重要的。通常,您自己的代码的运行速度与您可以设计的任何其他可达性方法一样快。
If you are having problems with it taking too long then try adjusting the length of your response, or having tighter timeouts.
如果您遇到时间过长的问题,请尝试调整响应的长度,或者缩短超时时间。
Simple algorithm:
简单算法:
shuffle IP addresses
foreach IP in addresses
attempt connect
if succeed then
break
回答by Hassan Syed
Try to open a socket using the connect() function from a BSD socket library. Its as fast as you can get, if the port is not open it wont respond to the SYN packet.
尝试使用BSD 套接字库中的 connect() 函数打开一个套接字。它尽可能快,如果端口未打开,它不会响应 SYN 数据包。
The key issue, as you realize, is tying up a thread which has to wait for a SYN-ACK before it can do anything else. Luckily, you do not need threads to parallelise IO anymore; however programming asynchronous operations can be subtle; therefore, I would recommend the libevent libraryfor dispatching TCP/IP connect operations in parallel... since the kernel is doing the heavy liftingyou only need one thread to do iton. You could probably do 100's or thousands of connects a second using libevent -- depending on your network hardware.
正如您所意识到的,关键问题是占用一个线程,该线程必须等待 SYN-ACK 才能执行其他任何操作。幸运的是,您不再需要线程来并行化 IO;然而,编程异步操作可能很微妙;因此,我建议使用libevent 库来并行调度 TCP/IP 连接操作......因为内核正在执行繁重的工作,您只需要一个线程来完成它。根据您的网络硬件,您可以使用 libevent 每秒进行 100 次或数千次连接。
Another alternative is Boost::ASIOwhich is more complicated. But since you are using C++ might suite you better.
另一种选择是Boost::ASIO,它更复杂。但是由于您使用的是 C++ 可能更适合您。
回答by Nick Dandoulakis
Below is code that you can use to create outgoing connections concurrently.
下面是可用于同时创建传出连接的代码。
Iterate over your IPs and SpawnOutgoing
connections in your loop.
Each connection conn_t*
is posted as LParam in a window message - concurrently.
在循环中迭代 IP 和SpawnOutgoing
连接。
每个连接conn_t*
都在窗口消息中作为 LParam 同时发布。
You should monitor the messages and save somewhere only the first connection - ignore (delete) other connections.
您应该监视消息并仅将第一个连接保存在某处 - 忽略(删除)其他连接。
#define MSG_NEW_CONNECTION (WM_USER + 1)
struct conn_t {
SOCKET s;
sockaddr_in server;
};
static
UINT OutgoingWorker(LPVOID param)
{
// `param` holds "conn_t*"
assert(param);
if (!param) return 0;
conn_t* c = (conn_t*)param;
if (SOCKET_ERROR == connect(c->s, (SOCKADDR*)&c->server, sizeof c->server)) {
closesocket(c->s);
return 0;
}
PostMessage(mainwnd, MSG_NEW_CONNECTION, 0, (LPARAM)c); // <-- mainwnd handle
return 0;
}
conn_t*
SpawnOutgoing(const char* dest_ip, const USHORT dest_port)
{
if (!dest_ip) return NULL;
SOCKET s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (INVALID_SOCKET == s) {
return NULL;
}
conn_t* c = new conn_t;
// Create the socket here but connect it in the worker
memset(&c->server, 0, sizeof sockaddr_in);
c->server.sin_family = AF_INET;
c->server.sin_addr.s_addr = inet_addr(dest_ip);
c->server.sin_port = htons(dest_port);
c->s = s;
CreateThread(0, 0, OutgoingWorker, c);
return c;
}