我需要以最快的方式查看是否可以访问IP地址。在每个IP地址上都有一个监听特定端口的服务器,所以让我知道你的方法是否要查找服务器是否正在监听端口。
原因是假设我有10个ip地址,每个ip地址上有10个服务器侦听端口101。我希望我的客户能够找到一个可达的IP地址,并尽可能快地连接到它(我不希望他等待30秒,以确定是否可以访问IP地址,然后尝试下一个IP地址列表)
可能必须同时在胎面上完成。
答案 0 :(得分:3)
虽然您可以快速确定IP是否可访问,但您的问题是确定无法访问IP。原因是您无法始终确定IP无法访问。虽然在某些情况下您会得到肯定的通知,告知IP无法访问,但通常您的代码将无法听到答案,并且在等待一段时间后,您的代码将认为无法访问IP。
决定超时的问题是网络拓扑。如果您有一个大型拓扑(例如Internet),如果您尝试连接到“远”的IP,则需要大的超时来处理潜在的高延迟。
根据您的描述,最好的想法是尝试同时连接到所有服务器并使用第一个接受连接的服务器。您可以使用线程,也可以使用非阻塞套接字。在非阻塞连接中,connect调用立即返回,然后使用select有效地确定连接调用何时完成(成功或出错)。
答案 1 :(得分:3)
您可以使用线程,但这会为此任务带来不必要的开销。
在这里使用非阻塞套接字(并尽可能避免使用非阻塞套接字!真的,但在这种情况下它们是有意义的):
// 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) }
}
}
}
查看connect
和select
的文档以获取更多详细信息!
答案 2 :(得分:2)
通常随机尝试短时间连接就足够了。
可达性不是很重要,从您到服务器的路由存在并不重要,无论您是否可以连接到所述服务器。通常,您自己的代码运行速度与您可以设计的任何其他可达性方法一样快。
如果您遇到问题需要花费太长时间,请尝试调整响应时长或缩短时间。
简单算法:
shuffle IP addresses
foreach IP in addresses
attempt connect
if succeed then
break
答案 3 :(得分:0)
尝试使用BSD socket library中的connect()函数打开套接字。它尽可能快,如果端口没有打开它就不会响应SYN数据包。
正如您所意识到的,关键问题是绑定一个线程,它必须等待SYN-ACK才能执行任何其他操作。 幸运的是,您不需要线程parallelise IO anymore ;但编程异步操作可能很微妙;因此,我建议libevent library并行调度TCP / IP连接操作...因为内核正在进行繁重的工作你只需需要一个线程来执行它/ strong> on。您可以使用libevent进行100秒或数千次连接 - 具体取决于您的网络硬件。
另一种选择是Boost::ASIO,这更复杂。但是,因为你使用C ++可能会让你更好。
答案 4 :(得分:0)
以下是可用于同时创建传出连接的代码。
在循环中迭代您的IP和SpawnOutgoing
连接
每个连接conn_t*
在窗口消息中同时发布为LParam。
您应该监控消息并仅保存第一个连接 - 忽略(删除)其他连接。
#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;
}