WinSock 2.2 TCP / IPv4 send()始终返回发送的所有字节

时间:2016-10-04 19:58:15

标签: c++ c sockets send

WinSock 2.2 send()函数总是返回我想要发送的所有字节!我在端口80上连接到google.com并发送随机t字母的数据。我甚至尝试发送多达1GB的t。它仍然返回所有发送的字节。我希望它能和我一起返回它可以容纳在数据包中的字节数,其余部分将由实现中的while循环处理,但它永远不会进入while循环内部!

我正在使用TCP / IPv4套接字。我知道我的互联网没有快速点亮,比我闪烁的速度快1GB。

此代码显示最重要的块。例如,省略了C字符串发送调用的包装器,它调用了我显示的发送。

调用测试代码

//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));

发送实施

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;
}

编辑1

因为这里开始混淆人们是包装

发送包装

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

编辑2

这是一个可以调试的完整工作示例。它产生相同的结果,它只是立即报告发送的所有字节。

#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;
}

编辑3

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

  

说明

     

...

     

成功完成发送功能不会   表明数据已成功交付并收到   接受者。此功能仅表示数据已成功   的发送

但@Karsten Koop指出了相关行为的类似问题: What is the size of a socket send buffer in Windows? 我很难理解那里有什么东西。但我得到的是它说该函数只是写入缓冲区并返回字节“已发送”。但这不仅意味着它不能保证接收方接收到数据(微软的状态),但这意味着它实际上根本没有发送......只是在缓冲区中。我在这里遗漏了什么,或者微软是否误导了它的行为?

1 个答案:

答案 0 :(得分:0)

有多种类型的套接字:Winsock支持多种协议。

仅仅因为socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)的当前实现通常在所有数据发送之前都会阻塞,但并不意味着其他套接字类型也会阻塞。

此外,如果进程中断,send可能发送少于完整缓冲区的内容,例如通过网络中断,或者如果套接字从另一端或另一个线程关闭。

此外,Winsock API旨在与BSD和Posix套接字API兼容,如果在呼叫期间收到信号,send通常会返回错误EINTR。 (在Unixland中,大多数阻塞调用都可以使用errno==EINTR返回,这就解决了在单线程系统上处理外部事件的问题。)