计算套接字上传速度

Sau*_*aul 5 c++ sockets performance winsock

我想知道是否有人知道如何计算C++中Berkeley套接字的上传速度.我的发送呼叫没有阻塞,需要0.001秒才能发送5兆字节的数据,但需要一段时间来收回响应(所以我知道它正在上传).

这是HTTP服务器的TCP套接字,我需要异步检查已上载/剩余的数据字节数.但是,我在Winsock中找不到任何API函数,所以我很难过.

任何帮助将不胜感激.

编辑:我找到了解决方案,并将尽快发布作为答案!

编辑2:作为答案添加适当的溶液,将在4小时内作为溶液添加.

Sau*_*aul 5

由于bdolan建议减少,我解决了我的问题SO_SNDBUF.但是,要使用此代码,您必须注意您的代码使用Winsock 2(对于重叠的套接字和WSASend).除此之外,您的SOCKET句柄必须类似地创建:

SOCKET sock = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, WSA_FLAG_OVERLAPPED);
Run Code Online (Sandbox Code Playgroud)

请注意该WSA_FLAG_OVERLAPPED标志作为最终参数.

在这个答案中,我将介绍将数据上传到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;
Run Code Online (Sandbox Code Playgroud)

注意:在我的测试中,降低UPLOAD_CHUNK_SIZE结果会提高上传速度的准确性,但会降低整体上传速度.增加UPLOAD_CHUNK_SIZE结果会降低上传速度的准确性,但会提高整体上传速度.4千字节(4096字节)是一个很好的comprimise文件~500kB的大小.

回调函数

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

void CALLBACK SendCompletionCallback(DWORD dwError, DWORD cbTransferred, LPWSAOVERLAPPED lpOverlapped, DWORD dwFlags)
{
    g_nChunksCompleted++;
    g_nBytesSent += cbTransferred;
}
Run Code Online (Sandbox Code Playgroud)

准备插座

最初,必须通过减少SO_SNDBUF为0 来准备套接字.

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

int nSndBuf = 0;
setsockopt(sock, SOL_SOCKET, SO_SNDBUF, (char*)&nSndBuf, sizeof(nSndBuf));
Run Code Online (Sandbox Code Playgroud)

创建WSAOVERLAPPED数组

WSAOVERLAPPED必须创建一组结构来保存所有上传块的重叠状态.要做到这一点,我只需:

// 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);
Run Code Online (Sandbox Code Playgroud)

上传数据

例如,需要发送的所有数据都保存在一个名为的变量中pszData.然后,使用WSASend,数据以常量定义的块发送,UPLOAD_CHUNK_SIZE.

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

等待的比赛

现在,我们可以在所有块上传的同时做任何我们想做的事情.

注意:调用的线程WSASend必须定期进入可警告状态,以便我们的'传输完成'回调(SendCompletionCallback)从APC(异步过程调用)列表中出列.

在我的代码中,我一直循环直到g_nUploadChunks == g_nChunksCompleted.这是为了显示最终用户上传进度和速度(可以修改以显示估计的完成时间,已用时间等)

注2:此代码Plat_FloatTime用作第二个计数器,将其替换为您的代码使用的任何第二个计时器(或相应地调整)

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");
Run Code Online (Sandbox Code Playgroud)

结论

我真的希望这有助于未来的某些人希望在没有任何服务器端修改的情况下计算TCP套接字的上传速度.我不知道性能如何有害SO_SNDBUF = 0,尽管我确信套接字大师会指出这一点.