So, I'm a relative newbie to network programming and programming in general, and I'm attempting to write a simple client/server text file transfer service. My code asks for the user to choose an upload or download option. When the user selects upload, the new file is created on the server's end, but isn't written with the data until the socket is closed. Also the string "upload" is appended onto the end of the text in the file.
I can't seem to find where my errors are, so any help would be greatly appreciated!
server.cpp
#define SIZE 1024
void write_file(int sockfd) // writing data to file function
{
int n;
FILE *fp;
char const *filename = "recv.txt";
char buffer[SIZE];
fp = fopen(filename, "w");
while (1)
{
n = recv(sockfd, buffer, SIZE, 0);
if (n <= 0)
{
break;
}
fprintf(fp, "%s", buffer);
bzero(buffer, SIZE);
}
fclose(fp);
return;
}
// in main
char msgRecv[10];
int n = 10;
while (n > 0)
{
rcv = read(connected_sd, &msgRecv, 10);
n -= rcv;
}
char msgUpload[10] = "upload";
if(strcmp(msgUpload, msgRecv) == 0)
{
write_file(connected_sd);
}
client.cpp
void send_file(FILE *points, int sockfd) // sending file through socket function
{
char bytes[SIZE] = {0};
bzero(bytes, SIZE);
while(fgets(bytes, SIZE, points) != NULL)
{
if(send(sockfd, bytes, sizeof(bytes), 0) == -1)
{
perror("Error in sending file.");
exit(1);
}
bzero(bytes, SIZE);
}
}
// in main
char msgUpload[10] = "upload";
send(sd, msgUpload, sizeof(msgUpload), 0);
string fileN;
cout << "What is the name of the file you wish to upload?\n";
cin >> fileN;
bzero(msgUpload, sizeof(msgUpload));
FILE *file;
char const *filename = fileN.c_str();
file = fopen(filename, "r");
if (file == NULL)
{
perror("Error in reading file.\n");
exit(1);
}
send_file(file, sd);
printf("File data sent successfully.\n\n");
fclose(file);
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 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.
When i send the file to the client it gets corrupted, and with a size in bytes higher.
I have a version of this server running on Windows and works perfectly,but I'm not having the same result on Linux.
The file size on disk may be the error in time to send the size in bytes to the client that runs on another platform?
fread function is being used correctly?
an expert can analyze and help find the error?
LINUX SERVER SIDE
// FUNCTION TO UPLOAD A FILE TO CLIENT WINDOWS
int Socket_Setup::FILE_UPLOAD(int iD, std::string DIR_UPLOAD)
{
char Block[1024];
long FileSize;
int BytesRead;
fp = fopen(DIR_UPLOAD.c_str(), "rb");
if (!fp)
{
errno_message.append((char*)strerror(errno));
FUNCTION_LOG(errno_message);
return 1;
}
fseek(fp, 0, SEEK_END);
FileSize = ftell(fp);
rewind(fp);
long Size_Send = htonl(FileSize);
Total = FileSize;
// Sending the file size to the Windows Client
iResult = send(client[iD].socket, (const char*)&Size_Send, sizeof(long), 0);
if (iResult <= 0)
{
errno_message.append((char*)strerror(errno));
FUNCTION_LOG(errno_message);
return 1;
}
// LOOP TO SEND FILE
while (FileSize > 0)
{
BytesRead = fread(Block, 1, sizeof(Block), fp);
if (BytesRead <= 0)
{
errno_message.append((char*)strerror(errno));
FUNCTION_LOG(errno_message);
fclose(fp);
return 1;
}
if (send(client[iD].socket, Block, BytesRead, 0) != BytesRead)
{
errno_message.append((char*)strerror(errno));
FUNCTION_LOG(errno_message);
fclose(fp);
return 1;
}
FileSize -= BytesRead;
}
fclose(fp);
return 0;
}
WINDOWS CLIENT SIDE:
int readBytes(SOCKET s, void *buffer, int buflen)
{
int total = 0;
char *pbuf = (char*)buffer;
while (buflen > 0)
{
int iResult = recv(s, pbuf, buflen, 0);
if (iResult < 0)
{
if (WSAGetLastError() == WSAEWOULDBLOCK)
continue;
return SOCKET_ERROR;
}
else if (iResult == 0)
return 0;
else
{
pbuf += iResult;
buflen -= iResult;
total += iResult;
}
}
return total;
}
// FUNCTION TO DOWNLOAD FILE FROM SERVER
int Remote_Manip::FILE_DOWNLOAD(std::string directory, Socket_Setup &socket_setup)
{
unsigned long FileSize;
char mfcc[65535];
File = fopen(directory.c_str(), "wb");
if (File == NULL)
{
closesocket(socket_setup.ConnectSocket);
WSACleanup();
return 1;
}
// Receiving file size from server
int iResult = readBytes(socket_setup.ConnectSocket, &FileSize, sizeof(FileSize));
if (iResult <= 0)
{
fclose(File);
closesocket(socket_setup.ConnectSocket);
WSACleanup();
return 1;
}
FileSize = ntohl(FileSize);
// LOOP TO RECEIVING FILE
while (FileSize > 0)
{
int Received = recv(socket_setup.ConnectSocket, mfcc, sizeof(mfcc),0);
if (Received <= 0)
{
fclose(File);
closesocket(socket_setup.ConnectSocket);
WSACleanup();
return 1;
}
if (fwrite(mfcc, 1, Received, File) != Received)
{
fclose(File);
closesocket(socket_setup.ConnectSocket);
WSACleanup();
return 1;
}
FileSize -= Received;
}
fflush(File);
fclose(File);
return 0;
}
Im trying to send and receive 2 data back to back on tcp socket. Protocol is written below.
Client send data
On receiving the data on sever it sends back to client
Now using below client code I'm not able to get 2nd data and I think the 'Recv' function doing something wrong. Below is the code snippet.
int Recv(char* buffer, int size)
{
int total = 0, n = 0;
while((n = ::recv(m_hSocket, buffer+total, size-total-1, 0)) > 0)
{
total += n;
}
buffer[total] = 0;
return total;
}
int SendAndReceiveData()
{
//CStringA cstData :: this data getting filled by some other code. Ignore!
//Send data
char chSendBuff[256];
memset(chSendBuff, 0, sizeof(chSendBuff));
sprintf_s(chSendBuff, sizeof(chSendBuff), "%s", (LPCTSTR)cstData);
send(m_hSocket, chSendBuff, (int)strlen(chSendBuff), 0);
//Read response
char chRecvBuff[256];
memset(chRecvBuff, 0, sizeof(chRecvBuff));
int iRet = Recv(chRecvBuff, 256);
}
Your receive function should look like this:
int receive(int sockfd, void *buf, size_t len, int flags)
{
size_t toread = len;
char *bufptr = (char*) buf;
while (toread > 0)
{
ssize_t rsz = recv(sockfd, bufptr, toread, flags);
if (rsz <= 0)
return rsz; /* Error or other end closed connection */
toread -= rsz; /* Read less next time */
bufptr += rsz; /* Next buffer position to read into */
}
return len;
}