I solved the problem thanks to bdolan , suggesting to reduce SO_SNDBUF . However, to use this code, you should notice that your code uses Winsock 2 (for overlapped sockets and WSASend ). In addition to this, your SOCKET descriptor should be created similarly:
SOCKET sock = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, WSA_FLAG_OVERLAPPED);
Note the WSA_FLAG_OVERLAPPED flag as the final parameter.
In this answer, I will talk about the steps of uploading data to a TCP server and tracking each download fragment and its completion status. This concept requires dividing your load buffer into chunks (requires minimal existing code modification) and loading it in parts, and then tracking each fragment.
My code stream
Global variables
Your code document should have the following global variables:
#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;
Note: in my tests, decreasing UPLOAD_CHUNK_SIZE increases the accuracy of the download speed, but reduces the overall download speed. Increasing UPLOAD_CHUNK_SIZE reduces the accuracy of the download speed, but increases the overall download speed. 4 kilobytes (4096 bytes) was good composition for a ~ 500 KB file.
Callback function
This function increases the bytes sent and completed completed variables (called after the fragment has been fully loaded on the server)
void CALLBACK SendCompletionCallback(DWORD dwError, DWORD cbTransferred, LPWSAOVERLAPPED lpOverlapped, DWORD dwFlags) { g_nChunksCompleted++; g_nBytesSent += cbTransferred; }
Prepare socket
Initially, the socket should be prepared by decreasing SO_SNDBUF to 0.
Note: In my tests, any value greater than 0 will lead to unwanted behavior.
int nSndBuf = 0; setsockopt(sock, SOL_SOCKET, SO_SNDBUF, (char*)&nSndBuf, sizeof(nSndBuf));
Create an array of WSAOVERLAPPED
It is necessary to create an array of WSAOVERLAPPED structures in order to maintain the overlapping status of all our pieces of loading. For this, I simply:
// 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);
Data loading
All data that needs to be sent, for example, targets, is stored in a variable called pszData . Then, using WSASend , the data is sent in blocks defined by the constant, 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); } }
Waiting game
Now we can do whatever we wish while all the pieces are loading.
Note. a thread called WSASend should be placed in a warning state regularly so that our βcompletedβ translation is completed (SendCompletionCallback) is unloaded from the APC list (asynchronous procedure call).
In my code, I loop continuously into g_nUploadChunks == g_nChunksCompleted . This should show the progress and download speed of the end user (can be changed to show the approximate completion time, elapsed time, etc.).
Note 2: this code uses Plat_FloatTime as the second counter, replace it with any second timer used by your code (or adjust accordingly)
g_flLastUploadTimeReset = Plat_FloatTime();
Conclusion
I really hope this helps someone in the future who wants to calculate the download speed on their TCP sockets without any server-side modifications. I have no idea how bad the performance of SO_SNDBUF = 0 , although I'm sure the socket guru will point it out.