1

I'm using VC++ on Visual Studio 2010.
Now I've got some problem. I think it's so silly question but I want to get a clear answer.

How can I get CLOSE_WAIT state of an selected SOCKET?

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

#include "stdafx.h"
#include <WinSock.h>
#include <Windows.h>

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

#define PR_RECORED_TIME 10*1000 // (ms)

BYTE* pByteCamData = NULL;
INT nHeight = 900;
INT nWidth = 1600;
INT nSpect = 3;
INT nSolution = nHeight * nWidth * nSpect;

VOID SendRecoredData(SOCKET socket2operation);

int _tmain(int argc, _TCHAR* argv[])
{
    pByteCamData = new BYTE[nSolution]; // <-- use [], not ()!

    //----------------------
    // Initialize Winsock.
    WSADATA wsaData;
    int iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
    if (iResult != NO_ERROR) {
        wprintf(L"WSAStartup failed with error: %d\n", iResult);
        delete[] pByteCamData;
        return 1;
    }

    //----------------------
    // Create a SOCKET for listening for
    // incoming connection requests.
    SOCKET ListenSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (ListenSocket == INVALID_SOCKET) {
        wprintf(L"socket failed with error: %d\n", WSAGetLastError());
        WSACleanup();
        delete[] pByteCamData;
        return 1;
    }

    //----------------------
    // The sockaddr_in structure specifies the address family,
    // IP address, and port for the socket that is being bound.
    sockaddr_in service = {};
    service.sin_family = AF_INET;
    service.sin_addr.s_addr = INADDR_ANY;   // inet_addr("127.0.0.1");
    service.sin_port = htons(27015);

    if (bind(ListenSocket, (SOCKADDR *) &service, sizeof(service)) == SOCKET_ERROR) {
        wprintf(L"bind failed with error: %d\n", WSAGetLastError());
        closesocket(ListenSocket);
        WSACleanup();
        delete[] pByteCamData;
        return 1;
    }

    //----------------------
    // Listen for incoming connection requests.
    // on the created socket
    if (listen(ListenSocket, 1) == SOCKET_ERROR) {
        wprintf(L"listen failed with error: %d\n", WSAGetLastError());
        closesocket(ListenSocket);
        WSACleanup();
        delete[] pByteCamData;
        return 1;
    }

    //----------------------
    // Accept the connection.
    wprintf(L"Waiting for client to connect...\n");
    SOCKET AcceptSocket = accept(ListenSocket, NULL, NULL);
    if (AcceptSocket == INVALID_SOCKET) {
        wprintf(L"accept failed with error: %d\n", WSAGetLastError());
        closesocket(ListenSocket);
        WSACleanup();
        delete[] pByteCamData;
        return 1;
    }

    wprintf(L"Client connected.\n");
    SendRecoredData(AcceptSocket); // <-- logic fix!

    // No longer need client socket
    closesocket(AcceptSocket); // <-- CLOSE_WAIT fix!

    // No longer need server socket
    closesocket(ListenSocket);

    WSACleanup();

    delete[] pByteCamData;

    return 0;
}

VOID SendRecoredData(SOCKET socket2operation)
{
    if(IsSocketAlive(socket2operation) == 0)
        return;

    INT nCountDown = 5;
    INT nSentData, nNumToSend;
    BYTE *pData;

    do
    {
        if (nCountDown == 0)
        {
            nCountDown = 5;

            pData = pByteCamData;
            nNumToSend = nSolution;

            while (nNumToSend > 0) <-- send() fix!
            {
                nSentData = send(socket2operation, (char*)pData, nNumToSend, 0);
                if (SOCKET_ERROR == nSentData) {
                    wprintf(L"send failed with error: %d\n", WSAGetLastError());
                    return;
                }
                pData += nSentData;
                nNumToSend -= nSentData;
            }

            wprintf(L"Sent Camera Data OK [%d] Bytes\n", nSolution);
        }

        Sleep(PR_RECORED_TIME);
        --nCountDown;
    }
    while (TRUE);
}
INT IsSocketAlive(SOCKET socket2check)
{
    if (socket2check == INVALID_SOCKET)
        return FALSE;

    INT nError_code = -1;
    INT nError_code_size = sizeof(nError_code);
    INT nRetValue = getsockopt(socket2check, SOL_SOCKET, SO_ERROR, (CHAR*)&nError_code, &nError_code_size);

//  if (nRetValue != -1)
    {
        //      _tprintf(_T("Error getting socket error code : %d \n"), strerror(nRetValue));
    }

    if (nError_code != 0)
    {
        _tprintf(_T("Socket error : %d \n"), strerror(nError_code));
    }

    switch (nError_code)
    {
    case 0:
        return TRUE;
        break;
    case SOCKET_ERROR:
        return FALSE;
    default:
        return FALSE;
    }
}

A problem is CLOSE_WAIT 'ed' socket is checked as alive socket.
When SocketThreadConn.exe is working on while loop, it doesn't break loop though Listen socket is CLOSE_WAIT.

