Welcome toVigges Developer Community-Open, Learning,Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
220 views
in Technique[技术] by (71.8m points)

How to terminate windows sockets when internet is down? (C++ WinAPI)

I have set up a Winsock2 connection but I need to cover the case where internet is down. Here is my code;

#include <winsock2.h>
#include <windows.h>
#include <ctime>
int main()
{
    WSADATA w;
    if(WSAStartup(MAKEWORD(2,2),&w)) return 0;
    sockaddr_in sad;
    sad.sin_family=AF_INET;
    sad.sin_addr.s_addr=inet_addr("200.20.186.76");
    sad.sin_port=htons(123);
    sockaddr saddr;
    int saddr_l=sizeof(saddr);
    int s=socket(PF_INET,SOCK_DGRAM,IPPROTO_UDP);
    if(s==INVALID_SOCKET) return 0;
    char msg[48]={8};
    if(sendto(s,msg,sizeof(msg),0,(sockaddr*)&sad,sizeof(sad))==SOCKET_ERROR) return 0;
    if(recvfrom(s,msg,48,0,&saddr,&saddr_l)==SOCKET_ERROR) return 0;
    if(closesocket(s)==SOCKET_ERROR) return 0;
    if(WSACleanup()) return 0;
    return 0;
}

Here it waits for the call to return as it's documented. I have two questions.

  1. Can I set a timeout like we can do when using select
  2. How else can I prevent the waiting and make it return immediately? Documentation states that:

When issuing a blocking Winsock call such as sendto, Winsock may need to wait for a network event before the call can complete. Winsock performs an alertable wait in this situation, which can be interrupted by an asynchronous procedure call (APC) scheduled on the same thread.

How to do that?


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

If you want to issue a recvfrom() and have it return immediately, then decide on your own how long to wait (I'm assuming Windows since you included winsock2.h), you can make an asynchronous OVERLAPPED request, then wait for the completion at any time by waiting for the hEvent member of the OVERLAPPED struct to be signaled.

Here's an updated sample based off your original code.

  • you set the timeout by waiting as long as you need with WaitForSingleObject (below I wait for 10 seconds 6 times)
  • by passing an OVERLAPPED pointer, you are indicating that you will wait for the completion yourself. Note that the OVERLAPPED struct cannot go out of scope until the hEvent is signaled. (or freed, if the OVERLAPPED was dynamically allocated).
  • Letting the OVERLAPPED go out of scope before guaranteeing the IO completed is a common Winsock bug (I've been working on Winsock for over 10 years or so - I've seen many variations of this bug)
  • As commented below, if you don't know hEvent has been signaled, then after calling closesocket you must wait for hEvent to be signaled before continuing - closesocket does not guarantee all asynchronous IO request have completed before returning.
#define _WINSOCK_DEPRECATED_NO_WARNINGS
#include <winsock2.h>
#include <windows.h>
#include <ctime>
int main()
{
    WSADATA w;
    if (WSAStartup(MAKEWORD(2, 2), &w)) return 0;
    sockaddr_in sad;
    sad.sin_family = AF_INET;
    sad.sin_addr.s_addr = inet_addr("200.20.186.76");
    sad.sin_port = htons(123);
    sockaddr saddr;
    int saddr_l = sizeof(saddr);
    int s = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
    if (s == INVALID_SOCKET) return 0;
    char msg[48] = { 8 };
    if (sendto(s, msg, sizeof(msg), 0, (sockaddr*)&sad, sizeof(sad)) == SOCKET_ERROR) return 0;
    OVERLAPPED ov{};
    ov.hEvent = CreateEvent(nullptr, TRUE, FALSE, nullptr);
    if (ov.hEvent == nullptr) return 0;
    WSABUF wsabuffer{};
    wsabuffer.buf = msg;
    wsabuffer.len = 48;
    DWORD flags = 0;
    if (WSARecvFrom(s, &wsabuffer, 1, nullptr, &flags, &saddr, &saddr_l, &ov, nullptr) == SOCKET_ERROR)
    {
        DWORD gle = WSAGetLastError();
        if (gle != WSA_IO_PENDING) return 0;
    }
    for (DWORD recv_count = 0; recv_count < 6; ++recv_count)
    {
        DWORD wait = WaitForSingleObject(ov.hEvent, 10000);
        if (wait == WAIT_FAILED) return 0;
        if (wait == WAIT_OBJECT_0) break; // WSARecvFrom completed
        if (wait == WAIT_TIMEOUT) continue; // WSARecvFrom is still pended waiting for data
    }
    // assuming WSARecvFrom completed - i.e. ov.hEvent was signaled
    DWORD transferred;
    if (WSAGetOverlappedResult(s, &ov, &transferred, FALSE, &flags))
    {
        // WSARecvFrom completed successfully - 'transferred' shows the # of bytes that were received
    }
    else
    {
        DWORD gle = WSAGetLastError();
        gle;
        // WSARecvFrom failed with the error code in 'gle'
    }
    if (closesocket(s) == SOCKET_ERROR) return 0;
    // with real code, we must guarantee that hEvent is set after calling closesocket
    // e.g. if we get here in an error path
    // closesocket() won't guarantee all async IO has completed before returning
    WaitForSingleObject(ov.hEvent, INFINITE);
    if (WSACleanup()) return 0;
    return 0;
}

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to Vigges Developer Community for programmer and developer-Open, Learning and Share
...