UDP - Closesocket() takes upto 5 seconds to return in disconnect/remote host down scenario due to pending data to send

The following write up from the support lines comes to us by one of the senior and key resources in Windows SDK EMEA/INDIA team, Nitin Dhawan

Socket(DATAGRAM…);

Sendto(….);

Closesocket();  

The above works just fine, except the scenario when remote host is down, or just that the cable is unplugged.

The socket() and sendto() calls would return immediately but closesocket() will take time to return.

Please note the reason is, while doing a closesocket(), because there is pending data to be sent due to previous sendto() call, the network stack will try to send the data again.

It will check the ARP entry to get the target MAC address, which is most probably not there because if that was the dynamic entry in the ARP table, that is removed by now. The network stack retries and then return back, but it may take upto 5 seconds to return.

NOTE:- This is by design since implementation on Windows Vista. Nitin tested it on Windows 7 onwards personally.

Workaround: The delay is because network stack is looking for pending data to be sent, it was not sent because there was no entry in the ARP table for the target as it was dynamic entry. If we have a static entry for the target in the ARP table, then data will be sent and closesocket() would not block. This workaround is feasible only when the targets are limited in closed network. One can use arp commands to make a static entry.

Arp –d                  ( clear the entries, if needed)

Arp –s IP_Address  MAC_Address           ( add static entry, IP and MAC address of target machine ) 

Or, you can use IP Helper function to add such entry. 

The below code can repro the issue.

// Udpsend.cpp : Defines the entry point for the console application.

// 

#include "stdafx.h"

#ifndef UNICODE

#define UNICODE

#endif

#define WIN32_LEAN_AND_MEAN

#include <winsock2.h>

#include <Ws2tcpip.h>

#include <stdio.h>

// Link with ws2_32.lib

#pragma comment(lib, "Ws2_32.lib")

int main()

{

    int iResult;

    WSADATA wsaData;

    SOCKET SendSocket = INVALID_SOCKET;

    sockaddr_in RecvAddr;

    unsigned short Port = 27015;

    char SendBuf[1024]="This is the data";

    int BufLen = 1024;

    //----------------------

    / / Initialize Winsock

    iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);

    if (iResult != NO_ERROR) {

        wprintf(L"WSAStartup failed with error: %d\n", iResult);

        return 1;

    }

 

    //---------------------------------------------

    // Create a socket for sending data

    SendSocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);

    if (SendSocket == INVALID_SOCKET) {

        wprintf(L"socket failed with error: %ld\n", WSAGetLastError());

        WSACleanup();

        return 1;

    }

    //---------------------------------------------

    // Set up the RecvAddr structure with the IP address of

    // the receiver (in this example case "192.168.1.1")

    // and the specified port number.

    RecvAddr.sin_family = AF_INET;

    RecvAddr.sin_port = htons(Port);

    RecvAddr.sin_addr.s_addr = inet_addr("192.168.1.121");  // <--- change the IP Address

 

   //---------------------------------------------

    // Send a datagram to the receiver

    wprintf(L"Sending a datagram to the receiver...\n");

    iResult = sendto(SendSocket,

                     SendBuf, BufLen, 0, (SOCKADDR *) & RecvAddr, sizeof (RecvAddr));

    if (iResult == SOCKET_ERROR) {

        wprintf(L"sendto failed with error: %d\n", WSAGetLastError());

        closesocket(SendSocket);

        WSACleanup();

        return 1;

    }

    //---------------------------------------------

    // When the application is finished sending, close the socket.

    wprintf(L"Finished sending. Closing socket.\n");

      DWORD start = GetTickCount();

    iResult = closesocket(SendSocket);

      DWORD end = GetTickCount();

      wprintf(L"closesocket took %d ms\n", end-start);

    if (iResult == SOCKET_ERROR) {

        wprintf(L"closesocket failed with error: %d\n", WSAGetLastError());

        WSACleanup();

        return 1;

    }

    //---------------------------------------------

    // Clean up and quit.

    wprintf(L"Exiting.\n");

    WSACleanup();

    return 0;

}

Written by: Nitin Dhawan

Reviewed by: Jeff Lambert