我正在尝试用C ++创建一个使用TCP进行文件传输的程序,但是我遇到了一个难以解决的问题:
这是服务器中的接收线程,它在循环上执行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: %d\n", 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 %d\n", 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: %d\n", WSAGetLastError());
// closesocket(clientsocket);
// WSACleanup();
break;
}
// function("Bytes sent: %d\n", nsendret);
break;
}
default:
{
function("Unknown msgID: %d, size: %d\n", msgID, nrecvret);
function("Last error: %d\n", WSAGetLastError());
break;
}
}
}
else if (nrecvret < 0)
{
if (errno == EAGAIN)
{
function("recv() timed out.\n");
}
else
{
function("recv() failed due to errno = %d\n", 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: %d\n", 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: %d\n", 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: %d\n", 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: %d\n", 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: %ld\n", 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: %d\n", WSAGetLastError());
closesocket(ConnectSocket);
WSACleanup();
return 1;
}
printf("Bytes Sent: %ld\n", iResult);
// Wait for the server to say hi back
do {
iResult = recv(ConnectSocket, recvbuf, recvbuflen, 0);
if (iResult > 0)
{
printf("Bytes received: %d\n", iResult);
break;
}
else if (iResult == 0)
printf("Connection closed\n");
else
printf("recv failed with error: %d\n", 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 100\n");
iResult = send(ConnectSocket, (char*)pSizePacketOrg, 5, 0);
if (iResult == SOCKET_ERROR) {
printf("send 100 with error: %d\n", WSAGetLastError());
closesocket(ConnectSocket);
WSACleanup();
return 1;
}
free(pSizePacketOrg);
unsigned int nWritten = 0;
printf("Sending 101\n"); // packet 101 sends the data, in chunks
while (nWritten < nSize)
{
unsigned int nToWrite = 1024;
printf("nToWrite %d\n", nToWrite);
unsigned int Remaining = nSize - nWritten;
printf("nRemaining %d\n", 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: %d\n", 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 102\n"); // 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: %d\n", 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: %d\n", 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: %d\n", 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会降低数据的速率。被转移。 我应该在代码中添加套接字选项吗?有什么我做错了吗?
先谢谢,约翰尼!
答案 0 :(得分:0)
实际上你不必像TCP已经为你做的那样手动做ACK。
如果由于某种原因必须手动执行ACK,那么您需要做的就是窗口化,即允许一定数量的未分组数据包最大限度地利用网络的容量。 (但确定窗口大小是一项艰巨的任务,并且仍然由TCP完成)