I just finished a small winsock program for sending files, but its
soooooooooooooooooooooooooooooooooooooooooooooooooooooooo slooooowwwwwwwwwwwwwwwww:yawn:

so I am hoping someone here may be able to offer me some advice.

so far, the program works as follows:
1. User1 chooses a file and attempts to send it.
2. User2 chooses to accept the file, saves it, and sends back acknowledgment of acceptance
3. User1 receives the acknowledgment and sends the first chunk of 1024 bytes
4. User2 receives the chunk and sends a request back to User1 for the next chunk
5. User1 responds to all requests for chunks until the entire file has been sent.
6. File transfer complete.

although the default communication package is 1024 bytes, meaning that the request for the next chunk is 1024 bytes, that would only effectively cut the transfer speed in half.
I could cut that down to 5 bytes if I had to, but that still doesn't account for the insanely slow transfer rate of somewhere around 15KB/S =(

I have had the same person download a file from me using Xitami webserver and the transfer rate was over 100KB/S.

I am using Async Sockets, and have set the TCP_NODELAY flag for the socket, but still can't get any decent speed out of it :icon_frown:

this is the code:

#pragma comment (lib, "ws2_32.lib")
#include <windows.h>
//#include <winsock.h>
#include <iostream>
#include <fstream>
using namespace std;
#define SCK_VERSION2 0x0202
#define SOCKET_MESSAGE WM_USER+1
#define DEFAULT_PORT 21234
#define BTN_LISTEN 0x8801
#define BTN_CONNECT 0x8802
#define BTN_SEND 0x8803
#define BTN_DISCONNECT 0x8804
#define TXT_IP 0x8806
#define EMSG(x) MessageBoxA(mWnd, x, "Error", MB_OK | MB_ICONERROR)
#define QMSGOKCAN(x) MessageBoxA(mWnd, x, "Error", MB_OK | MB_OKCANCEL | MB_ICONWARNING)
#define QMSGYESNO(x) MessageBoxA(mWnd, x, "Error", MB_OK | MB_YESNO | MB_ICONQUESTION)
//#define iMSG32(x) {char buff[12]; sprintf_s(buff, 12, "%i", x); MessageBoxA(mWnd, buff, "Error", MB_OK);}
#define IMSG(x) MessageBoxA(mWnd, x, "Error", MB_OK | MB_ICONINFORMATION)

#define AS_IDLE 1
#define AS_MESSAGE 2
#define AS_FILEIN 4
#define AS_FILEOUT 8
#define AS_PENDING 16
#define AS_ACCEPT 32
#define AS_DECLINE 64
#define AS_GIMME 128

#define CHUNK_SIZE 1024
#define TH_BUFFERSIZE 512
struct TRANSFER_HEADER
{
	int action;
	int attrib;
	char buffer[TH_BUFFERSIZE];
	char padding[504];
};
int AS_AppState = AS_IDLE;

ifstream *ifstrReadFile; //to open outgoing files
ofstream *ofstrSaveFile; //to save incoming files

int bytesWritten, inFileSize, fPercent;
char fileToSend[TH_BUFFERSIZE];
BYTE sendBuffer[CHUNK_SIZE];
TRANSFER_HEADER trHeader_in;
TRANSFER_HEADER trHeader_out;
int sizeSent, totalSent;
char fStatus[128];

