【问题标题】:WinSock 2.2 TCP/IPv4 send() always returns all bytes sentWinSock 2.2 TCP/IPv4 send() 总是返回所有发送的字节
【发布时间】:2017-02-13 02:51:29
【问题描述】:

WinSock 2.2 send() 函数总是返回我希望它发送的所有字节!我正在端口 80 上连接到 google.com 并发送随机的 t 字母以获取数据。我什至尝试发送多达 1GB 的 t's。它仍然返回所有发送的字节。我期待它返回给我一个数据包可以容纳多少字节,其余的将由实现中的 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? 我不太明白那里写的是什么。但我得到的是它说函数只是写入缓冲区并返回“发送”的字节。但这不仅意味着它不保证接收者收到数据(微软声明),而且意味着它实际上根本没有发送......只是在缓冲区中。我在这里遗漏了什么还是微软对其行为有误导?

【问题讨论】:

  • 默认情况下,TCP 套接字上的 send() 会阻塞,直到所有数据都发送完毕或发生错误,它不仅发送适合一个“数据包”的内容。但是,我们实际上无法调试此代码 - 创建一个完整的示例,让其他人可以自己编译和运行。
  • @nos 你错了微软。 msdn.microsoft.com/en-us/library/windows/desktop/… 在 RETURN 部分下“如果没有错误发生,send 返回发送的总字节数,可以小于 len 参数中请求发送的数量。”我也提供了一个完整的例子来调试你说的
  • 成功发送的返回值可以小于请求的字节数,原因有很多。也可以是全额。该文档明确提到了“短写”的可能性,因为忽略这种可能性是一个常见的错误。 (很多不仔细阅读文档的人认为唯一可能的返回值是错误和全长成功)
  • 这确实是Winsock的行为。您最多可以将几 GB 传递给 send 函数,如果您有足够的 RAM,该函数将立即返回,将数据复制到内部缓冲区。这与例如不同。 Linux,您可以将发送缓冲区的大小指定为内核参数,通常在 ~100 KB 范围内。

标签: c++ c sockets send


【解决方案1】:

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

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

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-07-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-12-01
    相关资源
    最近更新 更多