在C++winsock中通过TCP发送大数据(如文件)
Sending large data (like a file) over TCP in C++ winsock
我正试图使用TCP在C++中创建一个程序进行文件传输,但我遇到了一个很难解决的问题:
这是服务器中的接收线程,它在循环中执行recv()并处理从该客户端接收的数据包。
function()是一个日志函数,基本上,数据包中的第一个字节应该是消息ID。100代表开始文件传输101代表传输数据块102代表完成转移
200和201是我认为有帮助的回答。显然,如果在客户端上,每次发送数据块时我都会等待服务器的响应,那么文件传输的工作原理就像魔术一样,但由于计算机之间的ping,它的工作速度非常慢。
我想做的是让客户端将数据发送到服务器,而不必在每次发送数据时等待服务器响应。
void RecvThread(Object ^lpv)
{
// function("Hello! %s", ManagedStringToChar( (String^)lpv ) );
// function("Socket ID : %d!", (System::UInt32)lpv);
char recvbuf[4096*10];
int nrecvret;
SOCKET clientsocket = (SOCKET)lpv;
do {
nrecvret = recv(clientsocket, recvbuf, 4096*10, 0);
if (nrecvret > 0) {
// function("Bytes received: %dn", nrecvret);
BYTE msgID = recvbuf[0];
switch (msgID)
{
case 100:
{
nfilecount++;
DWORD dwFileSize = *(DWORD*)(recvbuf + 1);
function("File size: %d.n", dwFileSize);
fileList[nfilecount].nSize = dwFileSize;
fileList[nfilecount].pData = (unsigned char*)malloc(dwFileSize);
break;
}
case 101:
{
BYTE *pData = (BYTE*)recvbuf;
pData++;
unsigned int nSection = *(DWORD*)(pData);
pData += 4;
unsigned int nLength = *(DWORD*)(pData);
pData += 4;
function("Recieved msg id item data! Section %d, length %dn", nSection , nLength);
for (int i = 0; i < nLength; i++)
fileList[nfilecount].pData[nSection+i] = pData[i];
if (nSection + nLength != fileList[nfilecount].nSize)
{
BYTE* pSendPacketOrg = (BYTE*)malloc(0x5);
BYTE* pSend = pSendPacketOrg;
pSend[0] = 200;
pSend++;
*(DWORD*)pSend = nSection + nLength;
function("%d : %d. Requesting %d...", nSection, nLength, nSection + nLength);
send(clientsocket, (char*)pSendPacketOrg, 5, 0);
}
if (nSection + nLength == fileList[nfilecount].nSize)
{
function("Recieved all sections!");
BYTE pSend = 201;
send(clientsocket, (char*)&pSend, 1, 0);
}
break;
}
case 102:
{
function("finished recieving file!n");
FILE* file = fopen("FileRecieved.dat", "wb");
if (file)
{
int nWritten = 0;
while (nWritten < fileList[nfilecount].nSize)
{
int nToWrite = 1024 * 1024;
int Remaining = fileList[nfilecount].nSize - nWritten;
if (Remaining <= nToWrite)
nToWrite = Remaining;
int nBytesWritten = fwrite(fileList[nfilecount].pData + nWritten, 1, nToWrite, file);
nWritten += nBytesWritten;
}
}
fclose(file);
break;
}
case 't':
{
// Echo the buffer back to the sender
function("Recieved handshake!n");
int nsendret = send(clientsocket, recvbuf, nrecvret, 0);
if (nsendret == SOCKET_ERROR) {
function("send failed with error: %dn", WSAGetLastError());
// closesocket(clientsocket);
// WSACleanup();
break;
}
// function("Bytes sent: %dn", nsendret);
break;
}
default:
{
function("Unknown msgID: %d, size: %dn", msgID, nrecvret);
function("Last error: %dn", WSAGetLastError());
break;
}
}
}
else if (nrecvret < 0)
{
if (errno == EAGAIN)
{
function("recv() timed out.n");
}
else
{
function("recv() failed due to errno = %dn", errno);
// exit(1);
}
}
else if (nrecvret == 0)
function("Connection closing...n");
else if (WSAGetLastError() == 10053)
{
function("An established connection was aborted by the software in your host computer, possibly due to a data transmission time-out or protocol error.n");
break;
}
else {
function("recv failed with error: %dn", WSAGetLastError());
break;
}
} while (nrecvret > 0);
// shutdown the connection since we're done
function("Shutting down and closing socket %d...n", clientsocket);
nrecvret = shutdown(clientsocket, SD_SEND);
if (nrecvret == SOCKET_ERROR) {
function("shutdown failed with error: %dn", WSAGetLastError());
closesocket(clientsocket);
// WSACleanup();
return;
}
}
这部分代码来自客户端。客户端连接,打招呼,并将我分配的数据(目前仅用于测试)发送到服务器。我添加了一些注释来帮助理解代码。
int __cdecl main(int argc, char **argv)
{
unsigned char* pData; // "file" data
unsigned int nSize = 1023928; // "file" size,
pData = (unsigned char*)malloc(nSize); // for now these are allocated instead of reading a file
for (int i = 0; i < nSize; i++)
pData[i] = 0xD2; // the data for the file would be 0xD2 throughout the whole file
WSADATA wsaData;
SOCKET ConnectSocket = INVALID_SOCKET;
struct addrinfo *result = NULL,
hints;
char *sendbuf = "this is a test";
char *szServerIP = "109.xx.xxx.xx"; // server IP, censored
char recvbuf[DEFAULT_BUFLEN]; // default buflen is 512
int iResult;
int recvbuflen = DEFAULT_BUFLEN;
// Initialize Winsock
iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
if (iResult != 0) {
printf("WSAStartup failed with error: %dn", iResult);
return 1;
}
ZeroMemory(&hints, sizeof(hints));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
// Resolve the server address and port
iResult = getaddrinfo(szServerIP, "6005", &hints, &result);
if (iResult != 0) {
printf("getaddrinfo failed with error: %dn", iResult);
WSACleanup();
return 1;
}
int count = 0;
while (1)
{
count++;
printf("[%d] Connecting... n", count);
// Create a SOCKET for connecting to server
ConnectSocket = socket(result->ai_family, result->ai_socktype,
result->ai_protocol);
if (ConnectSocket == INVALID_SOCKET) {
printf("socket failed with error: %ldn", WSAGetLastError());
WSACleanup();
return 1;
}
// Connect to server.
iResult = connect(ConnectSocket, result->ai_addr, (int)result->ai_addrlen);
if (iResult == SOCKET_ERROR) {
closesocket(ConnectSocket);
ConnectSocket = INVALID_SOCKET;
continue;
}
break;
}
freeaddrinfo(result);
if (ConnectSocket == INVALID_SOCKET) {
printf("Unable to connect to server!n");
WSACleanup();
return 1;
}
printf("Connected to the server on port 6005! Now sending data...");
// Send an initial buffer
iResult = send(ConnectSocket, sendbuf, (int)strlen(sendbuf), 0); // says hi
if (iResult == SOCKET_ERROR) {
printf("send failed with error: %dn", WSAGetLastError());
closesocket(ConnectSocket);
WSACleanup();
return 1;
}
printf("Bytes Sent: %ldn", iResult);
// Wait for the server to say hi back
do {
iResult = recv(ConnectSocket, recvbuf, recvbuflen, 0);
if (iResult > 0)
{
printf("Bytes received: %dn", iResult);
break;
}
else if (iResult == 0)
printf("Connection closedn");
else
printf("recv failed with error: %dn", WSAGetLastError());
} while (iResult > 0);
Sleep(2000); // wait 2 seconds and then send our data
BYTE *pSizePacketOrg = (BYTE*)malloc(5);// packet 100 sends data size
BYTE *pSizePacket = pSizePacketOrg;//
pSizePacket[0] = 100;//
pSizePacket++;//
*(DWORD*)pSizePacket = 1023928;//
printf("Sending 100n");
iResult = send(ConnectSocket, (char*)pSizePacketOrg, 5, 0);
if (iResult == SOCKET_ERROR) {
printf("send 100 with error: %dn", WSAGetLastError());
closesocket(ConnectSocket);
WSACleanup();
return 1;
}
free(pSizePacketOrg);
unsigned int nWritten = 0;
printf("Sending 101n"); // packet 101 sends the data, in chunks
while (nWritten < nSize)
{
unsigned int nToWrite = 1024;
printf("nToWrite %dn", nToWrite);
unsigned int Remaining = nSize - nWritten;
printf("nRemaining %dn", nToWrite);
if (Remaining <= nToWrite)
{
nToWrite = Remaining;
printf("Remaining smaller or equal to write. Writing %d.n", nToWrite);
}
BYTE *pSendPacketOrg = (BYTE*)malloc(nToWrite + 1 + 4 + 4);
BYTE* pSendPacket = pSendPacketOrg;
pSendPacket[0] = 101;
pSendPacket++;
*(DWORD*)pSendPacket = nWritten;
pSendPacket += 4;
*(DWORD*)pSendPacket = nToWrite;
pSendPacket += 4;
memcpy(pSendPacket, (pData + nWritten), nToWrite);
// Sleep(100);
// Sleep(20);
int nBytesWritten = send(ConnectSocket, (char*)pSendPacketOrg, nToWrite + 9, 0);
if (nBytesWritten == SOCKET_ERROR) {
printf("send 101 : %d:%d with error: %dn", nWritten, nToWrite, WSAGetLastError());
closesocket(ConnectSocket);
WSACleanup();
return 1;
}
free(pSendPacketOrg);
printf("written %d bytes.n", nBytesWritten);
nWritten += nBytesWritten-9; // -9 because of the information before the data
if (nWritten == nSize) // if finished sending everything, wait for a response from the server
{
int nResponse = recv(ConnectSocket, recvbuf, recvbuflen, 0);
if (nResponse == 1)
{
if (((BYTE*)recvbuf)[0] == 201) // server notified finished recieving
{
printf("Server finished recieving! send end file...");
break;
}
}
//int nResponse = recv(ConnectSocket, recvbuf, recvbuflen, 0);
//if (nResponse == 5)
//{
// if ( ((BYTE*)recvbuf)[0] == 200)
// {
// printf("Server is requesting %d!", *(DWORD*)(recvbuf + 1));
// }
//}
}
// Sleep(50);
}
printf("Sending 102n"); // confirming the end of the transmission
BYTE pEndPacket = 102;
iResult = send(ConnectSocket, (char*)(&pEndPacket), 1, 0);
if (iResult == SOCKET_ERROR) {
printf("send 102 with error: %dn", WSAGetLastError());
closesocket(ConnectSocket);
WSACleanup();
return 1;
}
Sleep(1000);
iResult = send(ConnectSocket, sendbuf, (int)strlen(sendbuf), 0);
if (iResult == SOCKET_ERROR) {
printf("send failed with error: %dn", WSAGetLastError());
closesocket(ConnectSocket);
WSACleanup();
return 1;
}
Sleep(10000);
// shutdown the connection since no more data will be sent
iResult = shutdown(ConnectSocket, SD_SEND);
if (iResult == SOCKET_ERROR) {
printf("shutdown failed with error: %dn", WSAGetLastError());
closesocket(ConnectSocket);
WSACleanup();
return 1;
}
// cleanup
closesocket(ConnectSocket);
WSACleanup();
return 0;
}
在我的日志文件中,当我从计算机本地运行客户端和服务器时,它几乎完美地工作:
[LOG] Recieved handshake!
[LOG] File size: 1023928.
[LOG] Recieved msg id item data! Section 0, length 36864
[LOG] 0 : 36864. Requesting 36864...[LOG] Recieved msg id item data! Section 36864, length 36864
[LOG] 36864 : 36864. Requesting 73728...[LOG] Recieved msg id item data! Section 73728, length 36864
[LOG] 73728 : 36864. Requesting 110592...[LOG] Recieved msg id item data! Section 110592, length 36864
[LOG] 110592 : 36864. Requesting 147456...[LOG] Recieved msg id item data! Section 147456, length 36864
[LOG] 147456 : 36864. Requesting 184320...[LOG] Recieved msg id item data! Section 184320, length 36864
[LOG] 184320 : 36864. Requesting 221184...[LOG] Recieved msg id item data! Section 221184, length 36864
[LOG] 221184 : 36864. Requesting 258048...[LOG] Recieved msg id item data! Section 258048, length 36864
[LOG] 258048 : 36864. Requesting 294912...[LOG] Recieved msg id item data! Section 294912, length 36864
[LOG] 294912 : 36864. Requesting 331776...[LOG] Recieved msg id item data! Section 331776, length 36864
[LOG] 331776 : 36864. Requesting 368640...[LOG] Recieved msg id item data! Section 368640, length 36864
[LOG] 368640 : 36864. Requesting 405504...[LOG] Recieved msg id item data! Section 405504, length 36864
[LOG] 405504 : 36864. Requesting 442368...[LOG] Recieved msg id item data! Section 442368, length 36864
[LOG] 442368 : 36864. Requesting 479232...[LOG] Recieved msg id item data! Section 479232, length 36864
[LOG] 479232 : 36864. Requesting 516096...[LOG] Recieved msg id item data! Section 516096, length 36864
[LOG] 516096 : 36864. Requesting 552960...[LOG] Recieved msg id item data! Section 552960, length 36864
[LOG] 552960 : 36864. Requesting 589824...[LOG] Recieved msg id item data! Section 589824, length 36864
[LOG] 589824 : 36864. Requesting 626688...[LOG] Recieved msg id item data! Section 626688, length 36864
[LOG] 626688 : 36864. Requesting 663552...[LOG] Recieved msg id item data! Section 663552, length 36864
[LOG] 663552 : 36864. Requesting 700416...[LOG] Recieved msg id item data! Section 700416, length 36864
[LOG] 700416 : 36864. Requesting 737280...[LOG] Recieved msg id item data! Section 737280, length 36864
[LOG] 737280 : 36864. Requesting 774144...[LOG] Recieved msg id item data! Section 774144, length 36864
[LOG] 774144 : 36864. Requesting 811008...[LOG] Recieved msg id item data! Section 811008, length 36864
[LOG] 811008 : 36864. Requesting 847872...[LOG] Recieved msg id item data! Section 847872, length 36864
[LOG] 847872 : 36864. Requesting 884736...[LOG] Recieved msg id item data! Section 884736, length 36864
[LOG] 884736 : 36864. Requesting 921600...[LOG] Recieved msg id item data! Section 921600, length 36864
[LOG] 921600 : 36864. Requesting 958464...[LOG] Recieved msg id item data! Section 958464, length 36864
[LOG] 958464 : 36864. Requesting 995328...[LOG] Recieved msg id item data! Section 995328, length 28600
[LOG] Recieved all sections![LOG] finished recieving file!
然而。。。当我在远程PC上运行客户端时,服务器日志如下所示:
[LOG] Recieved handshake!
[LOG] File size: 1023928.
[LOG] Recieved msg id item data! Section 0, length 36864
[LOG] 0 : 36864. Requesting 36864...[LOG] Unknown msgID: 210, size: 36720
[LOG] Last error: 183
[LOG] Unknown msgID: 210, size: 36720
[LOG] Last error: 183
[LOG] Unknown msgID: 210, size: 35972
[LOG] Last error: 183
[LOG] Recieved msg id item data! Section 147456, length 36864
[LOG] 147456 : 36864. Requesting 184320...[LOG] Unknown msgID: 210, size: 40960
[LOG] Last error: 183
[LOG] Unknown msgID: 210, size: 1967
[LOG] Last error: 183
[LOG] Unknown msgID: 210, size: 6800
[LOG] Last error: 183
[LOG] Unknown msgID: 210, size: 593
[LOG] Last error: 183
[LOG] Unknown msgID: 210, size: 35980
[LOG] Last error: 183
[LOG] Unknown msgID: 210, size: 36720
[LOG] Last error: 183
[LOG] Unknown msgID: 0, size: 37214
[LOG] Last error: 183
[LOG] Unknown msgID: 210, size: 36582
[LOG] Last error: 183
[LOG] Unknown msgID: 210, size: 38080
[LOG] Last error: 183
[LOG] Unknown msgID: 210, size: 36720
[LOG] Last error: 183
[LOG] Unknown msgID: 210, size: 36506
[LOG] Last error: 183
[LOG] Unknown msgID: 210, size: 36410
[LOG] Last error: 183
[LOG] Unknown msgID: 210, size: 36720
[LOG] Last error: 183
[LOG] Unknown msgID: 210, size: 38080
[LOG] Last error: 183
[LOG] Unknown msgID: 210, size: 36720
[LOG] Last error: 183
[LOG] Unknown msgID: 210, size: 4080
[LOG] Last error: 183
[LOG] Unknown msgID: 210, size: 32640
[LOG] Last error: 183
[LOG] Unknown msgID: 210, size: 36720
[LOG] Last error: 183
[LOG] Unknown msgID: 210, size: 36720
[LOG] Last error: 183
[LOG] Unknown msgID: 210, size: 1360
[LOG] Last error: 183
[LOG] Unknown msgID: 210, size: 35123
[LOG] Last error: 183
[LOG] Unknown msgID: 210, size: 36720
[LOG] Last error: 183
[LOG] Unknown msgID: 210, size: 36720
[LOG] Last error: 183
[LOG] Unknown msgID: 210, size: 37899
[LOG] Last error: 183
[LOG] Unknown msgID: 210, size: 36234
[LOG] Last error: 183
[LOG] Unknown msgID: 210, size: 1360
[LOG] Last error: 183
[LOG] Unknown msgID: 210, size: 35360
[LOG] Last error: 183
[LOG] Unknown msgID: 210, size: 36720
[LOG] Last error: 183
[LOG] Unknown msgID: 210, size: 28515
[LOG] Last error: 183
[LOG] Connection closing...
[LOG] Shutting down and closing socket 680...
我收到客户端没有发送的数据包,大小都不一样,我假设它们是文件数据,因为数据包中的第一个字节是210(dec表示0xD2,我将其设置为"文件"数据)但是,为什么收到的包裹是这样的?
我被告知应该有一些延迟,可能与阻塞或非阻塞功能有关,我不是网络专家。。。
请记住,每次客户端发送数据包时等待服务器响应是我希望避免的目标,因为我希望确保最大传输速度,而计算机之间的ping会降低数据传输的速率。我应该在代码中添加一个套接字选项吗?我做错什么了吗?
提前谢谢,强尼!
实际上,您不必像TCP为您所做的那样手动执行ACK。
如果出于某种原因必须手动进行ACK,那么您需要做的是窗口化,即允许一定数量的未确认数据包最大限度地利用网络容量。(但确定窗口大小是一项艰巨的任务,TCP仍然可以完成)
- C++数据文件、数组和计算赋值
- 数据文件的第二行未正确读取
- 在 C++/C 中使用 CURL 发出带有数据文件的 GET HTTP 请求
- 将数据文件读入结构 C++
- 引入流时C++数据文件未正确读取?
- 如何在数据文件中查找和显示内容
- 如何处理错误"E1696命令行错误:无法在Visual Studio 2017中打开元数据文件"mscorlib.dll"?
- 尝试读取数据文件,存储在数组中并打印所有元素,但它不起作用
- 将数据文件读入对象数组时出现问题
- C :数据文件有错误:预期的无限制ID
- C 和 C++:带有错误"Expected unqualified-id"的数据文件
- 如何在C 中读取一系列数据文件
- 从数据文件中读取每2个字节,并在向量中进行比较
- 写入单个大数据文件或多个小文件:哪个更快?
- 在读取数据文件时,如何找到用户字符串输入的字谜?
- 保护外部数据文件免受未经授权的修改
- C 读取数据文件
- C 如何将数据文件读取到结构或向量以及返回结构或向量
- 具有挑战性的数据文件格式,需要将其读取为包含类对象的数组的VAR
- C 中的Google单元测试:如何编写持久数据文件