Sending a File via Winsock

Hello,

I am working on a project that should allow one person to transfer a file to another and vise versa via winsock. I am fairly new at this so I am sure there is more than one thing I am doing wrong. Anyhow I am not sure if I have to send the remote app the file size, location, and then the file, or if just sending the file is possible. Here is what I have broken into the send and receive scripts. It sends the file, but the file I receive has extra characters and junk in it.

Send
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
#define WIN32_LEAN_AND_MEAN

#include <windows.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#include <stdlib.h>
#include <stdio.h>
#include <iostream>
#include <sstream>
#include <fstream>
#include <string>

#pragma comment (lib, "Ws2_32.lib")


#define DEFAULT_BUFLEN 1024
#define DEFAULT_PORT "27015"

using namespace std;


int main()
{
		WSADATA wsaData;
		SOCKET ConnectSocket = INVALID_SOCKET;
		struct addrinfo *result = NULL,
                    *ptr = NULL,
                    hints;
		int iResult;
		int recvbuflen = DEFAULT_BUFLEN;
		char *newfilename;
		unsigned long iFileSize = 0;
		long size;     //file size
		ifstream infile (filename, ios::in|ios::binary|ios::ate);

		cout << "Enter the file to send:" << endl;
		getline(cin,filename);
		cout << endl;
		cout << "File is: " << filename << endl;
		system("PAUSE");
		
		//convert string to char
		size = infile.tellg();     //retrieve get pointer position
		infile.seekg (0, ios::beg);     //position get pointer at the begining of the file
		newfilename = new char [size];     //initialize the buffer
		infile.read (newfilename, size);     //read file to buffer
		infile.close();     //close file
		
		
		
		
		// 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(argv[1], DEFAULT_PORT, &hints, &result);
		if ( iResult != 0 ) {
			printf("getaddrinfo failed with error: %d\n", iResult);
			WSACleanup();
			return 1;
		}

		// Attempt to connect to an address until one succeeds
		for(ptr=result; ptr != NULL ;ptr=ptr->ai_next) {

			// Create a SOCKET for connecting to server
			ConnectSocket = socket(ptr->ai_family, ptr->ai_socktype, 
				ptr->ai_protocol);
			if (ConnectSocket == INVALID_SOCKET) {
				printf("socket failed with error: %ld\n", WSAGetLastError());
				WSACleanup();
				return 1;
			}

			// Connect to server.
			iResult = connect( ConnectSocket, ptr->ai_addr, (int)ptr->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;
		}
		
		//send file? 
		iResult = send( ConnectSocket, newfilename, (int)strlen(newfilename), 0 );
		if (iResult == SOCKET_ERROR) {
			printf("send failed with error: %d\n", WSAGetLastError());
			closesocket(ConnectSocket);
			WSACleanup();
			return 1;
		}
		
		// 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;
		}
		
		cout << "File Sent." << endl;

}


Receive
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
#define WIN32_LEAN_AND_MEAN

#include <windows.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#include <stdlib.h>
#include <stdio.h>
#include <iostream>
#include <sstream>
#include <string>
#include <fstream>

#pragma comment (lib, "Ws2_32.lib")


#define DEFAULT_BUFLEN 1024
#define DEFAULT_PORT "27015"

using namespace std;


