如何使用 Winsock API 控制连接超时?
我正在使用 Winsock API 编写一个程序,因为一位朋友想要一个简单的程序来检查Minecraft服务器是否正在运行。如果它正在运行,它工作正常,但是如果它没有运行,程序就会冻结,直到我假设连接超时。另一个问题是,如果我有这样的东西(伪代码):
void connectButtonClicked()
{
setLabel1Text("Connecting");
attemptConnection();
setLabel1Text("Done Connecting!");
}
它似乎直接跳到 attemptsConnection(),完全忽略它上面的内容。我注意到这一点是因为程序会冻结,但它不会将标签更改为“正在连接”。
这是我的实际连接代码:
bool CConnectionManager::ConnectToIp(String^ ipaddr)
{
if(!m_bValid)
return false;
const char* ip = StringToPConstChar(ipaddr);
m_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if(isalpha(ip[0]))
{
ip = getIPFromAddress(ipaddr);
}
sockaddr_in service;
service.sin_family = AF_INET;
service.sin_addr.s_addr = inet_addr(ip);
service.sin_port = htons(MINECRAFT_PORT);
if(m_socket == NULL)
{
return false;
}
if (connect(m_socket, (SOCKADDR*)&service, sizeof(service)) == SOCKET_ERROR)
{
closesocket(m_socket);
return false;
}
else
{
closesocket(m_socket);
return true;
}
return true;
}
CConnectionManager 的构造函数中还有启动 Winsock API 等的代码。
那么,如何避免这种冻结,并允许我在连接期间更新进度条之类的内容?我必须在单独的线程中建立连接吗?我只在 Java 中使用过线程,所以我不知道如何做到这一点:/
另外:我正在使用 CLR Windows 表单应用程序
我正在使用 Microsoft Visual C++ 2008 Express 版
I'm writing a program using the Winsock API because a friend wanted a simple program to check and see if a Minecraft server was running or not. It works fine if it is running, however if it is not running, the program freezes until, I'm assuming, the connection times out. Another issue is, if I have something like this (pseudo-code):
void connectButtonClicked()
{
setLabel1Text("Connecting");
attemptConnection();
setLabel1Text("Done Connecting!");
}
it seems to skip right to attemptConnection(), completely ignoring whats above it. I notice this because the program will freeze, but it wont change the label to "Connecting".
Here is my actual connection code:
bool CConnectionManager::ConnectToIp(String^ ipaddr)
{
if(!m_bValid)
return false;
const char* ip = StringToPConstChar(ipaddr);
m_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if(isalpha(ip[0]))
{
ip = getIPFromAddress(ipaddr);
}
sockaddr_in service;
service.sin_family = AF_INET;
service.sin_addr.s_addr = inet_addr(ip);
service.sin_port = htons(MINECRAFT_PORT);
if(m_socket == NULL)
{
return false;
}
if (connect(m_socket, (SOCKADDR*)&service, sizeof(service)) == SOCKET_ERROR)
{
closesocket(m_socket);
return false;
}
else
{
closesocket(m_socket);
return true;
}
return true;
}
There is also code in the CConnectionManager's contructor to start up Winsock API and such.
So, how do I avoid this freeze, and allow me to update something like a progress bar during connection? Do I have to make the connection in a separate thread? I have only worked with threads in Java, so I have no idea how to do that :/
Also: I am using a CLR Windows Form Application
I am using Microsoft Visual C++ 2008 Express Edition
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
您的代码不会跳过标签更新。更新仅涉及发出尚未处理的窗口消息,这就是为什么在连接套接字之前您看不到新文本出现的原因。在连接套接字之前,您必须在消息队列中抽取新消息。
不幸的是,对于套接字本身,WinSock API 中没有连接超时。您有两种选择来实现手动超时:
1) 假设您使用阻塞套接字(默认情况下套接字是阻塞的),在单独的工作线程中执行连接。
2)如果您不想使用线程,请将套接字切换为非阻塞模式。连接套接字总是会立即退出,因此您的主代码不会被阻塞,然后您稍后会收到连接成功与否的通知。有多种方法可以检测该情况,具体取决于您使用的 API - WSAAsyncSelect()、WSAAsyncEvent() 或 select()。
无论哪种方式,当连接正在进行时,在主线程中运行一个计时器。如果连接成功,则停止计时器。如果计时器超时,则断开套接字,这将导致连接因错误而中止。
Your code does not skip the label update. The update simply involves issuing window messages that have not been processed yet, that is why you do not see the new text appear before connecting the socket. You will have to pump the message queue for new messages before connecting the socket.
As for the socket itself, there is no connect timeout in the WinSock API, unfortunately. You have two choices to implement a manual timeout:
1) Assuming you are using a blocking socket (sockets are blocking by default), perform the connect in a separate worker thread.
2) If you don't want to use a thread then switch the socket to non-blocking mode. Connecting the socket will always exit immediately, so your main code will not be blocked, then you will receive a notification later on if the connection was successful or not. There are several ways to detect that, depending on which API you use - WSAAsyncSelect(), WSAAsyncEvent(), or select().
Either way, while the connect is in progress, run a timer in your main thread. If the connect succeeds, stop the timer. If the timer elapses, disconnect the socket, which will cause the connect to abort with an error.
也许您想阅读此处:
为了确保在关闭连接的套接字之前发送和接收所有数据,应用程序应在调用 closesocket 之前使用 shutdown 来关闭连接。 http://msdn.microsoft.com/en -us/library/ms740481%28v=VS.85%29.aspx
由于您处于阻止模式,因此可能仍然有一些数据...
Maybe you want to read here:
To assure that all data is sent and received on a connected socket before it is closed, an application should use shutdown to close connection before calling closesocket. http://msdn.microsoft.com/en-us/library/ms740481%28v=VS.85%29.aspx
Since you are in the blocking mode there still might be some data...