BOOL bActiveSocket = FALSE;
BOOL bConnected = FALSE;
HWND mWnd;
HWND hwndEdit = 0;
char szText[512];
SOCKET tmpSocket = 0;
SOCKET mSocket = 0;
SOCKADDR_IN userAddr;
int sizeAddr = sizeof(userAddr);
int flag = 1;
LRESULT CALLBACK MainProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
void CloseSocket(SOCKET sock);
BOOL CreateTCPSocket(HWND hWindow);
BOOL Connect(SOCKET sock, char *IPAddress, unsigned int port);
BOOL Listen(SOCKET sock, unsigned int port);
void Process(SOCKET sock);
BOOL OpenFile(HWND hWnd, char *oFileName, int buffsize);
BOOL SaveFile(HWND hWnd, char *ioFileName, int buffsize);
void SendFile();
int FileSize(const char * szFileName);
int CALLBACK WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,  LPSTR lpCmdLine, int nCmdShow)
{ 
	MSG msg;
	WNDCLASSEX cmain;

	cmain.cbSize        = sizeof(WNDCLASSEX);
	cmain.style         = CS_HREDRAW | CS_VREDRAW;
	cmain.lpfnWndProc   = MainProc;
	cmain.cbClsExtra    = 0;
	cmain.cbWndExtra    = 0;
	cmain.hIcon         = LoadIcon(NULL, IDI_APPLICATION);
	cmain.hCursor       = LoadCursor(NULL, IDC_ARROW);
	cmain.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
	cmain.lpszMenuName  = NULL;
	cmain.lpszClassName = L"deskConnect";
	cmain.hInstance     = hInstance;
	cmain.hIconSm       = LoadIcon(NULL, IDI_APPLICATION);

	RegisterClassEx(&cmain);
	
	mWnd = CreateWindow(L"deskConnect", L"Desk Connect", WS_OVERLAPPEDWINDOW, 300, 300, 512, 384, NULL, NULL, hInstance, NULL);

	CreateWindow(L"button", L"Listen", WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON, 5, 5, 80, 25, mWnd, (HMENU)BTN_LISTEN, hInstance, NULL);
	CreateWindow(L"button", L"Connect", WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON, 85, 5, 80, 25, mWnd, (HMENU)BTN_CONNECT, hInstance, NULL);
	CreateWindow(L"button", L"Send", WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON, 165, 5, 80, 25, mWnd, (HMENU)BTN_SEND, hInstance, NULL);
	CreateWindow(L"button", L"Disconnect", WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON, 245, 5, 80, 25, mWnd, (HMENU)BTN_DISCONNECT, hInstance, NULL);

	hwndEdit = CreateWindow(L"edit", NULL, WS_CHILD | WS_VISIBLE | WS_BORDER, 5, 40, 125, 20, mWnd, (HMENU)TXT_IP, hInstance, NULL);

	if(!mWnd) return 0;
	ShowWindow(mWnd, SW_SHOWNORMAL);
	UpdateWindow(mWnd);
	
	SetWindowTextA(hwndEdit, "127.0.0.1");

	while(GetMessage(&msg, NULL, 0, 0))
	{
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}
	return msg.wParam;
}
LRESULT CALLBACK MainProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam)
{
    switch(Msg)
    {
		case SOCKET_MESSAGE:
			switch (WSAGETSELECTEVENT(lParam))
            {
				case FD_ACCEPT:
					memset(&userAddr, 0, sizeof(SOCKADDR));
					tmpSocket = accept(wParam, (LPSOCKADDR)&userAddr, &sizeAddr);
					closesocket(mSocket);
					mSocket = tmpSocket;
					if(mSocket == INVALID_SOCKET)
					{
						EMSG("Connection failed!");
						CloseSocket(mSocket);
						break;
					}
					bConnected = TRUE;
					IMSG("Client connected.");
					break;
				case FD_CONNECT:
					if(!WSAGETSELECTERROR(lParam))
					{
						bConnected = TRUE;
						IMSG("Connection successful.");
					}
					else
					{
						EMSG("Connection failed!");
					}
					break;
				case FD_READ:
					Process(wParam);
					break;
				case FD_CLOSE:
					CloseSocket(mSocket);
					EMSG("Connection lost!");
					break;
            }
			break;
		case WM_COMMAND:
			switch(LOWORD(wParam))
			{
			case BTN_LISTEN:
				if(bActiveSocket)
				{
					if(QMSGOKCAN("This will reset the connection.")  == IDOK)
					{
						CloseSocket(mSocket);
					}
					else
					{
						break;
					}
				}
				bActiveSocket = CreateTCPSocket(hWnd);
				if(bActiveSocket)
					 Listen(mSocket, DEFAULT_PORT);
				break;
			case BTN_CONNECT:
				if(bActiveSocket)
				{
					if(QMSGOKCAN("This will reset the connection.")  == IDOK)
					{
						CloseSocket(mSocket);
					}
					else
					{
						break;
					}
				}
				bActiveSocket = CreateTCPSocket(hWnd);
				if(bActiveSocket)
					Connect(mSocket, szText, DEFAULT_PORT);
				break;
			case BTN_SEND:
				if(bConnected)
					SendFile();
				else
					EMSG("Not connected.");
				break;
			case BTN_DISCONNECT:
				CloseSocket(mSocket);
				break;
			case TXT_IP:
				if(HIWORD(wParam) == EN_UPDATE)
				{
					ZeroMemory(szText, 512);
					GetWindowTextA(hwndEdit, szText, 512);
					InvalidateRect(hwndEdit, NULL, TRUE);
				}
			}
			break;
		case WM_DESTROY:
			CloseSocket(mSocket);
			PostQuitMessage(WM_QUIT);
			break;
		default:
			return DefWindowProc(hWnd, Msg, wParam, lParam);
		}
    return TRUE;
}
BOOL CreateTCPSocket(HWND hWindow)
{
	WSADATA wsadata;
	
	if(WSAStartup(SCK_VERSION2, &wsadata) != 0)
		return FALSE;
	
	if(wsadata.wVersion != SCK_VERSION2)
	{
		WSACleanup();
		return FALSE;
	}
	
	mSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	if(mSocket == INVALID_SOCKET)
	{
		EMSG("Couldn't create socket.");
		WSACleanup();
		return FALSE;
	}
	setsockopt(mSocket, IPPROTO_TCP, TCP_NODELAY, (char *)&flag, sizeof(int));
	WSAAsyncSelect(mSocket, hWindow, SOCKET_MESSAGE, FD_ACCEPT | FD_CONNECT | FD_READ | FD_CLOSE);
	return TRUE;
}
BOOL Connect(SOCKET sock, char *IPAddress, unsigned int port)
{
	SOCKADDR_IN addr;

	ZeroMemory(&addr, sizeof(addr));

	addr.sin_family = AF_INET;
	addr.sin_port = htons(port);
	addr.sin_addr.S_un.S_addr = inet_addr(IPAddress);

	int error = connect(sock, (LPSOCKADDR)&addr, sizeof(addr));
	if(error != 0)
	{
		error = WSAGetLastError();
		if(error != WSAEWOULDBLOCK)
		{
			EMSG("Connection failed!");
			CloseSocket(mSocket);
			return FALSE;
		}
	}
	return TRUE;
}
BOOL Listen(SOCKET sock, unsigned int port)
{
	SOCKADDR_IN addr;
	addr.sin_family = AF_INET;
	addr.sin_port = htons(port);
	addr.sin_addr.S_un.S_addr = htonl(INADDR_ANY);

	if(bind(sock, (LPSOCKADDR)&addr, sizeof(addr)) == SOCKET_ERROR)
	{
		EMSG("Could not bind socket");
		CloseSocket(mSocket);
		return FALSE;
	}

	if(listen(sock, SOMAXCONN) == SOCKET_ERROR)
	{
		EMSG("could not listen");
		CloseSocket(mSocket);
		return FALSE;
	}

	return TRUE;
}
void CloseSocket(SOCKET sock)
{
	if(sock)  closesocket(sock);
	WSACleanup();
	bActiveSocket = FALSE;
	bConnected = FALSE;
	AS_AppState = AS_IDLE;
	if(ifstrReadFile->is_open()) ifstrReadFile->close();
	if(ofstrSaveFile->is_open()) ofstrSaveFile->close();
	SetWindowTextA(mWnd, "Desk Connect");
}
BOOL OpenFile(HWND hWnd, char *oFileName, int buffsize)
{
	OPENFILENAMEA ofn;

	ZeroMemory(&ofn, sizeof(OPENFILENAMEA));
	
	ofn.lStructSize = sizeof(OPENFILENAMEA);
	ofn.hwndOwner = hWnd;
	ofn.lpstrFilter = "All\0*.*\0";
	ofn.lpstrFile = oFileName;
	ofn.lpstrFile[0] = '\0';
	ofn.nMaxFile = buffsize;
	ofn.lpstrFileTitle = NULL;
	ofn.nMaxFileTitle = 0;
	ofn.lpstrTitle = "Open File\0";
	ofn.lpstrInitialDir = NULL;
	ofn.Flags = OFN_EXPLORER | OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST;

	return GetOpenFileNameA(&ofn);
}
BOOL SaveFile(HWND hWnd, char *ioFileName, int buffsize)
{
	OPENFILENAMEA ofn;
	ZeroMemory(&ofn, sizeof(OPENFILENAMEA));

	ofn.lStructSize = sizeof(OPENFILENAMEA);
	ofn.hwndOwner = hWnd;
	ofn.lpstrFilter = "All\0*.*\0";
	ofn.lpstrFile = ioFileName;
	ofn.nMaxFile = buffsize;
	ofn.lpstrFileTitle = NULL; //output?
	ofn.nMaxFileTitle = 0;
	ofn.lpstrTitle = "Save File As\0";
	ofn.lpstrInitialDir = NULL;
	ofn.Flags = OFN_EXPLORER | OFN_PATHMUSTEXIST | OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT;

	return(GetSaveFileNameA(&ofn));
}
void Process(SOCKET sock)
{	
	char buffer[CHUNK_SIZE];
	ZeroMemory(&buffer, CHUNK_SIZE);

	int bytesRecv = recv(sock, buffer, CHUNK_SIZE, 0);
	
	switch(AS_AppState)
	{
		case AS_IDLE:
			memcpy(&trHeader_in, buffer, CHUNK_SIZE);
			switch(trHeader_in.action)
			{
				case AS_MESSAGE:
					IMSG(trHeader_in.buffer);
					break;
				case AS_FILEIN:
				{
					char cQuestion[1024];
					sprintf_s(cQuestion, "Do you want to accept this file?\n%s (%iBytes)", trHeader_in.buffer, trHeader_in.attrib);
			
					int answer = QMSGYESNO(cQuestion);

					ZeroMemory(&trHeader_out, CHUNK_SIZE);
					if(answer == IDYES)
					{
						if(SaveFile(mWnd, trHeader_in.buffer, TH_BUFFERSIZE))
						{
							ofstrSaveFile = new ofstream(trHeader_in.buffer, ios_base::binary | ios_base::out);
							if(ofstrSaveFile->is_open() && ofstrSaveFile->good())
							{
								trHeader_out.action = AS_ACCEPT;
								send(sock, (char*)&trHeader_out, CHUNK_SIZE, 0);
								AS_AppState = AS_FILEIN;
								SetWindowTextA(mWnd, "Receiving file 0%");
								bytesWritten = 0;
								break;
							}
							else
							{
								ofstrSaveFile->close();
								EMSG("Couldn't open file");
							}
						}
					}
					//if user clicks No, or there is an error..
					trHeader_out.action = AS_DECLINE;
					send(sock, (char*)&trHeader_out, CHUNK_SIZE, 0);
					break;
				}
				case AS_ACCEPT:
					ifstrReadFile = new ifstream(trHeader_out.buffer, ios_base::binary | ios_base::in);
					if(ifstrReadFile->is_open() && ifstrReadFile->good() && !ifstrReadFile->eof())
					{
						AS_AppState = AS_FILEOUT;
						ZeroMemory(sendBuffer, CHUNK_SIZE);
						ifstrReadFile->read((char*)sendBuffer, CHUNK_SIZE);
						sizeSent = ifstrReadFile->gcount();
						send(sock, (char*)sendBuffer, sizeSent, 0);
						totalSent = 0;
						SetWindowTextA(mWnd, "Sending file 0%");
					}
					else
					{
						ifstrReadFile->close();
						EMSG("Couldn't open file");
					}
					break;
			}
			break;
		case AS_FILEIN:
			//put chunk in file
			ofstrSaveFile->write(buffer, bytesRecv);
			bytesWritten += bytesRecv;
			
			fPercent = int(float(bytesWritten) / float(trHeader_in.attrib) * 100.0f);
			sprintf_s(fStatus, "Receiving file %i%%", fPercent);
			SetWindowTextA(mWnd, fStatus);

			if(bytesWritten < trHeader_in.attrib)
			{
				ZeroMemory(&trHeader_out, CHUNK_SIZE);
				trHeader_out.action = AS_GIMME;
				send(sock, (char*)&trHeader_out, CHUNK_SIZE, 0);
			}
			else if(bytesWritten == trHeader_in.attrib)
			{
				//fuck..error
				AS_AppState = AS_IDLE;
				ofstrSaveFile->close();
				SetWindowTextA(mWnd, "Desk Connect");
				IMSG("File transfer is complete");
			}
			else if(bytesWritten > trHeader_in.attrib)
			{
				AS_AppState = AS_IDLE;
				ofstrSaveFile->close();
				remove(trHeader_in.buffer);
				SetWindowTextA(mWnd, "Desk Connect");
				EMSG("Error during file transfer!");
			}
			break;
		case AS_FILEOUT:
			memcpy(&trHeader_in, buffer, CHUNK_SIZE);
			switch(trHeader_in.action)
			{
				case AS_IDLE:
					ifstrReadFile->close();
					AS_AppState = AS_IDLE;
					EMSG("User cancelled file transfer");
					break;
				case AS_GIMME:
					if(!ifstrReadFile->eof())
					{
						ZeroMemory(sendBuffer, CHUNK_SIZE);
						ifstrReadFile->read((char*)sendBuffer, CHUNK_SIZE);
						sizeSent = ifstrReadFile->gcount();
						totalSent += sizeSent;
						send(sock, (char*)sendBuffer, sizeSent, 0);
						if(ifstrReadFile->eof())
						{
							SetWindowTextA(mWnd, "Desk Connect");
							AS_AppState = AS_IDLE;
							ifstrReadFile->close();
							IMSG("File transfer complete");
						}
						else
						{
							fPercent = int(float(totalSent) / float(trHeader_out.attrib) * 100.0f);
							sprintf_s(fStatus, "Sending file %i%%", fPercent);
							SetWindowTextA(mWnd, fStatus);
						}
					}
					break;
			}
			break;
	}
}
void SendFile()
{
	if(OpenFile(mWnd, trHeader_out.buffer, TH_BUFFERSIZE))
	{
		trHeader_out.action = AS_FILEIN;
		trHeader_out.attrib = FileSize(trHeader_out.buffer);
		if(trHeader_out.attrib)
		{
			send(mSocket, (char*)&trHeader_out, CHUNK_SIZE, 0);
		}
		else
		{
			EMSG("File is empty.");
		}
	}
}
int FileSize(const char * szFileName)
{
	ifstream f;
	f.open(szFileName, ios_base::binary | ios_base::in);
	if (!f.good() || f.eof() || !f.is_open()) {return 0;}
	f.seekg(0, ios_base::beg);
	ifstream::pos_type begin_pos = f.tellg();
	f.seekg(0, ios_base::end);
	return static_cast<int>(f.tellg() - begin_pos);
}

