计算套接字上传速度 我想知道有没有人知道如何计算C++中伯克利套接字的上传速度。我的发送呼叫没有阻塞,发送5兆字节的数据需要0.001秒,但接收响应需要一段时间(因此我知道它正在上传)

计算套接字上传速度 我想知道有没有人知道如何计算C++中伯克利套接字的上传速度。我的发送呼叫没有阻塞,发送5兆字节的数据需要0.001秒,但接收响应需要一段时间(因此我知道它正在上传),c++,sockets,performance,winsock,C++,Sockets,Performance,Winsock,这是HTTP服务器的TCP套接字,我需要异步检查已上载/剩余的数据字节数。然而,我在Winsock中找不到任何用于此的API函数,所以我很困惑 任何帮助都将不胜感激 编辑:我已找到解决方案,将尽快发布答案 编辑2:添加为答案的正确解决方案将在4小时内添加为解决方案。您可以通过从写入套接字的字节数中减去的值来获得接收和确认的数据量下限。可以使用setsockopt调整此缓冲区,但在某些情况下,操作系统可能会选择比您指定的长度小或大的长度,因此您必须在设置后重新检查 但是,要获得更精确的结果,您必须

这是HTTP服务器的TCP套接字,我需要异步检查已上载/剩余的数据字节数。然而,我在Winsock中找不到任何用于此的API函数,所以我很困惑

任何帮助都将不胜感激

编辑:我已找到解决方案,将尽快发布答案


编辑2:添加为答案的正确解决方案将在4小时内添加为解决方案。

您可以通过从写入套接字的字节数中减去的值来获得接收和确认的数据量下限。可以使用
setsockopt
调整此缓冲区,但在某些情况下,操作系统可能会选择比您指定的长度小或大的长度,因此您必须在设置后重新检查

但是,要获得更精确的结果,您必须让远程端通知您进度,因为winsock不会公开API来检索发送缓冲区中当前挂起的数据量


或者,您也可以在UDP上实现自己的传输协议,但实现这种协议的速率控制可能非常复杂。

如果您的应用程序使用类似于

0001234DT

其中一个包的包长度为000123,您可以考虑使用MSGPEPEKE+RVE()来获取包的长度,然后再使用ReVo(./P>)读取它。 问题是send()并没有按照您的想法执行—它是由内核缓冲的

getsockopt(sockfd, SOL_SOCKET, SO_SNDBUF, &flag, &sz));
fprintf(STDOUT, "%s: listener socket send buffer = %d\n", now(), flag);
sz=sizeof(int);
ERR_CHK(getsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, &flag, &sz));
fprintf(STDOUT, "%s: listener socket recv buffer = %d\n", now(), flag);
看看这些给你带来了什么

当您在具有数据的非阻塞套接字上进行recv时,它通常不会在buufer中存储MB的数据以准备进行recv。我所经历的大部分情况是,套接字每个recv大约有1500字节的数据。由于您可能正在读取阻塞套接字,因此recv()需要一段时间才能完成

套接字缓冲区大小可能是套接字吞吐量的最佳预测值。setsockopt()允许您在一定程度上更改套接字缓冲区大小。注意:这些缓冲区在许多操作系统(如Solaris)中的套接字之间共享。过度旋转这些设置可能会降低性能

另外,我不认为你在衡量你认为你在衡量的东西。send()的实际效率是对recv()端吞吐量的度量。不是send()结尾。
依我看,

由于您无法控制远程端,并且您希望在代码中实现这一点,因此我建议您执行非常简单的近似操作。我假设有一个长寿的计划/关系。一次上传会因为ARP、DNS查找、套接字缓冲、TCP慢启动等因素而变得过于倾斜

有两个计数器-未完成队列的长度(字节)(OB)和发送的字节数(SB):

  • 每次将块排入队列进行上载时,按要发送的字节数递增OB
  • 按从发送(2)(模数
    -1
    案例)返回的数字,递减OB并递增SB
  • 在计时器样本上,OB和SB-存储它们、记录它们或计算运行平均值
  • 每秒/分钟/任何时间计算未完成字节,发送字节也是如此
网络堆栈执行缓冲,TCP执行重传和流控制,但这并不重要。这两个计数器将告诉您应用程序生成数据的速率,以及将数据推送到网络的速率。这不是一种了解实际链接速度的方法,而是一种保存应用程序运行情况有用指标的方法

如果数据生成速率低于网络输出速率,则一切正常。如果是另一种情况,而网络跟不上应用程序的速度,那么就有问题了,你要么需要更快的网络,要么需要更慢的应用程序,要么需要不同的设计

对于一次性实验,只需定期拍摄
netstat-sptcp
output(或Windows上的任何内容)的快照,并手动计算发送速率


希望这能有所帮助。

