C++ internet关闭时如何终止windows套接字?(C+;+;WinAPI)

C++ internet关闭时如何终止windows套接字?(C+;+;WinAPI),c++,winapi,winsock,winsock2,C++,Winapi,Winsock,Winsock2,我已经建立了Winsock2连接,但我需要处理internet断开的情况。这是我的密码 #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;

我已经建立了Winsock2连接,但我需要处理internet断开的情况。这是我的密码

#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;
}
#包括
#包括
#包括
int main()
{
WSADATA w;
if(WSAStartup(MAKEWORD(2,2),&w))返回0;
sockaddr_在悲伤中;
悲哀的是,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=套接字(PF_INET、SOCK_DGRAM、IPPROTO_UDP);
如果(s==无效的_套接字),则返回0;
char msg[48]={8};
如果(sendto(s,msg,sizeof(msg),0,(sockaddr*)&sad,sizeof(sad))==SOCKET\u ERROR)返回0;
if(recvfrom(s,msg,48,0,&saddr,&saddr\u l)=套接字错误)返回0;
if(closesocket)=SOCKET\u错误)返回0;
if(WSACleanup())返回0;
返回0;
}
在这里,它等待调用按文档记录的方式返回。我有两个问题

  • 我可以像使用
    select
  • 我还能如何防止等待并使其立即返回?文件指出:
  • 当发出阻止Winsock调用(如sendto)时,Winsock可能需要等待网络事件,然后才能完成调用。在这种情况下,Winsock会执行可警报的等待,这可能会被安排在同一线程上的异步过程调用(APC)中断


    如何做到这一点?

    如果您想发出recvfrom()并让它立即返回,然后自行决定等待多长时间(我假设Windows包括winsock2.h),您可以发出异步重叠请求,然后通过等待重叠结构的hEvent成员发出信号,随时等待完成

    这是一个基于原始代码的更新示例

    • 通过使用WaitForSingleObject,您可以根据需要等待多长时间来设置超时(下面我等待了10秒6次)
    • 通过传递重叠指针,表示您将自己等待完成。请注意,在发出hEvent信号之前,重叠结构不能超出范围。(如果动态分配了重叠,则释放)
    • 在保证IO完成之前让重叠超出范围是一个常见的Winsock错误(我在Winsock上工作了十多年——我看到了这个错误的许多变体)
    • 正如下面所评论的,如果您不知道hEvent已发出信号,那么在调用closesocket之后,您必须等待hEvent发出信号,然后才能继续-closesocket不保证在返回之前所有异步IO请求都已完成
    \define\u WINSOCK\u已弃用\u无\u警告
    #包括
    #包括
    #包括
    int main()
    {
    WSADATA w;
    if(WSAStartup(MAKEWORD(2,2),&w))返回0;
    sockaddr_在悲伤中;
    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=套接字(PF_INET、SOCK_DGRAM、IPPROTO_UDP);
    如果(s==无效的_套接字),则返回0;
    char msg[48]={8};
    如果(sendto(s,msg,sizeof(msg),0,(sockaddr*)&sad,sizeof(sad))==SOCKET\u ERROR)返回0;
    重叠ov{};
    ov.hEvent=CreateEvent(nullptr、TRUE、FALSE、nullptr);
    if(ov.hEvent==nullptr)返回0;
    WSABUF wsabuffer{};
    wsabuffer.buf=msg;
    wsabuffer.len=48;
    DWORD标志=0;
    if(WSARecvFrom(s,&wsabuffer,1,nullptr,&flags,&saddr,&saddr_l,&ov,nullptr)=套接字错误)
    {
    DWORD gle=WSAGetLastError();
    如果(gle!=WSA_IO_PENDING)返回0;
    }
    对于(DWORD recv_计数=0;recv_计数<6;++recv_计数)
    {
    DWORD wait=WaitForSingleObject(ov.hEvent,10000);
    如果(wait==wait_FAILED)返回0;
    如果(wait==wait\u OBJECT\u 0)中断;//WSARecvFrom已完成
    如果(wait==wait\u TIMEOUT)继续;//WSARecvFrom仍挂起等待数据
    }
    //假设WSARecvFrom completed-即ov.hEvent已发出信号
    德沃德被转移;
    if(WSAGetOverlappedResult(s、&ov、&Transfer、FALSE和标志))
    {
    //WSARecvFrom completed successfully—“Transfer”显示已接收的字节数
    }
    其他的
    {
    DWORD gle=WSAGetLastError();
    gle;
    //WSARecvFrom失败,错误代码为“gle”
    }
    if(closesocket)=SOCKET\u错误)返回0;
    //对于实际代码,我们必须保证在调用closesocket后设置hEvent
    //例如,如果我们在错误路径中到达这里
    //closesocket()不能保证在返回之前所有异步IO都已完成
    WaitForSingleObject(ov.hEvent,无限);
    if(WSACleanup())返回0;
    返回0;
    }
    
    AFAIK您无法检测到UDP套接字的连接丢失,因为它是无连接的。你需要在这个问题上采取一些变通办法。@Ultimal,好的,谢谢。我将首先尝试在某个位置ping,或者考虑其他内容。使用读取超时,使用
    setsockopt()
    所以\u RCVTIMEO
    @user207421我已经看过了这些文档页面,但它们需要像TCP这样的面向连接的协议。我使用的是UDP,它是无连接的,因此这些方法可能无法工作。“我可以像使用
    select
    时那样设置超时吗?”-您可以将
    select()
    与UDP套接字一起使用。仅供参考,
    SO_RCVTIMEO
    也适用于UDP。感谢您的回答,我现在正在尝试。顺便说一句,我有个问题,如果我们不在节目结束时等待hEvent,会发生什么?它是否会导致未定义的行为、内存泄漏或崩溃?如果它没有引起这样的问题,那又有什么关系呢?在本例中,不需要第二次等待事件,因为第一次等待已经保证了事件的设置。当一个总超时的调用可以完成完全相同的事情时,以这种方式在循环中调用
    WaitForSingleObject()
    是完全没有必要的。但是,此代码是一个
    #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;
    }