thanks for any help

Edited 6 Years Ago by VBNick: n/a

It could simply be because your upload speed isn't as fast as you think.
What if you try a direct FTP transfer to him?
- This doesn't apply if you are on LAN ofcourse, then speed should never be low as 15KB/s.

What if you change chunk size?

Have you tried debugging? Write to a log file with time stamps to the microsecond, each time an important action occurs.
- Starting to read from file.
- Read from file complete.
- Starting transfer of chunk.
- Done transfering chunk.
- Received receipt from chunk.
- Starting to read from file.


etc. :) Those data could be useful to determine where the problem lies.

I'm in the process of rearranging my code a little bit, but now its freezing. Instead of sending requests for more chunks, Instead I tried this:

1. once a transfer is accepted, no further requests are made for chunks. If when FD_READ is called, the program is in "accept files mode", then the chunk is written to the file. This should eliminated any lag caused on the receiving side.

case FD_READ:
int bytesRecv = recv(wParam, buffer, MSG_CHUNK_SIZE, 0);

ofstrSaveFile->write(buffer, bytesRecv);
bytesWritten += bytesRecv;
			
fPercent = int(float(bytesWritten) / float(trHeader_in.attrib) * 100.0f);
sprintf_s(fStatus, "Receiving file %i%%", fPercent);
SetWindowTextA(mWnd, fStatus);

