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;
}
Related
I am facing a very strange problem. I have a server application that runs UDP socket and wait for incoming data. As soon as it gets the command it begins to send back a stream. Just for testing, I limit the server to sending only one piece of data 8000 bytes long. I don't provide the server code since it work as expected. It receives the command and sends data back, I can see it with Wireshark. My problem is the client size.
The issue: I instantiate a client non-blocking UDP socket and send "Hello" to the server that responses with 8000 bytes of data. I'm trying to read data in a loop in chunks of 1024 bytes. But the problem that only one chunk of data has read. the next loop returns -1 infinitely. If I try to read 8000 bytes in recv I read it successfully, If I try to read 8100 bytes in recv I read 8000 bytes that sent. I mean that only one call to recv succeed. All subsequent calls return an error although not all data has read yet.
Here is a simplified code:
class ClienSocket
{
public:
void Init()
{
pollfd m_poll = {};
m_poll.fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
if(m_poll.fd == -1)
{
throw std::runtime_error(GetLastError());
}
int optval = 1;
setsockopt(m_poll.fd, SOL_SOCKET, SO_REUSEADDR, static_cast<const void *>(&optval), sizeof(int));
int on = 1;
if(ioctl(m_poll.fd, FIONBIO, &on) < 0)
{
throw std::runtime_error(std::string("failed to set the client socket non-blocking: ") + strerror(errno));
}
}
void Run()
{
struct sockaddr_in serv_addr;
m_servaddr.sin_family = AF_INET;
m_servaddr.sin_addr.s_addr = inet_addr(m_address.c_str());
m_servaddr.sin_port = htons(static_cast<uint16_t>(m_port));
m_poll.events = POLLIN;
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(m_port);
m_running = true;
if(pthread_create(&m_readThread, nullptr, &ClienSocket::ReadThreadWrapper, this) != 0)
{
m_running = false;
throw std::runtime_error(std::string("thread creating error");
}
}
void ClienSocket::Write(const char *data, size_t size)
{
sendto(m_poll.fd, data, size, MSG_NOSIGNAL, reinterpret_cast<const struct sockaddr *>(&(m_servaddr)), sizeof(sockaddr_in));
}
static void *ClienSocket::ReadThreadWrapper(void *ptr)
{
ClienSocket *instance = static_cast<ClienSocket *>(ptr);
if(instance != nullptr)
{
return instance->ReadThreadFunc();
}
return nullptr;
}
void *ClienSocket::ReadThreadFunc()
{
while(m_running)
{
retval = poll(&m_poll, 1, 1000);
if(retval > 0)
{
if(m_poll.revents == POLLIN)
{
bool readMore = true;
do
{
ssize_t readBytes = recv(m_poll.fd, m_readBuffer, READ_BUFFER_SIZE, 0);
std::cout << readBytes << ", " << errno << std::endl;
if (readBytes < 0)
{
if (errno != EWOULDBLOCK)
{
throw std::runtime_error(std::string("socket error");
}
}
else if(readBytes == 0)
{
readMore = false;
}
else
{
ProcessData(m_readBuffer, readBytes);
}
}
while(readMore == true);
}
}
}
return nullptr;
}
void ClienSocket::Wait()
{
if(m_running)
{
pthread_join(m_readThread, nullptr);
}
}
void ProcessData(const char *data, size_t length)
{
std::cout << length << std::endl;
}
private:
bool m_running = false;
int m_port = 3335;
std::string m_address = "192.168.5.1";
struct sockaddr_in m_servaddr;
pollfd m_poll = {};
pthread_t m_readThread;
static constexpr size_t READ_BUFFER_SIZE = 1024;
char m_readBuffer[READ_BUFFER_SIZE];
}
The testcase:
ClienSocket client;
client.Init();
client.Run();
client.Write("hello", 5);
clientWait();
According to Wireshard 8000 bytes has sent:
Target system: Ubuntu 22.04
The output:
1024, 0
-1, 11
-1, 11
-1, 11
-1, 11
-1, 11
...
I'm trying to read data in a loop in chunks of 1024 bytes.
That will not work with UDP, as it is message-oriented rather than stream-oriented, like TCP is.
In UDP, there is a 1:1 relationship between sends and reads. If the UDP server sends a single message of 8000 bytes, the client must receive the entire message in a single read, it cannot receive it across multiple reads, like you are attempting to do.
If the buffer you are reading into is too small to receive the entire message, the read will fail with an EMSGSIZE error code and the unread bytes will be discarded, you can't recover them.
That is why your subsequent reads are failing (withan EWOULDBLOCK/EAGAIN error code), as there is no data available to read until the server sends a new message.
WinSock 2.2 send() function always returns me all the bytes I want it to send! I am connecting to google.com on port 80 and sending random t letters for data. I even tried to send as many as 1GB of t's. It still returns with all bytes sent. I was expecting it to return back with me with how many bytes it could fit in a packet and the rest would be handled by the while loop in the implementation, but it never enters inside the while loop as a result!
I am using TCP/IPv4 sockets. I know my internet is not lighting fast to send 1GB faster than I can blink.
This code is shown for most important chunks. For example a wrapper for the C-string send call is omitted that calls the send I shown.
Calling test code
//prepare the long data string
int buffSize = 1 * 1000 * 1000 * 1000;
char *buff = new char[buffSize];
memset(buff, 't', buffSize);
buff[buffSize - 3] = '\n';
buff[buffSize - 2] = '\n';
buff[buffSize - 1] = '\0';
//send thee data
int bytesSent = socket->send(buff);
//verify all thee data is sent
CHECK(bytesSent == strlen(buff));
Send implementatian
int mySocket::send(const void *data, int length)
{
int bytesSent = ::send(socketDescriptor, (char*)data, length, 0);
if (bytesSent == SOCKET_ERROR) return -1;
while(bytesSent < length)
{
int sent = ::send(socketDescriptor, (char*)data + bytesSent, length - bytesSent, 0);
if (sent == SOCKET_ERROR) return bytesSent;
bytesSent += sent;
}
return bytesSent;
}
EDIT 1
Since it started to confuse people here is the wrapper
Send wrapper
int mySocket::send(const char * data_string)
{
return send(dataString, strlen(data_string));
}
EDIT 2
here is a full working example you can debug. it produces the same result, it just instantly reports all bytes as sent.
#include <WinSock2.h>
#include <WS2tcpip.h>
#include <stdio.h>
#pragma comment(lib, "ws2_32.lib")
class mySocket
{
public:
mySocket(SOCKET sockeDesc)
{
socketDescriptor = sockeDesc;
}
int mySocket::send(const void *data, int length)
{
int bytesSent = ::send(socketDescriptor, (char*)data, length, 0);
if (bytesSent == SOCKET_ERROR) return -1;
while (bytesSent < length)
{
int sent = ::send(socketDescriptor, (char*)data + bytesSent, length - bytesSent, 0);
if (sent == SOCKET_ERROR) return bytesSent;
bytesSent += sent;
}
return bytesSent;
}
int mySocket::send(const char * data_string)
{
return send(data_string, strlen(data_string));
}
private:
SOCKET socketDescriptor;
};
int main()
{
WSAData wsd;
if (WSAStartup(MAKEWORD(2, 2), &wsd) || wsd.wVersion != MAKEWORD(2, 2))
{
WSACleanup();
return 1;
}
//create ze socket
SOCKET socketDescriptor = socket(AF_INET, SOCK_STREAM, 0);
//look up socket info
addrinfo hints;
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
addrinfo *res;
int errCode = getaddrinfo("google.com", "80", &hints, &res);
if (errCode) return 1;
//connect ze socket
int connectStatusCode = ::connect(socketDescriptor, res->ai_addr, res->ai_addrlen);
if (connectStatusCode == SOCKET_ERROR) return 1;
//create zeeeee socket!
mySocket *socket = new mySocket(socketDescriptor);
//prepare ze long data string
int buffSize = 1 * 1000 * 1000 * 1000;
char *buff = new char[buffSize];
memset(buff, 't', buffSize);
buff[buffSize - 3] = '\n';
buff[buffSize - 2] = '\n';
buff[buffSize - 1] = '\0';
//send ze data
int bytesSent = socket->send(buff);
//verify all ze data is sent
bool success = (bytesSent == strlen(buff));
printf("requested bytes = %i\nsent bytes = \t %i", strlen(buff), bytesSent);
printf("\n\nsuccess = %s", success ? "true" : "false");
closesocket(socketDescriptor);
delete socket;
freeaddrinfo(res);
WSACleanup();
getchar();
return 0;
}
EDIT 3
https://msdn.microsoft.com/en-us/library/windows/desktop/ms740506(v=vs.85).aspx
Remarks
...
The successful completion of a send function does not
indicate that the data was successfully delivered and received to the
recipient. This function only indicates the data was successfully
sent.
but #Karsten Koop, pointed to similar question on related behaviour:
What is the size of a socket send buffer in Windows?
i badly understand what's writtent there. but what i got is it says the function simply writes to a buffer and returns the bytes "sent". but this not only means it doesn't guarantee that the recepient received the data(which microsoft states) BUT it means it isn't actually sent at all... just in a buffer. am i missing something here or is microsoft misleading about its behaviour?
There is more than one type of socket: Winsock supports multiple protocols.
https://msdn.microsoft.com/en-us/library/windows/desktop/ms740506(v=vs.85).aspx
Just because the current implementation of socket(AF_INET, SOCK_STREAM, IPPROTO_TCP) generally seems to block until all data is sent, doesn't mean that other socket types will also block.
In addition, send may send less than the full buffer if the process is interrupted e.g. by a network outage, or if the socket is closed either from the other end or by another thread.
Also, the Winsock API is designed to be compatible with the BSD and Posix sockets APIs, and on those platforms send will generally return with error EINTR if a signal is received during the call. (In Unixland most blocking calls can return with errno==EINTR, this is how the problem of handling external events on a single-threaded system was solved).
I made a client server program by using C++.
I have a problem if I try to send large files. For example, a 50 byte file works fine while a 200 byte file fails.
Server Code:
// server.cpp : Defines the entry point for the console application.
#include "stdafx.h"
#include <WinSock2.h>
#include <Windows.h>
#include <stdio.h>
int _tmain(int argc, _TCHAR* argv[])
{
WORD wVersionRequested;
WSADATA wsaData;
int wsaerr;
wVersionRequested = MAKEWORD(2, 2);
wsaerr = WSAStartup(wVersionRequested, &wsaData);
if (wsaerr != 0) {
printf("The Winsock DLL not found \n ");
} else {
printf("The Winsock DLL found\n ");
}
if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2) {
printf("not support Winsock version 2.2 ");
} else {
printf("support winsock version 2.2 \n ");
}
SOCKET m_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (m_socket == INVALID_SOCKET) {
printf("Error di socket(): %ld\n", WSAGetLastError());
WSACleanup();
} else{
printf("Socket() Berhasil ! \n");
}
sockaddr_in service;
service.sin_family = AF_INET;
service.sin_addr.s_addr = inet_addr("127.0.0.1");
service.sin_port = htons(55555);
int namelen = sizeof(service);
int m_bind = bind(m_socket, (sockaddr*)&service, namelen);
if (m_bind == SOCKET_ERROR){
printf("bind() failed ! %ld\n ", WSAGetLastError());
} else {
printf("bind() ok ! \n");
}
if (listen(m_socket, 1) == SOCKET_ERROR) {
printf("Listen() failed ! %d\n ", WSAGetLastError());
} else {
printf("Listen() ok ! \n");
}
SOCKET AcceptSocket;
printf("waiting for Client...\n \n");
int addresslen = sizeof(service);
while (AcceptSocket = accept(m_socket, (sockaddr*)&service, &addresslen)) {
printf("Server dan Client connected --> ");
char *ClientIP = inet_ntoa(service.sin_addr);
int ClientPort = ntohs(service.sin_port);
printf("IP: %s:%d\n ", ClientIP, ClientPort);
char *Filesize = new char[10];
int Size = 0;
int recv_size, recv_file;
char Buffer[MAXCHAR];
FILE *File;
recv_file = recv(AcceptSocket, Buffer, Size, 0);
recv_size = recv(AcceptSocket, Filesize, 10, 0);
while (Filesize) {
//Menerima File Size
Size = atoi((const char*)Filesize);
File = fopen("D:\\fileReceived.txt", "wb");
fwrite((const char*)Buffer, 1, Size, File);
fclose(File);
printf("File received \n");
ZeroMemory(Buffer, Size);
// printf("File size : %d\n",Size);
recv_file = recv(AcceptSocket, Buffer, Size, 0);
recv_size = recv(AcceptSocket, Filesize, 10, 0);
}
}
}
Client Code
// client.cpp : Defines the entry point for the console application.
#include "stdafx.h"
#include <winsock2.h>
#include <Windows.h>
#include <stdio.h>
using namespace std;
int Size = 0;
char *Buffer;
int _tmain(int argc, _TCHAR* argv[])
{
WORD wVersionRequested;
WSADATA wsaData;
int wsaerr;
wVersionRequested = MAKEWORD(2, 2);
wsaerr = WSAStartup(wVersionRequested, &wsaData);
if (wsaerr != 0) {
printf("The Winsock DLL not found \n ");
} else {
printf("The Winsock DLL found \n ");
}
if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2) {
printf("not support Winsock version 2.2 ");
} else {
printf("support winsock version 2.2 \n ");
}
SOCKET Client_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (Client_socket == INVALID_SOCKET) {
printf("Error di socket(): %ld\n", WSAGetLastError());
WSACleanup();
} else{
printf("Socket() ok ! \n");
}
SOCKADDR_IN clientService;
clientService.sin_family = AF_INET;
clientService.sin_addr.s_addr = inet_addr("127.0.0.1");
clientService.sin_port = htons(55555);
if (connect(Client_socket, (SOCKADDR*)&clientService, sizeof(clientService)) == SOCKET_ERROR) {
printf("connect() fail ! \n");
} else {
printf(" connect() ok .... \n ");
while (1){
FILE *File;
File = fopen("D:\\logging21.txt", "rb");
if (!File){
printf("", WSAGetLastError());
}
printf("File open ok ! \n");
fseek(File, 0, SEEK_END);
Size = ftell(File);
fseek(File, 0, SEEK_SET);
char cisi[10];
sprintf(cisi, "%i", Size);
// fclose(File);
send(Client_socket, cisi, 10, 0); //file size sent
// Sleep(6000);
Buffer = (char*)malloc(Size + 1);
fread(Buffer, Size, 1, File);
fclose(File);
send(Client_socket, Buffer, Size, 0); // File Binary sent
free(Buffer);
printf("sending finished....\n");
Sleep(6000);
}
}
}
The server is riddled with errors, but this is the most relevant to the asked question: Why can I only send a few bytes?
char *Filesize = new char[10];
int Size = 0; // note the size is set to zero
int recv_size, recv_file;
char Buffer[MAXCHAR];
// no idea how big MAXCHAR is, but it turns out to be irrelevant
FILE *File;
recv_file = recv(AcceptSocket, Buffer, Size, 0);
// Above we use that size of zero to read zero bytes from the socket
recv_size = recv(AcceptSocket, Filesize, 10, 0);
// get the size of the file. This doesn't seem too bad
while (Filesize) { // but we just used a 10 byte blob of data containing who knows what
// as the exit condition from a while loop.
// never use anything from an external source, especially the internet
// without validating and verifying first.
//Menerima File Size
Size = atoi((const char*)Filesize); // atoi fails to convert silently. Use strtol instead.
File = fopen("D:\\fileReceived.txt", "wb"); // open file
fwrite((const char*)Buffer, 1, Size, File);
// write over file contents with what we hope is filesize from a buffer into
// which we read zero bytes. File now full of random crap.
fclose(File);
printf("File received \n");
ZeroMemory(Buffer, Size);
// printf("File size : %d\n",Size);
recv_file = recv(AcceptSocket, Buffer, Size, 0);
// read size of the **last file** (we hope) into buffer
recv_size = recv(AcceptSocket, Filesize, 10, 0);
}
At the very least, filesize must be read before trying to read the file.
Important fun fact about TCP: TCP is a stream, not a packet. Do not assume that because you wrote a number with send that the number is the only thing waiting to be read. For efficiency, TCP packs data together, so if you send "1234" and then a file 1234 bytes long, odds are pretty good both the file size and the file will arrive at the same time. So recv of 10 bytes will very likely read 1234, "1234"'s terminating null, and the first five bytes of the file. It's now up to you to separate the file length from the file data.
But if you send the length as a 32 bit integer, it will always be 4 bytes. Easy, yes? No. Because some computers and network protocols represent numbers backwards. I'm serious here. Google up endian.
Next: recv returns the number of bytes read. You may not get the number of bytes you asked for and have to keep asking until you get the while thing. recv also returns -1 if something goes wrong, so every time you recv, check that the return code is positive and that it's the number of bytes you need before doing anything with the data. Reading a 32 bit filesize, getting only 24 bits, and then trying to use those 24 bits to do meaningful work will really ruin your day.
And there's more! What if MAXCHARS is smaller than the size of the file? Well, that one is easy. You recv MAXCHARS or the number of bytes left in the file and write it out until the file is done.
So:
recv file size
Make sure it's really the filesize and nothing else.
open the output file
while file size is greater than zero
recv up to MAXCHARS or file size, whichever is lower, into buffer
if the number of bytes read is greater than zero
write the number of bytes read from buffer into output file
subtract the number of bytes read from file size
else
something bad happened to the connection. Give up.
close file
You tagged your question as C++ but the code is pretty much entirely C.
Here is a somewhat more C++ version of the server code. To finish the project, your client will need to start by sending a populated "FileTransfer" object, e.g.
FileTransfer xfer(file.size);
auto result = send(send_socket, &xfer, sizeof(xfer), 0);
then send the data from the file, ideally read <= FileTransfer::BufferSize bytes and then push them onto the socket until you have reach all the bytes you promised to send.
// move the code between 8x----x8x into protocol.h
// 8x---- snip ----x8
#pragma once
// protocol.h
#ifndef PROTOCOL_H
#define PROTOCOL_H 1
#include <cstdint>
struct FileTransfer
{
enum { ProtoVersion = 1 };
static const size_t BufferSize = 4 * 4096;
uint32_t m_proto;
size_t m_size;
FileTransfer(size_t size_) : m_proto(ProtoVersion), m_size(size_) {}
FileTransfer() : m_proto(0), m_size(0) {}
};
#endif // PROTOCOL_H
// 8x---- snip ----x8
// server.cpp : Defines the entry point for the console application.
#include "stdafx.h"
#define NOMINMAX
#include <WinSock2.h>
#include <Windows.h>
#include <Ws2tcpip.h>
#include <algorithm>
#include <fstream>
#include <iostream>
//#include "protocol.h"
#pragma comment(lib, "Ws2_32.lib")
void _describeConnection(sockaddr_in& service)
{
char clientIP[128];
inet_ntop(AF_INET, &(service.sin_addr), clientIP, sizeof(clientIP));
auto clientPort = ntohs(service.sin_port);
std::cout << "new connection from " << clientIP << ':' << clientPort << "\n";
}
bool _errorIndicatesInterrupted()
{
auto err = WSAGetLastError();
return (err == WSAEINTR || err == WSAEINPROGRESS);
}
void _receiveFile(SOCKET socket)
{
FileTransfer xfer;
auto recv_size = recv(socket, reinterpret_cast<char*>(&xfer), sizeof(xfer), 0);
if (recv_size < sizeof(xfer)) {
std::cout << "error: only " << recv_size
<< " bytes while recv()ing FileTransfer\n";
return;
}
if (xfer.m_proto != FileTransfer::ProtoVersion) {
std::cout << "error: connection protocol " << xfer.m_proto
<< " not supported\n";
return;
}
if (xfer.m_size <= 0) {
std::cout << "error: zero length transfer\n";
return;
}
std::ofstream out("D:\\fileReceived.txt", std::ios::binary | std::ios::trunc);
char recvBuffer[FileTransfer::BufferSize];
size_t bytesLeft = xfer.m_size;
while (bytesLeft) {
do {
recv_size = recv(socket, recvBuffer, std::min(bytesLeft, FileTransfer::BufferSize), 0);
} while (recv_size < 0 && _errorIndicatesInterrupted());
if (recv_size < 0) {
std::cout << "error: transfer aborted\n";
return;
}
out.write(recvBuffer, recv_size);
bytesLeft -= recv_size;
}
std::cout << "transfered " << xfer.m_size << " bytes\n";
}
bool _server()
{
SOCKET m_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (m_socket == INVALID_SOCKET) {
std::cout << "socket() failed! " << WSAGetLastError() << "\n";
return false;
}
sockaddr_in service;
service.sin_family = AF_INET;
inet_pton(service.sin_family, "127.0.0.1", &service.sin_addr.s_addr);
service.sin_port = htons(55555);
int m_bind = bind(m_socket, (sockaddr*)&service, sizeof(service));
if (m_bind == SOCKET_ERROR) {
std::cout << "bind() failed! " << WSAGetLastError() << "\n";
return false;
}
if (listen(m_socket, 1) == SOCKET_ERROR) {
std::cout << "listen() failed! " << WSAGetLastError() << "\n";
return false;
}
// This code can only accept one connection at a time.
int addresslen = sizeof(service);
for (;;) {
std::cout << "waiting for client...\n";
SOCKET acceptSocket = accept(m_socket, (sockaddr*)&service, &addresslen);
if (acceptSocket < 0) {
std::cout << "accept() failed: " << WSAGetLastError() << "\n";
return false;
}
_describeConnection(service);
_receiveFile(acceptSocket);
closesocket(acceptSocket);
}
}
int _tmain()
{
WSADATA wsaData;
WORD wVersionRequested = MAKEWORD(2, 2);
int wsaerr = WSAStartup(wVersionRequested, &wsaData);
if (wsaerr != 0) {
std::cout << "WinSock DLL not found\n";
return 1;
}
if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2) {
std::cout << "WinSock 2.2 required\n";
return 1;
}
_server();
// system("PAUSE"); Just use CTRL+F5.
return 0;
}
If this is not a homework project and you are earnestly trying to get a file transfer project set up, consider using one of the libraries mentioned here: Best C/C++ Network Library.
I made a server and client that should transfer files.
I tried to make it read the whole file and send it.
But now as I see it, I am having a problem.
Server should automatically send the file when the client is connected.
But the file is empty, and I don't know where the problem is
You can see that I'm trying to send .txt file. But I would like in the future send a big file, but not bigger than 1MB.)
Edit:
Picture here: http://img819.imageshack.us/img819/8259/aadi.jpg
Left side: The file that I tried to send.
Right side: The file I received
The Problem: The file that I received has been damaged, and I can't use it.
Server:
#include <WinSock2.h>
#include <Windows.h>
#include <stdio.h>
#include <iostream>
using namespace std;
#pragma comment(lib, "Ws2_32.lib")
#define Port 6000
SOCKET Socket, Sub;
WSADATA Winsock;
sockaddr_in Addr;
sockaddr_in IncomingAddress;
int AddressLen = sizeof(IncomingAddress);
int main()
{
WSAStartup(MAKEWORD(2, 2), &Winsock); // Start Winsock
if(LOBYTE(Winsock.wVersion) != 2 || HIBYTE(Winsock.wVersion) != 2) // Check version
{
WSACleanup();
return 0;
}
Socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
ZeroMemory(&Addr, sizeof(Addr));
Addr.sin_family = AF_INET;
Addr.sin_port = htons(Port);
bind(Socket, (sockaddr*)&Addr, sizeof(Addr));
if(listen(Socket, 1) == SOCKET_ERROR)
{
printf("listening error\n");
}
else
{
printf("listening ok\n");
}
if(Sub = accept(Socket, (sockaddr*)&IncomingAddress, &AddressLen))
{
char *ClientIP = inet_ntoa(IncomingAddress.sin_addr);
int ClientPort = ntohs(IncomingAddress.sin_port);
printf("Client conncted!\n");
printf("IP: %s:%d\n", ClientIP, ClientPort);
printf("Sending file .. \n");
FILE *File;
char *Buffer;
unsigned long Size;
File = fopen("C:\\Prog.rar", "rb");
if(!File)
{
printf("Error while readaing the file\n");
getchar();
return 0;
}
fseek(File, 0, SEEK_END);
Size = ftell(File);
fseek(File, 0, SEEK_SET);
Buffer = new char[Size];
fread(Buffer, Size, 1, File);
char cSize[MAX_PATH];
sprintf(cSize, "%i", Size);
fclose(File);
send(Sub, cSize, MAX_PATH, 0); // File size
//int len = Size;
//char *data = Buffer;
int Offset = 0;
while(Size > Offset)
{
int Amount = send(Sub, Buffer + Offset, Size - Offset, 0);
if(Amount <= 0)
{
cout << "Error: " << WSAGetLastError() << endl;
break;
}
else
{
Offset += Amount;
printf("2\n");
}
}
free(Buffer);
closesocket(Sub);
closesocket(Socket);
WSACleanup();
}
getchar();
return 0;
}
Client:
#include <WinSock2.h>
#include <Windows.h>
#include <stdio.h>
#include <iostream>
using namespace std;
#pragma comment(lib, "Ws2_32.lib")
SOCKET Socket;
WSADATA Winsock;
sockaddr_in Addr;
int Addrlen = sizeof(Addr);
int main()
{
WSAStartup(MAKEWORD(2, 2), &Winsock); // Start Winsock
if(LOBYTE(Winsock.wVersion) != 2 || HIBYTE(Winsock.wVersion) != 2) // Check version
{
WSACleanup();
return 0;
}
Socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
ZeroMemory(&Addr, sizeof(Addr)); // clear the struct
Addr.sin_family = AF_INET; // set the address family
Addr.sin_addr.s_addr = inet_addr("127.0.0.1");
Addr.sin_port = htons(6000); // set the port
if(connect(Socket, (sockaddr*)&Addr, sizeof(Addr)) < 0)
{
printf("Connection failed !\n");
getchar();
return 0;
}
printf("Connection successful !\n");
printf("Receiving file .. \n");
int Size;
char *Filesize = new char[1024];
if(recv(Socket, Filesize, 1024, 0)) // File size
{
Size = atoi((const char*)Filesize);
printf("File size: %d\n", Size);
}
char *Buffer = new char[Size];
//int len = Size;
//char *data = Buffer;
int Offset = 0;
while(Size > Offset)
{
int Amount = recv(Socket, Buffer + Offset, Size - Offset, 0);
if(Amount <= 0)
{
cout << "Error: " << WSAGetLastError() << endl;
break;
}
else
{
Offset += Amount;
printf("2\n");
}
}
FILE *File;
File = fopen("Prog.rar", "wb");
fwrite(Buffer, 1, Size, File);
fclose(File);
getchar();
closesocket(Socket);
WSACleanup();
return 0;
}
The send API may not send all the data you requested to send. So, you have to pay attention to the return value, and retry the send from where the last send ended. As an example:
offset = 0;
while (offset < bufsize) {
r = send(socket, buf+offset, bufsize-offset);
if (r <= 0) break;
offset += r;
}
While you are doing something similar for your file transfer, you do not make sure this is the case for your file size.
When you send the file size, you only need to send the string that represents the size, not the entire MAX_PATH. The receiver then needs to parse the first string to determine the size, but any data read in after the end of the first string needs to be considered part of the file. However, since you are trying the send MAX_PATH, the receiver should receive the same amount. Your client code receives 1024 bytes, but there is no indication this is the same size as MAX_PATH.
The recv API may also return fewer bytes than requested. You use a loop to handle reading the file, but you may need a loop to read the entire message that contains the file size.
In your client receive loop, you are incrementing the data pointer. This makes it unusable to write out the file later. You already have Buffer though, so use that to write out your file.
fwrite(Buffer, 1, len, File);
If you encounter an error doing socket I/O, you can retrieve the error with WSAGetLastError(), or you can issue getsockopt() on the socket with the SO_ERROR option. These may return different values, but the error reason should be correlated.
Myself faced the same problem and after googling found that send() api can send a maximum data based on low level TCP buffers which are os dependent.So inorder to send a huge file we need to perform file chunking , ie send the file in the form of chunks.
`const int FILE_CHUNK_SIZE = 2000;
//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;`
At the receiving end we need to have a recv() api called till the whole file content is read.
credits to:shacktar cplusplus.com
I'm having trouble with a socket application I'm programming in C++. I'm doing my programming with Bloodshed Dev-Cpp on Windows XP. I made a class for handling all the message transfers and have a client and server program that both use that class for handling their services. The application itself is very basic, the only intent I have for it is to get all this to work.
The client, which is for sending messages, works like I expect it to. If my server is running, it doesn't have any errors when sending a message. If it's not running it'll pass an error. But my server continuously accepts weird gibberish. It's always the same data. When it receives the message there is no effect. If I have my client try to identify the server, it gets back gibberish.
I have included my source code here. The linker also brings in two extra parameters: -lwsock32 and an inclusion of the library libws2_32.a, which came with Dev-Cpp.
Here's the header for my Messager class:
#ifndef MESSAGER
#define MESSAGER
#include <string>
class Messager{
private:
int sendSocket;
int listenSocket;
public:
void init(void);
bool connect(std::string ip, std::string port);
bool bind(std::string port);
void listen(void);
void send(std::string message);
std::string receive(void);
};
#endif
These are my definitions for the Messager class:
#include "Messager.h"
#include <winsock2.h>
#include <sys/types.h>
#include <ws2tcpip.h>
#include <windows.h>
void Messager::init(void){
WSADATA wsaData;
WSAStartup(MAKEWORD(1,1), &wsaData);
}
bool Messager::connect(std::string ip, std::string port){
struct addrinfo hints;
struct addrinfo *res;
bool success = false;
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
getaddrinfo(ip.c_str(), port.c_str(), &hints, &res);
sendSocket = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
success = ::connect(sendSocket, res->ai_addr, res->ai_addrlen) != -1;
freeaddrinfo(res);
return success;
}
bool Messager::bind(std::string port){
struct addrinfo hints, *res;
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE;
getaddrinfo(NULL, port.c_str(), &hints, &res);
listenSocket = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
if(listenSocket == INVALID_SOCKET){
return false;
}
if(::bind(listenSocket, res->ai_addr, res->ai_addrlen) == -1){
return false;
}
return true;
}
void Messager::listen(void){
::listen(listenSocket, 10);
}
int Messager::send(std::string message){
const std::string terminator = "\r\n";
std::string realMessage;
int size = 0;
int totalSent = 0;
realMessage = message;
realMessage += terminator;
size = realMessage.size();
totalSent = ::send(sendSocket, realMessage.c_str(), size, 0);
if(totalSent == 0 || totalSent == -1){
return 0; // There must be an error, 0 means it is an error
}
// This statement keeps adding the results of ::send to totalSent until it's the size of the full message
for(totalSent = 0; totalSent < size; totalSent += ::send(sendSocket, realMessage.c_str(), size, 0));
return totalSent;
}
// This function has been updated a lot thanks to #Luke
std::string Messager::receive(void){
const int bufferSize = 256;
const std::string terminator = "\r\n";
char buffer[bufferSize];
int i = 0;
int received = 0;
std::string tempString;
size_t term = 0;
for(i = 0; i < bufferSize; i++){
buffer[i] = 0;
}
received = ::recv(listenSocket, buffer, bufferSize, 0);
tempString = buffer;
term = tempString.find(terminator);
if(term != -1){ // Already have line
line = tempString;
}
while(received != -1 && received != 0){ // While it is receiving information...
// Flush the buffer
for(i = 0; i < bufferSize; i++){
buffer[i] = 0;
}
::recv(listenSocket, buffer, bufferSize, 0);
tempString += buffer;
term = tempString.find(terminator);
if(term != -1){ // Found terminator!
return tempString;
}
}
throw 0; // Didn't receive any information. Throw an error
}
Any ideas about what might be going on would be really appreciated. If necessary I can post the code the server and client use, but I can give a general outline:
Server:
messager.init()
messager.bind()
messager.listen()
messager.receive() <-- includes accept()
Client:
messager.init()
messager.connect()
messager.send()
Thanks in advance.
I see two concerns.
You can't safely use the string assignment operator in Message::receive(). The assignment operator relies on the character array being NULL-terminated, and in this case it is not. It's probably filling it up with a bunch of garbage data. You should get the number of characters actually received (i.e. the return value of recv()) and use the string::assign() method to fill the string object.
There is no code to ensure all the data has been sent or received. recv() is going to return as soon as any data is available; you really need to loop until you have received the entire message. For plain-text data, typically people use a CR-LF pair to indicate the end of a line. You keep calling recv() and buffering the results until you see that CR-LF pair and then return that line to the caller. You should also loop on send() until your entire buffer has been sent.
Typically this looks something like the following (this is all from memory so there are probably a few minor errors, but this is the gist of it):
bool Message::Receive(std::string& line)
{
// look for the terminating pair in the buffer
size_t term = m_buffer.find("\r\n");
if(term != -1)
{
// already have a line in the buffer
line.assign(m_buffer, 0, term); // copy the line from the buffer
m_buffer.erase(0, term + 2); // remove the line from the buffer
return true;
}
// no terminating pair in the buffer; receive some data over the wire
char tmp[256];
int count = recv(m_socket, tmp, 256);
while(count != -1 && count != 0)
{
// successfully received some data; buffer it
m_buffer.append(tmp, count);
// see if there is now a terminating pair in the buffer
term = m_buffer.find("\r\n");
if(term != -1)
{
// we now have a line in the buffer
line.assign(m_buffer, 0, term); // copy the line from the buffer
m_buffer.erase(0, term + 2); // remove the line from the buffer
return true;
}
// we still don't have a line in the buffer; receive some more data
count = recv(m_socket, tmp, 256);
}
// failed to receive data; return failure
return false;
}
Two suggestions:
check the return values of all the socket functions you call
ditch DevC++ - it is buggy as hell & is no longer being developed - use http://www.codeblocks.org/ instead.
I'd be a bit concerned about your receive code. It creates a char* buffer to receive the data but doesn't actually allocate any memory for it.
Now I can't tell whether you're calling the WinSock recv there since you don't explicitly say ::recv but I think you would need to either:
allocate some space with malloc first (id recv wants a buffer); or
pass the address of the buffer pointer (if recv allocates its own buffer).
I'm actually surprised that the doesn't cause a core dump since the value of buffer could be set to anything when you call recv.
Something like this may be better:
char *Messager::receive(void){
int newSocket = 0;
struct sockaddr_storage *senderAddress;
socklen_t addressSize;
char *buffer;
addressSize = sizeof senderAddress;
newSocket = accept(listenSocket, (struct sockaddr *)&senderAddress,
&addressSize);
buffer = new char[20];
recv(newSocket, buffer, 20, 0);
return buffer;
}
But you need to remember that the client of this function is responsible for freeing the buffer when it's finished with it.
In your receive function, the buffer local is never initialized to anything, so you end up reading your message into some random memory and probably causing corruption or crashing. You probably want char buffer[MAX_MSG_LENGTH]; instead of char *buffer