2

WinSock 2.2 send() function always returns me all the bytes I want it to send! I am connecting to google.com on port 80 and sending random t letters for data. I even tried to send as many as 1GB of t's. It still returns with all bytes sent. I was expecting it to return back with me with how many bytes it could fit in a packet and the rest would be handled by the while loop in the implementation, but it never enters inside the while loop as a result!

I am using TCP/IPv4 sockets. I know my internet is not lighting fast to send 1GB faster than I can blink.

This code is shown for most important chunks. For example a wrapper for the C-string send call is omitted that calls the send I shown.

Calling test code

//prepare the long data string
int buffSize = 1 * 1000 * 1000 * 1000;
char *buff = new char[buffSize];
memset(buff, 't', buffSize);
buff[buffSize - 3] = '\n';
buff[buffSize - 2] = '\n';
buff[buffSize - 1] = '\0';

//send thee data
int bytesSent = socket->send(buff);

//verify all thee data is sent
CHECK(bytesSent == strlen(buff));

Send implementatian

int mySocket::send(const void *data, int length)
{
    int bytesSent = ::send(socketDescriptor, (char*)data, length, 0);

    if (bytesSent == SOCKET_ERROR) return -1;

    while(bytesSent < length)
    {
        int sent = ::send(socketDescriptor, (char*)data + bytesSent, length - bytesSent, 0);

        if (sent == SOCKET_ERROR) return bytesSent;

        bytesSent += sent;
    }

    return bytesSent;
}

EDIT 1

Since it started to confuse people here is the wrapper

Send wrapper

int mySocket::send(const char * data_string)
{
    return send(dataString, strlen(data_string));
}

EDIT 2

here is a full working example you can debug. it produces the same result, it just instantly reports all bytes as sent.

#include <WinSock2.h>
#include <WS2tcpip.h>
#include <stdio.h>
#pragma comment(lib, "ws2_32.lib")

class mySocket
{
public:

    mySocket(SOCKET sockeDesc)
    {
        socketDescriptor = sockeDesc;
    }

    int mySocket::send(const void *data, int length)
    {
        int bytesSent = ::send(socketDescriptor, (char*)data, length, 0);

        if (bytesSent == SOCKET_ERROR) return -1;

        while (bytesSent < length)
        {
            int sent = ::send(socketDescriptor, (char*)data + bytesSent, length - bytesSent, 0);

            if (sent == SOCKET_ERROR) return bytesSent;

            bytesSent += sent;
        }

        return bytesSent;
    }

    int mySocket::send(const char * data_string)
    {
        return send(data_string, strlen(data_string));
    }
private:
    SOCKET socketDescriptor;
};

int main()
{
    WSAData wsd;
    if (WSAStartup(MAKEWORD(2, 2), &wsd) || wsd.wVersion != MAKEWORD(2, 2))
    {
        WSACleanup();
        return 1;
    }

    //create ze socket
    SOCKET socketDescriptor = socket(AF_INET, SOCK_STREAM, 0);

    //look up socket info
    addrinfo hints;
    memset(&hints, 0, sizeof(hints));
    hints.ai_family = AF_INET;
    hints.ai_socktype = SOCK_STREAM;

    addrinfo *res;
    int errCode = getaddrinfo("google.com", "80", &hints, &res);
    if (errCode) return 1;

    //connect ze socket
    int connectStatusCode = ::connect(socketDescriptor, res->ai_addr, res->ai_addrlen);
    if (connectStatusCode == SOCKET_ERROR) return 1;

    //create zeeeee socket!
    mySocket *socket = new mySocket(socketDescriptor);

    //prepare ze long data string
    int buffSize = 1 * 1000 * 1000 * 1000;
    char *buff = new char[buffSize];
    memset(buff, 't', buffSize);
    buff[buffSize - 3] = '\n';
    buff[buffSize - 2] = '\n';
    buff[buffSize - 1] = '\0';

    //send ze data
    int bytesSent = socket->send(buff);

    //verify all ze data is sent
    bool success = (bytesSent == strlen(buff));

    printf("requested bytes = %i\nsent bytes = \t  %i", strlen(buff), bytesSent);
    printf("\n\nsuccess = %s", success ? "true" : "false");

    closesocket(socketDescriptor);
    delete socket;
    freeaddrinfo(res);
    WSACleanup();
    getchar();
    return 0;
}