if(bytesWritten >= trHeader_in.attrib) //if > filesize
{
	AS_AppState = AS_IDLE;
	ofstrSaveFile->close();
	SetWindowTextA(mWnd, "Desk Connect");
	IMSG("File transfer is complete");
}
break;

2. as soon as the program receives the command to accept the transfer, the following loop is called to send chunks until send() returns WSAEWOULDBLOCK.

while(send_error != WSAEWOULDBLOCK && !ifstrReadFile->eof())
{
	ZeroMemory(sendBuffer, MSG_CHUNK_SIZE);
	ifstrReadFile->read((char*)sendBuffer, MSG_CHUNK_SIZE);
	sizeSent = ifstrReadFile->gcount();
	totalSent += sizeSent;
	fPercent = int(float(totalSent) / float(trHeader_out.attrib) * 100.0f);
	sprintf_s(fStatus, "Sending file - %i%% %iBytes sent", fPercent, totalSent);
	SetWindowTextA(mWnd, fStatus);

	if(send(wParam, (char*)sendBuffer, sizeSent, 0) == SOCKET_ERROR)
		send_error = WSAGetLastError();
}

once WSAEWOULDBLOCK is raised, the program stops sending until it receives FD_WRITE, at which time it calls the same loop as above, each time, until the file transfer completes.


but now the program freezes during large transfers =/ and I'm not sure why.

it just turns grey and windows pops up a message saying it has stopped responding

Well, I cured the freezing problem, but then ended up with missing packets. I think I found the cause though. What I was doing was this:

1. receive acceptance
2. send chunks in a loop until send returns WSAEWOULDBLOCK
3. start up the sending loop again on each FD_WRITE event until finished

But I was losing packets. Adding a select() right before the send() seemed to fix it. I thought that when send() returned WSAEWOULDBLOCK, that the following FD_WRITE meant that the socket was ready for writing, because the data had finally been sent, but given the fact that placing select() before the send() instead of waiting for send() to return a WSAEWOULDBLOCK it appears that it was not sent.

can anyone confirm this?

edit: one more question while I have it in mind: Is there any way that I can assign WSAAsyncSelect directly to a thread procedure? or do I have to have another hidden window if I want it to have its own thread?

Edited 6 Years Ago by VBNick: n/a

Good news! the problem stated above did fix the problem with the missing chunks. Now that I don't have the restriction of waiting for the client to respond after each chunk, I have a file transfer zooming along at 112KB/s. If anyone wants it, I can throw up the final code once I tidy it up a little.

This article has been dead for over six months. Start a new discussion instead.