由于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,尽管我确信套接字大师会指出这一点.
| 归档时间: |
|
| 查看次数: |
4565 次 |
| 最近记录: |