Related
This question already has answers here:
C: send file to socket
(4 answers)
Closed 2 years ago.
I want to send files over TCP sockets in C++ on Windows, all is working absolutely fine, however I can't send big files like this, I understand that TCP as any protocol has it's limitations, like I can't send more than 64KB per packet, my method works for small file sizes(tested all up to 12KB), but I would like to send LARGE files, like iso image of ubuntu or windows, which are surely bigger than 12 fully packed packets and etc.
Server
int filesize = 0;
int err = recv(conn, (char*)&filesize, sizeof(filesize), 0);
if (err <= 0)
{
printf("recv: %d\n", WSAGetLastError());
clean(conn);
}
printf("recv %d bytes [OK]\n", err);
char* buffer = new char[filesize];
ZeroMemory(buffer, filesize);
err = recv(conn, buffer, filesize, MSG_WAITALL);
if (err <= 0)
{
printf("recv: %d\n", WSAGetLastError());
clean(conn);
}
printf("recv %d bytes [OK]\n", err);
ofstream file("a.txt", ios::binary);
file.write(buffer, filesize);
delete[] buffer;
file.close();
Client
ifstream file("a.txt", ios::binary);
file.seekg(0, ios::end);
int size = file.tellg();
file.seekg(0, ios::beg);
char* buffer = new char[size];
file.read(buffer, size);
file.close();
int* fsize = &size;
int err = send(client, (char*)fsize, sizeof(int), 0);
if (err <= 0)
{
printf("send: %d\n", WSAGetLastError());
}
printf("send %d bytes [OK]\n", err);
err = send(client, buffer, size, 0);
if (err <= 0)
{
printf("send: %d\n", WSAGetLastError());
}
printf("send %d bytes [OK]\n", err);
delete[] buffer;
All values for both sides are initialised, and error handling is done well, and if I had problem then I would have said about that. I decided to use MSG_WAITALL because I guess that is suitable for this case, please correct my code for recieving/sending and if possible refactor it, it would be nicer if it would be with explainations, so that evrybody could learn to code better, thanks)))
The one main point that should be taken away from the comments below your question is that send and recv are fickle. Just because you write send(buffer with 100 bytes) doesn't mean it's going to send 100 bytes. It could send 25 bytes, or 99 bytes, or fail out completely. It's up to you to take the return value and compute what needs to still be sent.
Same goes with recv. If you write recv(buffer with 100 bytes) because you are expecting 100 bytes, it could only grab 25 bytes, or 99 bytes, or fail out completely. Again, it's up to you to use that return value and compute what still needs to be received.
File I/O is completely different. If you want to write 100 bytes to a file, those 100 bytes are guaranteed to be written if the method doesn't fail. So, when folks who have worked with file I/O move to socket I/O usually end up confused why things aren't sending or receiving correctly.
One of the trickier parts to socket programming is knowing how much data you will need to receive. You covered that by sending the length of the file first. The server will know to read in that value, then continue reading until that value is satisfied.
Some protocols, like HTTP, will use delimiters (in HTTP's case \r\n\r\n) to signal when a packet of data has ended. So, as a socket programmer, you would recv on a loop until those 4 bytes are read.
I put together an example on how you could accomplish sending and receiving a large file (this will handle files up to 9,223,372,036,854,775,807 in length). This isn't pure C++, I cheated in places because of lack of time. I used some Windows-only constructs for the same reason.
So let's take a look at it:
int64_t GetFileSize(const std::string& fileName) {
// no idea how to get filesizes > 2.1 GB in a C++ kind-of way.
// I will cheat and use Microsoft's C-style file API
FILE* f;
if (fopen_s(&f, fileName.c_str(), "rb") != 0) {
return -1;
}
_fseeki64(f, 0, SEEK_END);
const int64_t len = _ftelli64(f);
fclose(f);
return len;
}
///
/// Recieves data in to buffer until bufferSize value is met
///
int RecvBuffer(SOCKET s, char* buffer, int bufferSize, int chunkSize = 4 * 1024) {
int i = 0;
while (i < bufferSize) {
const int l = recv(s, &buffer[i], __min(chunkSize, bufferSize - i), 0);
if (l < 0) { return l; } // this is an error
i += l;
}
return i;
}
///
/// Sends data in buffer until bufferSize value is met
///
int SendBuffer(SOCKET s, const char* buffer, int bufferSize, int chunkSize = 4 * 1024) {
int i = 0;
while (i < bufferSize) {
const int l = send(s, &buffer[i], __min(chunkSize, bufferSize - i), 0);
if (l < 0) { return l; } // this is an error
i += l;
}
return i;
}
//
// Sends a file
// returns size of file if success
// returns -1 if file couldn't be opened for input
// returns -2 if couldn't send file length properly
// returns -3 if file couldn't be sent properly
//
int64_t SendFile(SOCKET s, const std::string& fileName, int chunkSize = 64 * 1024) {
const int64_t fileSize = GetFileSize(fileName);
if (fileSize < 0) { return -1; }
std::ifstream file(fileName, std::ifstream::binary);
if (file.fail()) { return -1; }
if (SendBuffer(s, reinterpret_cast<const char*>(&fileSize),
sizeof(fileSize)) != sizeof(fileSize)) {
return -2;
}
char* buffer = new char[chunkSize];
bool errored = false;
int64_t i = fileSize;
while (i != 0) {
const int64_t ssize = __min(i, (int64_t)chunkSize);
if (!file.read(buffer, ssize)) { errored = true; break; }
const int l = SendBuffer(s, buffer, (int)ssize);
if (l < 0) { errored = true; break; }
i -= l;
}
delete[] buffer;
file.close();
return errored ? -3 : fileSize;
}
//
// Receives a file
// returns size of file if success
// returns -1 if file couldn't be opened for output
// returns -2 if couldn't receive file length properly
// returns -3 if couldn't receive file properly
//
int64_t RecvFile(SOCKET s, const std::string& fileName, int chunkSize = 64 * 1024) {
std::ofstream file(fileName, std::ofstream::binary);
if (file.fail()) { return -1; }
int64_t fileSize;
if (RecvBuffer(s, reinterpret_cast<char*>(&fileSize),
sizeof(fileSize)) != sizeof(fileSize)) {
return -2;
}
char* buffer = new char[chunkSize];
bool errored = false;
int64_t i = fileSize;
while (i != 0) {
const int r = RecvBuffer(s, buffer, (int)__min(i, (int64_t)chunkSize));
if ((r < 0) || !file.write(buffer, r)) { errored = true; break; }
i -= r;
}
delete[] buffer;
file.close();
return errored ? -3 : fileSize;
}
Sending and Receiving Buffers
At the top we have two methods that works with buffers in memory. You can send it any buffer at any size (stay reasonable here), and those methods will send and receive until all the bytes passed in have been transmitted.
This does what I was talking about above. It takes the buffer and loops until all the bytes have been successfully sent or received. After these methods complete, you are guaranteed that all data is transmitted (as long as the return value is zero or positive).
You can define a "chunk size" which is the default size of the chunks of data the methods will use to send or receive data. I am sure these can be optimized by using more suitable values than what they are currently set at, but I don't know what those values are. It's safe to leave them at the default. I don't think that with the speed of today's computers you will notice too much of a difference if you change it to something else.
Sending and Receiving Files
The code for doing files is almost identical in nature to the buffer code. Same idea, except now we can assume that if the return value is greater than zero from the buffer methods then it was successful. So the code is a little simpler. I use a chunk size of 64KB... for no special reason. This time the chunk size determines how much data is read from the file I/O operations, not the sockets I/O.
Test Server and Client
Just to be complete, I used this code below to test this with a 5.3 GB file I have on disk. I basically just re-wrote Microsoft's client/server examples in a very slimmed down way.
#pragma comment(lib, "Ws2_32.lib")
#include <iostream>
#include <winsock2.h>
#include <ws2tcpip.h>
#include <fstream>
DWORD __stdcall ClientProc(LPVOID param) {
struct addrinfo hints = { 0 }, * result, * ptr;
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
if (getaddrinfo("127.0.0.1", "9001", &hints, &result) != 0) {
return ~0;
}
SOCKET client = INVALID_SOCKET;
for (ptr = result; ptr != NULL; ptr = ptr->ai_next) {
client = socket(ptr->ai_family, ptr->ai_socktype, ptr->ai_protocol);
if (client == SOCKET_ERROR) {
// TODO: failed (don't just return, cleanup)
}
if (connect(client, ptr->ai_addr, (int)ptr->ai_addrlen) == SOCKET_ERROR) {
closesocket(client);
client = INVALID_SOCKET;
continue;
}
break;
}
freeaddrinfo(result);
if (client == SOCKET_ERROR) {
std::cout << "Couldn't create client socket" << std::endl;
return ~1;
}
int64_t rc = SendFile(client, "D:\\hugefiletosend.bin");
if (rc < 0) {
std::cout << "Failed to send file: " << rc << std::endl;
}
closesocket(client);
return 0;
}
int main()
{
WSADATA wsaData;
WSAStartup(MAKEWORD(2, 2), &wsaData);
{
struct addrinfo hints = { 0 };
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
hints.ai_flags = AI_PASSIVE;
struct addrinfo* result = NULL;
if (0 != getaddrinfo(NULL, "9001", &hints, &result)) {
// TODO: failed (don't just return, clean up)
}
SOCKET server = socket(result->ai_family, result->ai_socktype, result->ai_protocol);
if (server == INVALID_SOCKET) {
// TODO: failed (don't just return, clean up)
}
if (bind(server, result->ai_addr, (int)result->ai_addrlen) == INVALID_SOCKET) {
// TODO: failed (don't just return, clean up)
}
freeaddrinfo(result);
if (listen(server, SOMAXCONN) == SOCKET_ERROR) {
// TODO: failed (don't just return, clean up)
}
// start a client on another thread
HANDLE hClientThread = CreateThread(NULL, 0, ClientProc, NULL, 0, 0);
SOCKET client = accept(server, NULL, NULL);
const int64_t rc = RecvFile(client, "D:\\thetransmittedfile.bin");
if (rc < 0) {
std::cout << "Failed to recv file: " << rc << std::endl;
}
closesocket(client);
closesocket(server);
WaitForSingleObject(hClientThread, INFINITE);
CloseHandle(hClientThread);
}
WSACleanup();
return 0;
}
I am trying to send images through websocket from one javascript/html client to another. The problem is that the server is incorrectly receiving the image. I am sending all the images as data URI's in text so that when the javascript client receives it, it can just simply set the src of the img to the URI. The problem (I believe) comes from how I am handling message fragmentation. Sending simple text messages work fine so I led to believe its the size of the message that's causing issues, and the only main code difference is how I handle message fragmentation. From this documentation, I am led to believe that all that must be done is to unmask the payload of each fragmented frame and concatenate the buffers together. The URI read on the server is sufficiently shorter than the actual data URI of the image. On the client end all I am doing is calling the socket.send() function. I have confirmed that the data URI I read in the javascript FileReader is correct (on the client side).
int wSock::readData(/*input socket data buffer*/ char ** sockp, /*output payload*/ char ** buffer, /*output payload info*/ WebSocketFrameData * data) {
char * sock = *sockp;
if (!webSocketIsOpened(sock)) return -32; //checks if the socket is open
u_long package_size;
SOCKET socket;
size_t dataRead = 0;
size_t dr = 0;
size_t firstLength = 0;
memcpy_s(&socket, 4, sock, 4);
ioctlsocket(socket, FIONREAD, &package_size);
if (package_size <= 0) return 1;
char * buf = new char[package_size + 1];
while (dataRead < package_size) {
dr = recv(socket, buf + dataRead, package_size - dataRead, NULL);
if (dr == SOCKET_ERROR) {
delete[] buf;
return WSAGetLastError();
}
dataRead += dr;
}
*(buf + package_size) = '\0';
if (package_size > 0) {
decodeFrame(buf, buffer, &firstLength);
if (data != NULL) {
data->payloadLength = firstLength;
data->opcode = *buf & 0b00001111;
}
}
else return 1;
// code handling other opcodes such as a close frame or a ping
char fin = (*buf) >> 7;
if (!fin) { //start handling message fragmentation
printf("Fragmentation! \n");
FD_SET tempRead;
size_t totalLength = firstLength -1; //firstLength includes the null terminator
char * combinedPayloads = new char[totalLength];
memcpy_s(combinedPayloads, totalLength, *buffer, totalLength);
printf("First frage of size: %u \n", totalLength);
while (fin != 1) {
FD_ZERO(&tempRead);
FD_SET(socket, &tempRead);
select(0, &tempRead, NULL, NULL, NULL);
package_size = 0;
ioctlsocket(socket, FIONREAD, &package_size);
printf("Reading next frag of size: %u \n", package_size);
char * contBuf = new char[package_size];
dataRead = 0;
while (dataRead < package_size) {
dr = recv(socket, contBuf + dataRead, package_size - dataRead, NULL);
if (dr == SOCKET_ERROR) {
delete[] contBuf;
return WSAGetLastError();
}
dataRead += dr;
}
char * payload;
size_t payloadLength = 0;
decodeFrame(contBuf, &payload, &payloadLength);
payloadLength--; //the output payloadLength from the decodeFrame function includes a null terminator
char * backBuffer = new char[totalLength];
memcpy_s(backBuffer, totalLength, combinedPayloads, totalLength);
delete[] combinedPayloads;
combinedPayloads = new char[totalLength + payloadLength];
memcpy_s(combinedPayloads, totalLength, backBuffer, totalLength);
memcpy_s(combinedPayloads + totalLength, payloadLength, payload, payloadLength);
fin = contBuf[0] >> 7;
totalLength += payloadLength;
delete[] backBuffer;
delete[] contBuf;
delete[] payload;
if (fin) break;
}
delete[] *buffer;
*buffer = new char[totalLength + 1];
memcpy_s(*buffer, totalLength, combinedPayloads, totalLength);
(*buffer)[totalLength] = '\0';
delete[] combinedPayloads;
data->payloadLength = totalLength;
printf("Finished fragment! Total size: %u \n", totalLength);
}
delete[] buf;
return 0;
}
And this is the code for decoding each websocket frame. As I mentioned the server works fine for smaller chat messages so I assume the problem is the message re-assembling but I will include the decodeFrame function with hopes that it well help understanding.
int wSock::decodeFrame(char * message, char ** output, size_t * payloadLength)
{
char read;
memcpy_s(&read, 1, message + 1, 1);
unsigned long long size = read & 0b01111111;
//takes bits 9 - 15;
int lastByte = 2;
if (size == 126) {
unsigned short rr;
memcpy_s(&rr, 2, message + 2, 2);
size = ntohs(rr);
lastByte = 4;
}
else if (size == 127) {
unsigned long long data;
memcpy_s(&data, 8, message + 2, 8);
size = ntohll(data);
lastByte = 10;
}
if(payloadLength != NULL)
*payloadLength = size + 1;
char mask[4];
memcpy_s(mask, 4, message + lastByte, 4);
*output = new char[(size + 1)];
lastByte += 4;
for (int i = 0; i < size; i++) {
(*output)[i] = message[lastByte + i] ^ mask.mask[i % 4];
}
(*output)[size] = '\0';
return 0;
}
On the server side for debugging, I took the read message and wrote it into a text file. However, the URI that was written is only about 4,000 - 6,000 characters long and the last 200 - 400 characters are not valid base64 characters, however the characters before these invalid characters do match their corresponding characters on the real data URI. The printf statement during the re-assembling process will tend to read about 262,368 bytes (total) while the actual URI is 389,906 characters long. After reading the URI the server sends it to the clients, which causes them to disconnect. So as I mentioned my guess is that something is going wrong when I'm re-assembling the data frames. Any help will be appreciated.
ioctlsocket(socket, FIONREAD, &package_size);
FIONREAD returns the number of bytes that can be read without blocking. This means that the recv() loop following this line of code is completely futile One recv() will read that amount of data. It can't not.
You are also not handling end of stream correctly (recv() returns zero).
OK, I figured it out. What I forgot to account for was TCP message fragmentation. As #EJP mentioned, ioctlsocket returns only the amount of bytes that can be read in one single recv() call. I was treating each fragment of data received as its own WebSocket frame while that wasn't always the case. Often times (almost all the time) the single recv() call would only read a partial frame, and the next part of the first frame would be read with the first part of the second frame at the second recv() call. Then the second buffer (which is now a mix of two different incomplete frames) would obviously not be demasked properly and the decoded size would be incorrect. The javascript client was fragmenting each WebSocket frame at around 131K bytes and the underlying TCP layer would fragment those frames further into around 65K byte packets. So what I did is I received all the data I could in a single recv() call, and then use a function along the lines of:
unsigned long long wSock::decodeTotalFrameSize(char * frame)
{
char secondByte = 0;
memcpy_s(&secondByte, 1, frame + 1, 1);
unsigned long long size = secondByte & 0b01111111;
int headerSize = 2 + 4;
if (size == 126) {
unsigned short length;
memcpy_s(&length, 2, frame + 2, 2);
size = ntohs(length);
headerSize += 2;
}
else if (size == 127) {
unsigned long long length;
memcpy_s(&length, 8, frame + 2, 8);
size = ntohll(length);
headerSize += 8;
}
return size + headerSize;
}
to get the total WebSocket frame size. Then loop until you read that amount of bytes into a single frame. Something similar to:
FD_ZERO(&tempRead);
FD_SET(socket, &tempRead);
select(0, &tempRead, NULL, NULL, NULL);
package_size = 0;
ioctlsocket(socket, FIONREAD, &package_size);
char * contBuf = new char[package_size];
dataRead = 0;
dr = recv(socket, contBuf, package_size, NULL);
if (dr == SOCKET_ERROR) {
delete[] contBuf;
return WSAGetLastError();
}
unsigned long long actualSize = decodeTotalFrameSize(contBuf);
if (package_size < actualSize) {
char * backBuffer = new char[package_size];
memcpy_s(backBuffer, package_size, contBuf, package_size);
delete[] contBuf;
contBuf = new char[actualSize];
memcpy_s(contBuf, actualSize, backBuffer, package_size);
delete[] backBuffer;
dataRead = package_size;
dr = 0;
while (dataRead < actualSize) {
dr = recv(socket, contBuf + dataRead, actualSize - dataRead, NULL);
if (dr == SOCKET_ERROR) {
delete[] contBuf;
return WSAGetLastError();
}
else if (dr == 0) break;
dataRead += dr;
}
printf("Read total frag of %u \n", dataRead);
}
I want to read all messages that are sent from the client.
I am implementing a tcp server and it receives data. Each message is appended by the size of the message as a header. So Now I can read the header and find the size from that and allocate that much memory for the message to be read later. However, with my very little exposure to C++ this is what I came up with.
How to read all messages ?
void *dothistask(void *socket_desc)
{
int sock = *(int*)socket_desc;
free(socket_desc);
int read_size;
unsigned int x = 4;
char *header = (char*)malloc(sizeof(char) * 4);
char *message;
int index = 0;
long p;
int status;
while(true) {
status = ReadXBytes(sock, 4, header);
if(status == -1)
{
break;
}
message = (char *)malloc(sizeof(char) * 10);
status = ReadXBytes(sock, 10, message);
if(status == -1)
{
break;
}
cout<<"The header is "<<header<<endl;
cout<<"The message is "<<message<<endl;
}
return 0;
}
int ReadXBytes(int socket, unsigned int x, void* buff)
{
char *buffer = (char*)buff;
int bytesRead = 0;
int result;
while (bytesRead < x)
{
result = recv(socket, buffer + bytesRead, x - bytesRead, 0);
if(result == 0)
{
cout<<"Client disconnected"<<endl;
fflush(stdout);
return -1;
}
else if( result == -1)
{
perror("recv failed");
return -1;
}
bytesRead += result;
}
return 0;
}
Read that it is ideal to read the header first and then read the message. I am able to do this once but I want to do this over a repeated period of time, basically forever, till the client disconnects.
Thank you! for the help!
To read the message, you have
ReadXBytes(sock, 10, message);
but it should be something like
ReadXBytes(sock, *((int*)header), message);
depending on the content of header. As you have a hard-coded 10 in there, you will only ever read 10 bytes. You will also have to adjust the malloc accordingly to not only allocate 10 bytes.
I will rephrase the whole question here so that it is answerable.
I am able to copy binary file perfectly in the same machine not using sockets but just making a simple copy function. Trying to implement this code for copying onto a TCP/IP connection but can't get it to work.
FILE *filehandle = fopen("imagefile.jpg", "rb");
FILE *dest =fopen("imagecopy.jpg", "wb"); // copied image file
fseek(filehandle, 0, SEEK_END);
unsigned long filesize = ftell(filehandle);
char *buffer = (char*)malloc(sizeof(char)*filesize);
rewind(filehandle);
int bytesread = fread(buffer, sizeof(char), filesize, filehandle);
for( int i=0; i<filesize; i++ )
{
fputc(buffer[i], filehandle); // copies all the contents to dest
}
The code above works perfectly for copying an image file in the computer but when implemented to copy on server, it is difficult to go about it.
I am trying to send an image file from a server to a client both which have been made manually in C. The length of the file to be sent by the server is only known to the server when it's sending the file so the buffer is dynamically generated in the server, something like this:
SERVER
fseek(filehandle, 0, SEEK_END);
long filesize = ftell(filehandle); // file could be 11000bytes
char *buffer = (char*)malloc(sizeof(char)*filesize); // char buffer with 11000 bytes to store the data from the file.
// then I call the send() function
rewind(filehandle); // go back to beginning
send(clientsocket, buffer, filesize, 0); // this is being sent perfectly, no errors because in the actual code, I am checking for errors
CLIENT
// here is where I don't understand how to dynamically allocate the 11000 bytes to store the data in a client buffer
// the filesize is not necessarily going to be 11000 so need to dynamically allocate
// I did the following:
#define BUFSIZE 10
FILE *filehandle = fopen("imagefile.jpg", "wb"); // image file created by client
char *buffer = (char*)malloc(sizeof(char)*BUFSIZE);
int bytesread = recv(buffer, 1, strlen(buffer), 0);
if( bytesread > 0 )
{
printf("Bytes read: %d\n", bytesread); // bytes read is 5
printf("Buffer: %s\n", buffer); // but buffer shows all the binary text like it normally would
// when I try to store buffer in a file, it doesn't put full buffer because only 5 characters are written
for( int i=0; i<bytesread; i++ )
{
fputc(buffer[i], filehandle); // this doesn't create full image
}
}
How can I dynamically allocate the 11000 bytes sent by the server?
You need to loop both the sending and receiving. Neither send() nor recv() are guaranteed to send/read as many bytes as you requested.
You also should send the file size before the file data so the receiver knows how many bytes to expect and when to stop reading.
Try something more like this:
SERVER
bool senddata(SOCKET sock, void *buf, int buflen)
{
unsigned char *pbuf = (unsigned char *) buf;
while (buflen > 0)
{
int num = send(sock, pbuf, buflen, 0);
if (num == SOCKET_ERROR)
{
if (WSAGetLastError() == WSAEWOULDBLOCK)
{
// optional: use select() to check for timeout to fail the send
continue;
}
return false;
}
pbuf += num;
buflen -= num;
}
return true;
}
bool sendlong(SOCKET sock, long value)
{
value = htonl(value);
return senddata(sock, &value, sizeof(value));
}
bool sendfile(SOCKET sock, FILE *f)
{
fseek(f, 0, SEEK_END);
long filesize = ftell(f);
rewind(f);
if (filesize == EOF)
return false;
if (!sendlong(sock, filesize))
return false;
if (filesize > 0)
{
char buffer[1024];
do
{
size_t num = min(filesize, sizeof(buffer));
num = fread(buffer, 1, num, f);
if (num < 1)
return false;
if (!senddata(sock, buffer, num, 0))
return false;
filesize -= num;
}
while (filesize > 0);
}
return true;
}
FILE *filehandle = fopen("imagefile.jpg", "rb");
if (filehandle != NULL)
{
sendfile(clientsocket, filehandle);
fclose(filehandle);
}
CLIENT
bool readdata(SOCKET sock, void *buf, int buflen)
{
unsigned char *pbuf = (unsigned char *) buf;
while (buflen > 0)
{
int num = recv(sock, pbuf, buflen, 0);
if (num == SOCKET_ERROR)
{
if (WSAGetLastError() == WSAEWOULDBLOCK)
{
// optional: use select() to check for timeout to fail the read
continue;
}
return false;
}
else if (num == 0)
return false;
pbuf += num;
buflen -= num;
}
return true;
}
bool readlong(SOCKET sock, long *value)
{
if (!readdata(sock, value, sizeof(value)))
return false;
*value = ntohl(*value);
return true;
}
bool readfile(SOCKET sock, FILE *f)
{
long filesize;
if (!readlong(sock, &filesize))
return false;
if (filesize > 0)
{
char buffer[1024];
do
{
int num = min(filesize, sizeof(buffer));
if (!readdata(sock, buffer, num))
return false;
int offset = 0;
do
{
size_t written = fwrite(&buffer[offset], 1, num-offset, f);
if (written < 1)
return false;
offset += written;
}
while (offset < num);
filesize -= num;
}
while (filesize > 0);
}
return true;
}
FILE *filehandle = fopen("imagefile.jpg", "wb");
if (filehandle != NULL)
{
bool ok = readfile(clientsocket, filehandle);
fclose(filehandle);
if (ok)
{
// use file as needed...
}
else
remove("imagefile.jpg");
}
We could avoid the header that contains the image size, but we just read to the end of the sent data. About the buffer size, we could use a fixed number such as 10 * 1024, when we received some data from the server, we just save it into a file according to the actual received data length.
// please open a file ...
FILE * fp;
// ...
const int LENGTH = 10 * 1024;
int len = 0;
char * buffer = (char *)malloc(LENGTH);
while ((len = recv(socket, buffer, LENGTH, 0)) > 0) {
fwrite(buffer, 1, len, fp);
}
free(buffer);
// close the file
#T.C: I guess we cannot allocate a buffer according to the size sent from the server in case the image is too large to save inside the client's memory. Not mention the server is fake, and intended to make any attack.
I wrote a simple FTP application that can send files back and forth between a client and a server and it was working fine. More recently I wrote a socket library to use with the client and server, some of the functionality has changed, and I'm having trouble wrapping my head around how to get it to work. At the moment I'm integrating the library on my server.
The issue is that a part of my specs are to hide the socket handle with the library, so I've wrapped 'recv' and 'send' in library functions that pass a char pointer by reference. Before I implemented this I was passing char[] directly into the recv functions which was coming out nicely enough for my purposes. Now that I'm using a char* it would seem like I need to know the exact length of the incoming message as my char* is coming out with the send data as well as garbage characters.
Here is a part of my server code:
while (true)
{
command = (char*)malloc(sizeof(char)*32);
int bytesRecv = socketObject.receiveData('c', &command, 32);
if(_stricmp(command,"mput") == 0)
{
while( true ) {
SizeCheck = 0;
FileSize = 0;
fileName = (char*)malloc(sizeof(char)*1024);
bytesRecv = socketObject.receiveData('c', &fileName, 1024);
if(_stricmp(fileName,"goodbye") == 0)
{
break;
}
while( true )
{
char GotFileSize[1024];
GotFileSize = (char*)malloc(sizeof(char)*1024);
socketObject.receiveData('c', &sentSize, 1024);
FileSize = atoi(GotFileSize);
if (FileSize > 0)
{
break;
}
}
mfcc = (char*)malloc(sizeof(char)*FileSize);
FILE *fp;
if( (fp = fopen(fileArray, "wb")) == NULL)
{
std::cout << "fopen has caused an error" << endl;
}
while(SizeCheck < FileSize){
int Received = socketObject.receiveData('f', &mfcc, FileSize);
if(Received != SOCKET_ERROR)
{
std::cout << "Before fwrite" << std::endl;
int written = fwrite(mfcc, 1, FileSize, fp);
SizeCheck += Received;
fflush(fp);
}
}//while transfer
}//while true
}//end mput if
Here is my receive function:
int Socket::receiveData( char socketType, char** data, int size)
{
if(socketType != 'f' && socketType != 'c')
{
return -1;
}
if(socketType == 'f')
{
int bytes = recv( fAccepted, *data, size, 0);
return bytes;
}
else if(socketType == 'c')
{
int bytes = recv( cAccepted, *data, size, 0);
if (bytes == SOCKET_ERROR) {
printf("send failed: %d\n", WSAGetLastError());
}
return bytes;
}
return -1;
}
I've done some reading up on recv that tells me I should somehow either send the size of the filename with the filename, or compile a full string in a loop. I'm not sure if these methods are appropriate for what I'm attempting to do, or if there is an easier way.
The receiveData function is perfectly fine: It writes the received bytes to the buffer, and returns the number of bytes that were received.
All other bytes in the buffer can and should be ignored.
In your current code, each time you receive data, you're writing the entire buffer to the file, even though receiveData tells you precisely how much data you should write.
Namely, you shouldn't do
int written = fwrite(mfcc, 1, FileSize, fp);
but instead
int written = fwrite(mfcc, 1, Received, fp);
You should consider using a more reasonable buffer size, such as 1500 bytes (the usual MTU for network packets), or 1MB (something that should fit into the RAM without issues), instead of the full filesize.
By the way, there is no need to pass data as a double pointer, or, as you call it, as a reference to a pointer. Just pass it as a normal pointer. But that has nothing to do with your 'garbage data' issue.