多亏了bdolan建议减少
SO\u SNDBUF
,我解决了我的问题。但是,要使用此代码,您必须注意您的代码使用Winsock 2(用于重叠套接字和
WSASend
)。除此之外,您的
套接字
句柄的创建方式必须与以下类似:

SOCKET sock = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, WSA_FLAG_OVERLAPPED);
请注意,
WSA\u标志\u重叠
标志作为最终参数

在这个回答中,我将经历将数据上传到TCP服务器的各个阶段,并跟踪每个上传块及其完成状态。这个概念需要将上传缓冲区分成块(所需的现有代码修改最少),然后逐块上传,然后跟踪每个块

我的代码流 全局变量 您的代码文档必须具有以下全局变量:

#define UPLOAD_CHUNK_SIZE 4096

int g_nUploadChunks = 0;
int g_nChunksCompleted = 0;
WSAOVERLAPPED *g_pSendOverlapped = NULL;
int g_nBytesSent = 0;
float g_flLastUploadTimeReset = 0.0f;
注意:在我的测试中,减小
UPLOAD\u CHUNK\u SIZE
会提高上传速度和准确性,但会降低总体上传速度。增加
UPLOAD\u CHUNK\u SIZE
会降低上传速度和准确性,但会提高总体上传速度。4KB(4096字节)对于大小约500kB的文件来说是一个很好的压缩

回调函数 此函数增加发送的字节数和chunk completed变量(在chunk完全上传到服务器后调用)

准备插座 首先,必须通过将
SO_SNDBUF
减少到0来准备套接字

注意:在我的测试中,任何大于0的值都将导致不良行为

int nSndBuf = 0;
setsockopt(sock, SOL_SOCKET, SO_SNDBUF, (char*)&nSndBuf, sizeof(nSndBuf));
创建
WSAOVERLAPPED
数组 必须创建一个
wsaooverlapped
结构数组,以保存所有上传块的重叠状态。要做到这一点,我只需:

// Calculate the amount of upload chunks we will have to create.
// nDataBytes is the size of data you wish to upload
g_nUploadChunks = ceil(nDataBytes / float(UPLOAD_CHUNK_SIZE));

// Overlapped array, should be delete'd after all uploads have completed
g_pSendOverlapped = new WSAOVERLAPPED[g_nUploadChunks];
memset(g_pSendOverlapped, 0, sizeof(WSAOVERLAPPED) * g_nUploadChunks);
上传数据 所有需要发送的数据,例如
// Calculate the amount of upload chunks we will have to create.
// nDataBytes is the size of data you wish to upload
g_nUploadChunks = ceil(nDataBytes / float(UPLOAD_CHUNK_SIZE));

// Overlapped array, should be delete'd after all uploads have completed
g_pSendOverlapped = new WSAOVERLAPPED[g_nUploadChunks];
memset(g_pSendOverlapped, 0, sizeof(WSAOVERLAPPED) * g_nUploadChunks);
WSABUF dataBuf;
DWORD dwBytesSent = 0;
int err;
int i, j;

for(i = 0, j = 0; i < nDataBytes; i += UPLOAD_CHUNK_SIZE, j++)
{
    int nTransferBytes = min(nDataBytes - i, UPLOAD_CHUNK_SIZE);

    dataBuf.buf = &pszData[i];
    dataBuf.len = nTransferBytes;

    // Now upload the data
    int rc = WSASend(sock, &dataBuf, 1, &dwBytesSent, 0, &g_pSendOverlapped[j], SendCompletionCallback);

    if ((rc == SOCKET_ERROR) && (WSA_IO_PENDING != (err = WSAGetLastError())))
    {
        fprintf(stderr, "WSASend failed: %d\n", err);
        exit(EXIT_FAILURE);
    }
}
g_flLastUploadTimeReset = Plat_FloatTime();

// Clear the line on the screen with some default data
printf("(0 chunks of %d) Upload speed: ???? KiB/sec", g_nUploadChunks);

// Keep looping until ALL upload chunks have completed
while(g_nChunksCompleted < g_nUploadChunks)
{
    // Wait for 10ms so then we aren't repeatedly updating the screen
    SleepEx(10, TRUE);

    // Updata chunk count
    printf("\r(%d chunks of %d) ", g_nChunksCompleted, g_nUploadChunks);

    // Not enough time passed?
    if(g_flLastUploadTimeReset + 1 > Plat_FloatTime())
        continue;

    // Reset timer
    g_flLastUploadTimeReset = Plat_FloatTime();

    // Calculate how many kibibytes have been transmitted in the last second
    float flByteRate = g_nBytesSent/1024.0f;
    printf("Upload speed: %.2f KiB/sec", flByteRate);

    // Reset byte count
    g_nBytesSent = 0;
}

// Delete overlapped data (not used anymore)
delete [] g_pSendOverlapped;

// Note that the transfer has completed
Msg("\nTransfer completed successfully!\n");