EDIT 3

https://msdn.microsoft.com/en-us/library/windows/desktop/ms740506(v=vs.85).aspx

Remarks

...

The successful completion of a send function does not indicate that the data was successfully delivered and received to the recipient. This function only indicates the data was successfully sent.

but @Karsten Koop, pointed to similar question on related behaviour: What is the size of a socket send buffer in Windows? i badly understand what's writtent there. but what i got is it says the function simply writes to a buffer and returns the bytes "sent". but this not only means it doesn't guarantee that the recepient received the data(which microsoft states) BUT it means it isn't actually sent at all... just in a buffer. am i missing something here or is microsoft misleading about its behaviour?

Community
  • 1
  • 1
Infogeek
  • 93
  • 1
  • 8
  • `int bytesSent = socket->send(buff);` you forgot to specify length. It was likely filled as 0, so the compiler optimized away the buffer (since you never used it), then send successfully sent exactly 0 bytes. Try passing length, maybe it will work? – LambdaBeta Oct 04 '16 at 20:07
  • `socket->send(buff, strlen(buff));` – Raindrop7 Oct 04 '16 at 20:09
  • @LambdaBeta no. there is a wrapper for that. i have update the question with it. – Infogeek Oct 04 '16 at 20:20
  • 4
    send() on a TCP socket by default blocks until all data is sent or an error occurs, it does not only send what would fit in one "packet". However, it's not really possible for us to debug this code - create a complete example that someone can compile and run for themselves. – nos Oct 04 '16 at 20:28
  • 1
    @nos you are wrong accrosing to microsoft. https://msdn.microsoft.com/en-us/library/windows/desktop/ms740149(v=vs.85).aspx under RETURN section "If no error occurs, send returns the total number of bytes sent, which can be less than the number requested to be sent in the len parameter." Also i have provided a full example to debug as you said – Infogeek Oct 04 '16 at 22:10
  • 2
    The return value of a successful send *can* be less than the number of bytes requested, for various reasons. It can also be the full amount. The documentation explicitly mentions the possibility of "short writes", because it is a common mistake to overlook that possibility. (A lot of people who don't read documentation carefully assume that the only possible return values are error and full-length success) –  Oct 05 '16 at 03:03
  • 1
    This is indeed the behaviour of Winsock. You can pass up to several GB to the send function, and the function will return immediatly, copying the data to internal buffers, if you have enough RAM. This is different to e.g. Linux, where you can specify the size of the send buffer as a kernel parameter, and is usually in the ~100 KB range. – Karsten Koop Oct 05 '16 at 11:14
  • 1
    [Here is a similar question](http://stackoverflow.com/questions/28785626/what-is-the-size-of-a-socket-send-buffer-in-windows) – Karsten Koop Oct 05 '16 at 11:15
  • @LambdaBeta The compiler can neither see that it was filled with all nulls nor optimise away any such call. – user207421 Oct 05 '16 at 11:18
  • @Infogeek It can be less in non-blocking mode. Not otherwise. This is guaranteed by Posix, with which Windows complies. It's also mentioned in the Remarks of your citation. – user207421 Oct 05 '16 at 11:20

1 Answers1

0

There is more than one type of socket: Winsock supports multiple protocols.

Just because the current implementation of socket(AF_INET, SOCK_STREAM, IPPROTO_TCP) generally seems to block until all data is sent, doesn't mean that other socket types will also block.

In addition, send may send less than the full buffer if the process is interrupted e.g. by a network outage, or if the socket is closed either from the other end or by another thread.

Also, the Winsock API is designed to be compatible with the BSD and Posix sockets APIs, and on those platforms send will generally return with error EINTR if a signal is received during the call. (In Unixland most blocking calls can return with errno==EINTR, this is how the problem of handling external events on a single-threaded system was solved).

Ben
  • 34,935
  • 6
  • 74
  • 113