int main()
{
	WSADATA wsaData;
        int iResult;
	char recvbuf[DEFAULT_BUFLEN];
	char clrbuf[DEFAULT_BUFLEN];
        int recvbuflen = DEFAULT_BUFLEN;
	int respbuflen = DEFAULT_BUFLEN;
	char filebuf[DEFAULT_BUFLEN];
	int filebuflen = DEFAULT_BUFLEN;

        SOCKET ListenSocket = INVALID_SOCKET;
        SOCKET ClientSocket = INVALID_SOCKET;
	
	cout << "Ready to receive file?" << endl;
	system("PAUSE");
	
	// 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_INET;
	hints.ai_socktype = SOCK_STREAM;
	hints.ai_protocol = IPPROTO_TCP;
	hints.ai_flags = AI_PASSIVE;

	// Resolve the server address and port
	iResult = getaddrinfo(NULL, DEFAULT_PORT, &hints, &result);
	if ( iResult != 0 ) {
		printf("getaddrinfo failed with error: %d\n", iResult);
		WSACleanup();
		return 1;
	}

	// Create a SOCKET for connecting to server
	ListenSocket = socket(result->ai_family, result->ai_socktype, result->ai_protocol);
	if (ListenSocket == INVALID_SOCKET) {
		printf("socket failed with error: %ld\n", WSAGetLastError());
		freeaddrinfo(result);
		WSACleanup();
		return 1;
	}

	// Setup the TCP listening socket
	iResult = bind( ListenSocket, result->ai_addr, (int)result->ai_addrlen);
	if (iResult == SOCKET_ERROR) {
		printf("bind failed with error: %d\n", WSAGetLastError());
		freeaddrinfo(result);
		closesocket(ListenSocket);
		WSACleanup();
		return 1;
	}

	freeaddrinfo(result);

	iResult = listen(ListenSocket, SOMAXCONN);
	if (iResult == SOCKET_ERROR) {
		printf("listen failed with error: %d\n", WSAGetLastError());
		closesocket(ListenSocket);
		WSACleanup();
		return 1;
	}
	cout << "Waiting for file..." << endl;
	// Accept a client socket
	ClientSocket = accept(ListenSocket, NULL, NULL);
	if (ClientSocket == INVALID_SOCKET) {
		printf("accept failed with error: %d\n", WSAGetLastError());
		closesocket(ListenSocket);
		WSACleanup();
		return 1;
	}
	// No longer need server socket
	closesocket(ListenSocket);
	cout << "Connected. Receive started." << endl;
	while (1) {
		// Read data into buffer.  We may not have enough to fill up buffer, so we
		// store how many bytes were actually read in bytes_read.
		bytes_read = recv(ClientSocket, filebuf, filebuflen, 0);
		if (bytes_read == 0) // We're done reading from the file
			break;

		if (bytes_read < 0) {
			// handle errors
			cout << "file read error";
		}

	}
	
	//write the buffer to a file.///

	infile = fopen("test.txt","wb");
	fwrite (filebuf, 1, sizeof(filebuf), infile);
	fclose (infile);
	
	
	
	cout << "File received!" << endl;
	system("PAUSE");
	return 0;
}

				


So the test file originally has "this is a test" in it, and the received file has "this is a testqjgrhare;ghag" and so on in it.

Any help would be greatly appreciated, as I am very new to winsock programming.
Here, you should instantiate infile after you know its name:

