I'm learning some examples about C++ socket. One of the code here has an error : "expect token while got fclose" at the line above the last line
The code seems fine with me, so I can't figure out what is wrong here.
Any ideas are appreciated.
void RecvFile(int sock, const char* filename)
{
int rval;
char buf[0x1000];
FILE *file = fopen(filename, "wb");
if (!file)
{
printf("Can't open file for writing");
return;
}
do
{
rval = recv(sock, buf, sizeof(buf), 0);
if (rval < 0)
{
// if the socket is non-blocking, then check
// the socket error for WSAEWOULDBLOCK/EAGAIN
// (depending on platform) and if true then
// use select() to wait for a small period of
// time to see if the socket becomes readable
// again before failing the transfer...
printf("Can't read from socket");
fclose(file);
return;
}
if (rval == 0)
break;
int off = 0;
do
{
int written = fwrite(&buf[off], 1, rval - off, file);
if (written < 1)
{
printf("Can't write to file");
fclose(file);
return;
}
off += written;
}
while (off < rval);
}
fclose(file);
}
You have a do with no corresponding while:
do
{
// ...
do
{
// ...
}
while (off < rval);
}
// No while here
fclose(file);
It appears that it should just be while (true), which you might as well just stick at the top, instead of doing a do while. Execution will break from the loop if recv returns 0 or less, which indicate an orderly shutdown and an error respectively. So change it to:
while (true)
{
// ...
do
{
// ...
}
while (off < rval);
}
fclose(file);
You have a do statement without a corresponding while:
do // <== THERE IS NO CORRESPONDING while FOR THIS do
{
rval = recv(sock, buf, sizeof(buf), 0);
if (rval < 0)
{
// ...
}
// ...
do
{
// ...
}
while (off < rval); // <== This is OK: the "do" has a matching "while"
}
// Nothing here! Should have a "while (condition)"
If you just want to repeat your loop indefinitely, then you should use while (true) - either replacing the do keyword (preferably), or adding it where the missing while should go (as indicated by the above comments).
You started a do without actually supplying a while();
do
{
rval = recv(sock, buf, sizeof(buf), 0);
if (rval < 0)
{
// if the socket is non-blocking, then check
// the socket error for WSAEWOULDBLOCK/EAGAIN
// (depending on platform) and if true then
// use select() to wait for a small period of
// time to see if the socket becomes readable
// again before failing the transfer...
printf("Can't read from socket");
fclose(file);
return;
}
if (rval == 0)
break;
int off = 0;
do
{
int written = fwrite(&buf[off], 1, rval - off, file);
if (written < 1)
{
printf("Can't write to file");
fclose(file);
return;
}
off += written;
}
while (off < rval);
} //while() Needs to go here
fclose(file);
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 have established a connection between a client and a server in c using poll() on a single fd. I want for the client to receive a message when the server has something to send and vice versa.
As far as I know, poll() listens for events on file desctriptors. I am not clear though as to how these events (or revents) are triggered, to determine when it is time to send or receive on the fd.
I have tried using read and write (or send and recv for that matter) in a loop for the client side but they block, so I switched to poll() for the client side too.
On the client side, I am never getting to the }else if (fds[0].revents & POLLOUT) part, meaning that the socket is never available to write to.
//Create - bind - listen to a socket named listeningSocket
struct pollfd fds[1];
fds[0].fd = listeningSocket;
fds[0].events = POLLIN | POLLPRI;
if (poll(fds, 1, 3000)) {
(client_sock = accept(listeningSocket, &client, (socklen_t *) &c));
spdlog::info("Connection accepted");
std::thread thread(&ConnectionHandler::Handle, std::ref(requestHandler), client_sock);
thread.detach();
}
The client block
while (true) {
if ((rv = poll(fds, 1, 100) > 0)) {
if (fds[0].revents & POLLIN ){
recv(sockfd, buff, 200, 0);
printf("From Server : %s", buff);
bzero(buff, sizeof(buff));
}
}else if (fds[0].revents & POLLOUT){
puts(buff);
strcpy(buff, "HELLO WORLD");
write(sockfd, buff, sizeof(buff));
break;
}
}
On connect, the server sends a welcoming message with write() to the client. The client recv()s this but then never gets its turn to right back to the server.
Am I missing something? Shouldn't the socket be ready to write to when there are no lingering data to be received on it?
Lets reformat your while loop to make it easier to read:
while (true)
{
if ((rv = poll(fds, 1, 100) > 0))
{
if (fds[0].revents & POLLIN)
{
recv(sockfd, buff, 200, 0);
printf("From Server : %s", buff);
bzero(buff, sizeof(buff));
}
}
else if (fds[0].revents & POLLOUT)
{
puts(buff);
strcpy(buff, "HELLO WORLD");
write(sockfd, buff, sizeof(buff));
break;
}
}
It should now be apparent why your POLLOUT block is never executed - that if statement is only reached when poll() returns <= 0, which is not what you want.
You need this kind of logic instead:
while (true)
{
if ((rv = poll(fds, 1, 100) > 0))
{
if (fds[0].revents & POLLIN )
{
recv(sockfd, buff, 200, 0);
printf("From Server : %s", buff);
bzero(buff, sizeof(buff));
}
else if (fds[0].revents & POLLOUT)
{
puts(buff);
strcpy(buff, "HELLO WORLD");
write(sockfd, buff, sizeof(buff));
break;
}
}
}
Which, when reformatted back to your original coding style, would look like this:
while (true) {
if ((rv = poll(fds, 1, 100) > 0)) {
if (fds[0].revents & POLLIN ){
recv(sockfd, buff, 200, 0);
printf("From Server : %s", buff);
bzero(buff, sizeof(buff));
} else if (fds[0].revents & POLLOUT){
puts(buff);
strcpy(buff, "HELLO WORLD");
write(sockfd, buff, sizeof(buff));
break;
}
}
}
See the difference good formatting makes?
That being said, know that a socket enters a writable state as soon as it connects. So your code is likely to send the client's greeting before waiting for the server's greeting. If you need to wait for the server's greeting before replying, you have to actually read the greeting first, eg:
while (true)
{
if ((rv = poll(fds, 1, 100) > 0))
{
if (fds[0].revents & POLLIN)
{
rv = recv(sockfd, buff, 200, 0);
if (rv <= 0) break;
printf("From Server : %.*s", rv, buff);
if (/* the complete greeting has been read */) // MAY take multiple reads!
{
strcpy(buff, "HELLO WORLD");
write(sockfd, buff, strlen(buff));
break;
}
}
}
}
Note that it is not necessary to handle POLLOUT when using blocking sockets. But if you are using non-blocking sockets instead, you are responsible for checking if write() fails with an EWOULDBLOCK error, and if so then buffer the data you tried to send that failed, as well as any subsequent data, until POLLOUT is reported, then you can send the buffered data (continuing to handle EWOULDBLOCK and POLLOUT) until the buffer is empty. Only then can you send new data over the socket without handling POLLOUT again until a new EWOULDBLOCK error is reported. For example:
int sendData(int fd, void *data, int len)
{
char *pdata = (char *) buff;
if (/* fd's buffer is empty */)
{
while (len > 0)
{
int rv = send(fd, pdata, len, 0);
if (rv < 0)
{
if (errno != EWOULDBLOCK)
return rv;
break;
}
pdata += rv;
len -= rv;
}
}
if (len > 0)
{
// add pdata up to len bytes to fd's buffer...
}
return 0;
}
...
while (true)
{
if ((rv = poll(fds, 1, 100) > 0))
{
if (fds[0].revents & POLLIN)
{
rv = recv(sockfd, buff, 200, 0);
if (rv <= 0) break;
printf("From Server : %.*s", rv, buff);
if (/* the complete greeting has been read */) // MAY take multiple reads!
{
strcpy(buff, "HELLO WORLD");
sendData(sockfd, buff, strlen(buff));
}
}
if (fds[0].revents & POLLOUT)
{
char *pdata = ...; // fd's buffer data
int len = ...; // fd's buffer length
int sent = 0;
while (len > 0)
{
rv = send(fd, pdata, len, 0);
if (rv < 0)
break;
pdata += rv;
len -= rv;
sent += rv;
}
if (sent > 0)
{
// remove sent bytes from fd's buffer...
}
}
}
}
Then you can use sendData() any time you need to send any data to fd, instead of calling write()/send() directly.
Remove the } before the else if.
Then put it after the else if-block.
I am using fread function to read file, which I am sending via TCP. I found out, that fread doesn't read the whole file, if the file is binary. I tried everything what i found on the internet, but nothing helped. My code is:
#define BUFSIZE 1024
char buf[BUFSIZE];
FILE *file = fopen(soubor,"rb"); //I do a check which i won't write here
size_t bytes_loaded = 0;
while (!feof(file))
{
bytes_loaded = fread(buf,1,BUFSIZE,file);
if(bytes_loaded != BUFSIZE)
{
if(!feof(file))
{
for(int i = 0; i < 100;i++)
{
fseek(file,-strlen(buf),SEEK_CUR);
bytes_loaded = fread(buf,1,BUFSIZE,file);
if(bytes_loaded == BUFSIZE)
{
break;
}
else if(i == 99)
{
fprintf(stderr,"C could't read the file\n");
fclose(file);
close(client_socket);
return 1;
}
}
}
}
bytestx = send(client_socket, buf, BUFSIZE, 0);
if (bytestx < 0)
perror("ERROR in sendto");
bzero(buf, BUFSIZE);
bytes_loaded = 0;
}
Am I doing something wrong? For example that fread check...
Your whole fread() error handling is wrong, get rid of it (using strlen() on a binary buffer is wrong anyway).
In fact, you shouldn't be using feof() to control your loop. Simply call fread() in a loop until it returns < 1 on EOF or error (use feof() and ferror() to differentiate). And when it returns > 0, you need to pass that value to send instead of passing BUFSIZE.
Try something more like this:
#define BUFSIZE 1024
char buf[BUFSIZE], *pbuf;
FILE *file = fopen(soubor, "rb");
...
size_t bytes_loaded;
do
{
bytes_loaded = fread(buf, 1, BUFSIZE, file);
if (bytes_loaded < 1)
{
if ((!feof(file)) && ferror(file))
fprintf(stderr, "Couldn't read the file\n");
break;
}
pbuf = buf;
do
{
bytestx = send(client_socket, pbuf, bytes_loaded, 0);
if (bytestx < 0)
{
perror("ERROR in send");
break;
}
pbuf += bytestx;
bytes_loaded -= bytestx;
}
while (bytes_loaded > 0);
}
while (bytes_loaded == 0);
fclose(file);
...
If you are just shifting bytes from the file to the socket then you can just keep looping on the return value from std::fread which tells you how many bytes you read and then send exactly that many bytes to your send() command.
Something like this (untested) code:
if(FILE* fp = std::fopen(soubor, "rb"))
{
char buf[1024];
std::size_t bytesrx;
while((bytesrx = std::fread(0, 1, sizeof(buf), fp)) > 0)
{
int bytestx;
if((bytestx = send(client_socket, buf, bytesrx, 0) < 0))
{
// socket error
std::cout << "socket error: " << std::strerror(errno) << '\n';
return EXIT_FAILURE;
}
}
if(bytesrx < 0)
{
// file error
std::cout << "file error: " << std::strerror(errno) << '\n';
return EXIT_FAILURE;
}
}
else
{
// error opening file
}
I'm working with some C++ socket examples. The client run and can connect to the server but can't send the file. I think there's a problem with the send() but I can't fix it.Edit: the error message is "connection reset by peer"
Any ideas are welcomed.
I use OpenSuSE with QT 4.7.4
Here's the send and receive function
void str_server(int sock)
{
char buf[1025];
const char* filename = "//home//romanov//Documents//HelloWorld-build-desktop-Qt_4_7_4_in_PATH__System__Release//ss.png";
FILE *file = fopen(filename, "rb");
if (!file)
{
cerr<<"Can't open file for reading";
return;
}
while (!feof(file))
{
int rval = fread(buf, 1, sizeof(buf), file);//read value
if (rval < 1)
{
cerr<<"Can't read from file";
fclose(file);
return;
}
int off = 0;
do
{
int sent = send(sock, &buf[off], rval - off, 0);
if (sent < 1)
{
// if the socket is non-blocking, then check
// the socket error for WSAEWOULDBLOCK/EAGAIN
// (depending on platform) and if true then
// use select() to wait for a small period of
// time to see if the socket becomes writable
// again before failing the transfer...
cout<<"Can't write to socket";
fclose(file);
return;
}
off += sent;
}
while (off < rval);
}
fclose(file);
}
//===================
void RecvFile(int winsock)
{
int rval;
char buf[1025];
FILE *file = fopen("//home//romanov//Documents//HelloWorld-build-desktop-Qt_4_7_4_in_PATH__System__Release//ss2.png", "wb");
if (!file)
{
printf("Can't open file for writing");
return;
}
do
{
rval = recv(winsock, buf, sizeof(buf), 0);
if (rval < 0)
{
// if the socket is non-blocking, then check
// the socket error for WSAEWOULDBLOCK/EAGAIN
// (depending on platform) and if true then
// use select() to wait for a small period of
// time to see if the socket becomes readable
// again before failing the transfer...
printf("Can't read from socket");
fclose(file);
return;
}
if (rval == 0)
break; //line 159
int off = 0;
do
{
int written = fwrite(&buf[off], 1, rval - off, file);
if (written < 1)
{
printf("Can't write to file");
fclose(file);
return;
}
off += written;
}
while (off < rval);
} //line 175
while (1);
fclose(file);
}
Well , the problem is my bad, I read the wrong socket in the recvFile() , if you guys encounter the same problem, you should check it out.
I have some questions about when it is needed to store the data in the wait buffer (waiting for the FD_WRITE event).
This is my send function (fixed):
bool MyClass::DataSend(char *buf, int len)
{
if (len <=0 || m_Socket == INVALID_SOCKET) return false;
if (m_SendBufferLen > 0)
{
if ((m_SendBufferLen + len) < MAX_BUFF_SIZE)
{
memcpy((m_SendBuffer + m_SendBufferLen), buf, len);
m_SendBufferLen += len;
return true;
}
else
{
// close the connection and log insuficient wait buffer size
return false;
}
}
int iResult;
int nPosition = 0;
int nLeft = len;
while (true)
{
iResult = send(m_Socket, (char*)(buf + nPosition), nLeft, 0);
if (iResult != SOCKET_ERROR)
{
if (iResult > 0)
{
nPosition += iResult;
nLeft -= iResult;
}
else
{
// log 0 bytes sent
break;
}
}
else
{
if (WSAGetLastError() == WSAEWOULDBLOCK)
{
if ((m_SendBufferLen + nLeft) < MAX_BUFF_SIZE)
{
// log data copied to the wait buffer
memcpy((m_SendBuffer + m_SendBufferLen), (buf + nPosition), nLeft);
m_SendBufferLen += nLeft;
return true;
}
else
{
// close the connection and log insuficient wait buffer size
return false;
}
}
else
{
// close the connection and log winsock error
return false;
}
}
if (nLeft <= 0) break;
}
return true;
}
My send (FD_WRITE event) function (fixed):
bool MyClass::DataSendEvent()
{
if (m_SendBufferLen < 1) return true;
int iResult;
int nPosition = 0;
int nLeft = m_SendBufferLen;
while (true)
{
iResult = send(m_Socket, (char*)(m_SendBuffer + nPosition), nLeft, 0);
if (iResult != SOCKET_ERROR)
{
if (iResult > 0)
{
nPosition += iResult;
nLeft -= iResult;
}
else
{
// log 0 bytes sent
break;
}
}
else
{
if (WSAGetLastError() == WSAEWOULDBLOCK)
{
if (nPosition > 0)
{
memmove(m_SendBuffer, (m_SendBuffer + nPosition), (m_SendBufferLen - nPosition));
m_SendBufferLen -= nPosition;
}
break;
}
else
{
// close the connection and log winsock error
return false;
}
}
if (nLeft <= 0)
{
if (m_SendBufferLen == nPosition)
{
m_SendBufferLen = 0;
break;
}
else
{
memmove(m_SendBuffer, (m_SendBuffer + nPosition), (m_SendBufferLen - nPosition));
m_SendBufferLen -= nPosition;
nPosition = 0;
nLeft = m_SendBufferLen;
}
}
}
return true;
}
Do I really need the if (nPosition > 0) or not? How do I simulate this scenario? Is there possible send() in non-blocking mode send less bytes than the requested? If not, why using the while() looping?
This is the final code (thanks to #user315052)
At the top of your while loop, you are already decrementing nLeft, so you don't need to subtract nPosition from it.
iResult = send(m_Socket, (char*)(buf + nPosition), nLeft, 0);
In your second function, when you are shifting the unsent bytes to the beginning of the array, you should use memmove, since you have overlapped regions (you are copying a region of m_SendBuffer into itself). The overlap is illustrated below, where some of the A's would get copied onto itself.
m_SendBuffer: [XXXXAAAAAAAAAAAA]
mPosition: 4
nLeft: 12
I am a little confused about why DataSend() is implemented to allow the caller to keep calling it with success even when the WSAEWOULDBLOCK is encountered. I would suggest the interface be modified to return a result that lets the caller know that it should stop sending, and to wait for an indication to resume sending.
You don't need the nPosition > 0 check.
You can force the case to occur by having the receiver of the data not read anything.
It is definitely possible for send in non-blocking mode to send fewer bytes than requested.