【发布时间】: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 范围内。