How can I check accepted socket is ESTABLISHED?

(This code has changed by @Remy Lebeau)

How can I check is a socket is still ESTABLISHED not using send() or GetTcpTable().

Function GetTcpTable() is working well but I have to find socket only using their value(USHORT). There is no field for socket value in MIB_TCPTABLE.

Ken Y-N
  • 14,644
  • 21
  • 71
  • 114
Kaizen
  • 139
  • 10
  • 'Listen socket is CLOSE_WAIT' is completely and utterly meaningless. Only a connected socket can be in that state. – user207421 Nov 21 '17 at 10:22
  • 1
    [Why is it impossible, without attempting I/O, to detect that TCP socket was gracefully closed by peer?](https://stackoverflow.com/questions/155243/why-is-it-impossible-without-attempting-i-o-to-detect-that-tcp-socket-was-grac) – user207421 Nov 22 '17 at 03:15

3 Answers3

1
SendRecoredData(ListenSocket);

That should be

SendRecoredData(AcceptSocket);

which would have been causing an error in send() that you haven't told us about, and you are never closing AcceptSocket. That's what's causing the CLOSE_WAIT states. You don't need to look for them explicitly. Just fix your bugs.

user207421
  • 305,947
  • 44
  • 307
  • 483
1

How can I get CLOSE_WAIT state of an selected SOCKET?

There is no Winsock API for that particular purpose. However, you can retrieve the SOCKET's two IP/port pairs (via getsockname() and getpeername()) and then look for them in the output of GetTcpTable() or related function. That will give you the current state, and even the owning process.

But, there is a bug in your code that is causing the CLOSE_WAIT state - you are not closing the SOCKET returned by accept(). CLOSE_WAIT means an established connection received a FIN from the other peer, and is waiting for you to send a FIN and close your SOCKET handle for the connection.

state diagram

Fix that bug, and you won't need to resort to using GetTcpTable() at all.

How can I check accepted socket is ESTABLISHED?

You could use GetTcpTable() for that as well. However, the best way is to just perform actual I/O over the connection and see if it succeeds or fails. Your IsSocketAlive() function is incorrect and useless as-is. Use the return value from send() to know if the socket is still alive (since you are not reading any data from the client).


With that said, there are other bugs in your code, too.

You are not allocating pByteCamData correctly. You need to use [] instead of (). You are allocating a single BYTE with nSolution as its value, not an array of nSolution number of bytes.

You are passing the listening SOCKET to SendRecoredData(), but you should be passing the SOCKET from accept() instead.

send() is not guaranteed to send all of the requested bytes in a single go. It can send fewer bytes than requested (which is likely since you are trying to send > 4MB at a time). If the return value is less than the number of bytes requested, you have to call send() again to send the remaining bytes. So, call send() in a loop until there are no more bytes to send.

Try something more like this:

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

#include "stdafx.h"
#include <WinSock.h>
#include <Windows.h>

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

#define PR_RECORED_TIME 10*1000 // (ms)

BYTE* pByteCamData = NULL;
INT nHeight = 900;
INT nWidth = 1600;
INT nSpect = 3;
INT nSolution = nHeight * nWidth * nSpect;

VOID SendRecoredData(SOCKET socket2operation);

int _tmain(int argc, _TCHAR* argv[])
{
    pByteCamData = new BYTE[nSolution]; // <-- use [], not ()!

    //----------------------
    // Initialize Winsock.
    WSADATA wsaData;
    int iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
    if (iResult != NO_ERROR) {
        wprintf(L"WSAStartup failed with error: %d\n", iResult);
        delete[] pByteCamData;
        return 1;
    }

    //----------------------
    // Create a SOCKET for listening for
    // incoming connection requests.
    SOCKET ListenSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (ListenSocket == INVALID_SOCKET) {
        wprintf(L"socket failed with error: %d\n", WSAGetLastError());
        WSACleanup();
        delete[] pByteCamData;
        return 1;
    }

    //----------------------
    // The sockaddr_in structure specifies the address family,
    // IP address, and port for the socket that is being bound.
    sockaddr_in service = {};
    service.sin_family = AF_INET;
    service.sin_addr.s_addr = INADDR_ANY;   // inet_addr("127.0.0.1");
    service.sin_port = htons(27015);

    if (bind(ListenSocket, (SOCKADDR *) &service, sizeof(service)) == SOCKET_ERROR) {
        wprintf(L"bind failed with error: %d\n", WSAGetLastError());
        closesocket(ListenSocket);
        WSACleanup();
        delete[] pByteCamData;
        return 1;
    }

    //----------------------
    // Listen for incoming connection requests.
    // on the created socket
    if (listen(ListenSocket, 1) == SOCKET_ERROR) {
        wprintf(L"listen failed with error: %d\n", WSAGetLastError());
        closesocket(ListenSocket);
        WSACleanup();
        delete[] pByteCamData;
        return 1;
    }

    //----------------------
    // Accept the connection.
    wprintf(L"Waiting for client to connect...\n");
    SOCKET AcceptSocket = accept(ListenSocket, NULL, NULL);
    if (AcceptSocket == INVALID_SOCKET) {
        wprintf(L"accept failed with error: %d\n", WSAGetLastError());
        closesocket(ListenSocket);
        WSACleanup();
        delete[] pByteCamData;
        return 1;
    }

    wprintf(L"Client connected.\n");
    SendRecoredData(AcceptSocket); // <-- logic fix!

    // No longer need client socket
    closesocket(AcceptSocket); // <-- CLOSE_WAIT fix!

    // No longer need server socket
    closesocket(ListenSocket);

    WSACleanup();

    delete[] pByteCamData;

    return 0;
}

VOID SendRecoredData(SOCKET socket2operation)
{
    INT nCountDown = 5;
    INT nSentData, nNumToSend;
    BYTE *pData;

    do
    {
        if (nCountDown == 0)
        {
            nCountDown = 5;

            pData = pByteCamData;
            nNumToSend = nSolution;

            while (nNumToSend > 0) <-- send() fix!
            {
                nSentData = send(socket2operation, (char*)pData, nNumToSend, 0);
                if (SOCKET_ERROR == nSentData) {
                    wprintf(L"send failed with error: %d\n", WSAGetLastError());
                    return;
                }
                pData += nSentData;
                nNumToSend -= nSentData;
            }

            wprintf(L"Sent Camera Data OK [%d] Bytes\n", nSolution);
        }

        Sleep(PR_RECORED_TIME);
        --nCountDown;
    }
    while (TRUE);
}
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • Thank you for your kindness. Is there any API function to get state of an selected socket except `GetTcpTable()`? If it is possible, I can get state easily like this. ex:) `socket2accept` // = 182 (ex), `if(SOCKET_ESTABLISHED != GetState(socket2accept)){...}`. Have you got function like `GetState(socket)`? Thank you in advance. – Kaizen Nov 21 '17 at 00:37
  • 1
    @Kaizen: "*Is there any API function to get state of an selected socket?*" - as I said in the **very first sentence** of my answer: "*There is no Winsock API for that particular purpose.*" There is NO GOOD REASON to query the socket state like you are asking for. IT IS NOT NECESSARY, if you write your socket code the proper way to begin with. – Remy Lebeau Nov 21 '17 at 02:20