(Also, I don't see filename declared anywhere)

1
2
3
4
5
6
ifstream infile (filename, ios::in|ios::binary|ios::ate);

cout << "Enter the file to send:" << endl;
getline(cin,filename);
cout << endl;
cout << "File is: " << filename << endl;


Anyhow I am not sure if I have to send the remote app the file size, location, and then the file, or if just sending the file is possible.

What I would do is reserve a few bytes at the beginning of the message for the file size (maybe make it a unsigned int to be safe). Then, the rest of the message would store the actual file content. That way, the server would read the first (4) bytes so it could know the buffer size it needs to allocate. On the server side, it would look something like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
char fileSizeAsChars[4] = {0};

//keep calling recv until 4 bytes have been read
bytes_read = recv(ClientSocket, fileSizeAsChars, 4, 0);

unsigned int fileSize = /*construct an unsigned int from the 4 chars*/;

char fileBuffer = new char[fileSize];

//read the file (keep calling recv until fileSize bytes have been read)
//on subsequent calls to recv you'll need to start reading from further
//on in the fileBuffer and you'll need to read fileSize - bytes_read_so_far
//bytes (if applicable)
bytes_read = recv(ClientSocket, fileBuffer, fileSize, 0);

//use the file

delete [] fileBuffer;


Of course the server can keep reading until it finds the end of file byte (this has the ASCII value 4), but the server wouldn't know the size beforehand and you'd have to read one byte at a time (or N bytes at a time) and append the read bytes onto a master buffer.

It sends the file, but the file I receive has extra characters and junk in it.

You're assigning a buffer with a length of 1024. If the file is less than 1024 bytes, then the rest will be uninitialized garbage.

Instead of doing:

fwrite (filebuf, 1, sizeof(filebuf), infile);

you should do

fwrite (filebuf, 1, bytes_read_in_total, infile);

(and hopefully bytes_read_in_total is less than or equal to 1024. If you prepend the file size like I suggested, then you know how many bytes to read from the socket and how many to write to the output file).
Last edited on
Thanks for the speedy reply.

I understand what you mean about sending the file size at the beginning of the transfer before the file. But I seem to have a few problems with the implementation.

Firstly, I am not sure how to append the file's size to the front of itself before sending. It seems that when I try to send the file now, it receives the file name, but the file is 0 bytes.

Secondly, I seem to be having trouble converting from char to unsigned int, as the result seems to be 0 each time.

Here is a snippet of what I have now :

Receive
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52


                                bytes_read = recv(ClientSocket, fileSizeAsChars, 4, 0);

				cout << "bytes read so far: " << bytes_read << endl;

				//change from char to int
				strValue << fileSizeAsChars;
				strValue >> filesize;

				cout << "File size in bytes: " << filesize << endl;

				//set buffer size
				fbuffer = new char[filesize];

				//read the file (keep calling recv until fileSize bytes have been read)
				//on subsequent calls to recv you'll need to start reading from further
				//on in the fileBuffer and you'll need to read fileSize - bytes_read_so_far
				//bytes (if applicable)
				bytes_read = recv(ClientSocket, fbuffer, filesize, 0);

				cout << "bytes read so far: " << bytes_read << endl;

				//use the file

				//delete [] fbuffer;

				bytes_read_in_total = bytes_read;
				
				  resp = "File Received!";
				  cout << "If it was text only it received:" << fbuffer << endl;
				  cout << "File Receive attempt complete." << endl;
                                  
                                cout << "Total bytes read: " << bytes_read_in_total << endl;

				// Echo the buffer back to the sender
				iSendResult = send( ClientSocket, resp, respbuflen, 0 );
				if (iSendResult == SOCKET_ERROR) {
					printf("send failed with error: %d\n", WSAGetLastError());
					go = false;
					/*closesocket(ClientSocket);
					WSACleanup();
					return 1;*/
				}
				printf("Bytes sent: %d\n", iSendResult);

				//write the buffer to a file.///

				infile = fopen("test.txt","wb");
				fwrite (fbuffer, 1, bytes_read_in_total, infile);
				fclose (infile);
                                delete fbuffer[];


Thanks again for your time.
Last edited on
Firstly, I am not sure how to append the file's size to the front of itself before sending. It seems that when I try to send the file now, it receives the file name, but the file is 0 bytes.

Secondly, I seem to be having trouble converting from char to unsigned int, as the result seems to be 0 each time.

I may have mislead you. You don't have to physically change it from 4 bytes into an unsigned int (as I had in mind).

1
2
3
4
5
6
7
8
9
10
11
//client

//get file size
ifstream file("myFile.file", ios::binary);
file.seekg(0, ios::end);
unsigned int fileSize = file.tellg();
file.close();

//send the file size
unsigned int fileSizeSend = htonl(fileSize); //prepare the unsigned int for transmission
send( ConnectSocket, (char*) &fileSizeSend, sizeof(unsigned int), 0 );


1
2
3
4
5
6
7
8
//server

//get the file size (network byte ordering)
unsigned int fileSize = 0;
recv(ClientSocket, (char*) &fileSize, sizeof(unsigned int), 0);

//convert file size from network byte ordering
fileSize = ntohl(fileSize);


And of course, the other viable option is to send only the file contents (and be sure to put the end of file character '\4' at the end). Then, the server can read the file in chunks until it sees the end of file character. I'm beginning to think that this might be the better solution in your case because the server can just print out the characters as it reads them (no need to append to a master buffer).
Last edited on
When I try to do:
1
2
3
4
5
//server

//get the file size (network byte ordering)
unsigned int fileSize = 0;
recv(ClientSocket, &fileSize, sizeof(unsigned int), 0);


I get this error:
Error: argument of type "unsigned int *" is incompatible with parameter of type "char *"

So does this mean I have to convert?

Oh yeah, oops:

recv(ClientSocket, (char*) &fileSize, sizeof(unsigned int), 0);

Same thing with send:

send(ConnectSocket, (char*) &fileSizeSend, sizeof(unsigned int), 0 );

Fixed my code above.
Last edited on
Does this have a limit as far as file size? I was able to get it to send small text files, but if I send anything larger than 9999 bytes it seems to fail sending, is this normal?
MSDN wrote:
The maximum message packet size for a provider can be obtained by calling getsockopt with the optname parameter set to SO_MAX_MSG_SIZE to retrieve the value of socket option

It's very possible that your message could be exceeding the maximum message packet size. Try calling the above function and see. If that's the case, then you can send the file in chunks.
I believe that's whats happening, I can send small txt and even small exes without corrupting them, but anything over a certain size will not send correctly. I suppose the next step would be to send the files in chunks, but I have no idea on how to accomplish this. Any suggestions?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
const int FILE_CHUNK_SIZE = 5000; //or higher, just as long as it's under the max

//get file size
ifstream file("myFile.file", ios::binary);
file.seekg(0, ios::end);
unsigned int fileSize = file.tellg();
file.close();

//get the file
char* fileBuffer = new char[fileSize];
file.open("myFile.file", ios::binary);
file.seekg (0, ios::beg);
file.read (fileBuffer, fileSize);
file.close();

//send file in chunks
unsigned int bytesSent = 0;
int bytesToSend = 0;

while(bytesSent < fileSize)
{
    if(fileSize - bytesSent >= FILE_CHUNK_SIZE)
    {
        bytesToSend = FILE_CHUNK_SIZE;
    }
    else
    {
        bytesToSend = fileSize - bytesSent;
    }

    send(ConnectSocket, fileBuffer + bytesSent, bytesToSend, 0 );
    bytesSent += bytesToSend;
}

delete [] fileBuffer;


The server code should be unchanged. You might have to put the recv call in a loop until all the bytes have been received.
Topic archived. No new replies allowed.