1
#define SOCKET_ALIVE 10
#define SOCKET_DEAD 9

INT CheckSocketAlive(SOCKET socket2check)
{
    fd_set  fdR;
    struct  timeval timeout;
    timeout.tv_sec = 1;
    timeout.tv_usec = 0;
    FD_ZERO(&fdR);  
    FD_SET(socket2check, &fdR);
    switch (select(socket2check + 1, &fdR, NULL,NULL, &timeout))
    {  
    case SOCKET_ERROR:
        _tprintf(_T("\tError condition. Code : [%d]\n"), WSAGetLastError());
        return SOCKET_DEAD;
    case 0:
        return SOCKET_ALIVE;
    default:
        if ( FD_ISSET(socket2check, &fdR) )
        {
            _tprintf(_T("A read event has occurred on socket [socket2check].\n"));
            return SOCKET_DEAD;
        }
        break;
    }
}

You can get state of selected socket using this function.
But only SOCKET_ERROR or SOCKET_ALIVE.
You can change timeout.tv_sec (1sec) as you want.

AleXelton
  • 767
  • 5
  • 27
  • Thank you very much. It works well then I can now correctly determine if the socket is connected (`ESTABLISHED` or `CLOSE_WAIT` 'ed' ). – Kaizen Nov 21 '17 at 02:04
  • @Kaizen This answer is complete and utter nonsense. A socket being readable does not mean that it's dead. This code returns a guess if there was a select error or timeout, and complete garbage in the (impossible) situation that `select()` returned a positive value but no FDs were readable. – user207421 Nov 21 '17 at 10:00
  • @EJP, I know that it seems like utter nonsense, but **Is there any way to get state of socket only by knowing socket value?** using `GetTcpTable()`? I know that there is no API function to determine the state of a socket. I think my answer is absurd, but readable means `Established`, and unreadable means `Closed` I think. Thank you for your advice. – AleXelton Nov 22 '17 at 01:38
  • @A.Godnov If there is a pending EOS on a readable socket it means CLOSE_WAIT. If there is pending data, it means ESTABLISHED. `select()` cannot tell you which. Your method cannot tell you which. Your method doesn't answer the question. Nothing does. The whole design of TCP is that you can only detect network conditions by doing I/O. There is no dial tone', by design, and therefore no API or utility method can possibly be provided to yield the information the OP wants. The real fact is that with correct network programming, as per the other answers, he doesn't need it. – user207421 Nov 22 '17 at 02:59
  • And if you think that 'readable' means 'established' why does your method return `SOCKET_DEAD` in that case? – user207421 Nov 22 '17 at 03:22