ProtocolBuffer missing required fields but they are filled in - c++

I'm creating a client-server environment and I have just implemented UDP. I was only working and testing with a TCP connection before and then everything worked fine.
Now, if I try to send packets to the server from the client I get an error that some required fields weren't filled in which they cleary are (see code later on). I don't really have an idea why this is happening since it worked before.
One thing I guess that can be important, the receive method of the TCP socket and the UDP socket are on a different thread. (the server uses multithreading)
So, Here's some code:
The TCP Receive (part of it):
iResult = recv(m_Connections[t].socket, m_TCPRecvbuf, m_TCPRecvbuflen, 0);
// Deserialize the data
MessageID* receivedData = new MessageID();
receivedData->ParseFromArray((char*)m_TCPRecvbuf, receivedData->packetsize());
I did the size in ParseFromArray with iResult at first (which seemed to work) but I changed it so I put the size in the data itself. (which has now become receivedData->packetsize())
The UDP Receive (part of it) on a different thread:
iResult = recvfrom(m_UDPListenSocket, m_UDPRecvbuf, m_UDPRecvbuflen, 0, (struct sockaddr *)&address, &addrlen);
// Deserialize the data
MessageID* receivedData = new MessageID();
receivedData->ParseFromArray((char*)m_TCPRecvbuf, receivedData->packetsize());
This is the first creation of a packet that gets sent:
void NetworkInterface::SendLoginData(string username, string password)
{
// Create base message
// -----------------
MessageID message;
message.set_type(MessageID::Type::MessageID_Type_LOGINDATA);
// Create logindata
// ------------------
LoginData data = message.logindata();
data.set_username(username);
data.set_password(password);
// Create packet
// ----------------------
int size = message.ByteSize();
void* buffer = malloc(size);
message.set_packetsize(size);
message.set_clientid(0);
message.SerializeToArray(buffer, size);
if (m_pTCPNetworkingObject != nullptr)
m_pTCPNetworkingObject->SendData(buffer, size);
else
printf("NetworkInterface [ERROR]: No connection data was found. Please use CreateConnectionObject() first!\n");
}
The error that it gives after this is that the Type, ClientID and packetSize fields are missing but you can see those are filled in
Here's the SendData() method:
void SendData(void* data, int size)
{
// SEND DATA
int iResult;
iResult = send(m_TCPConnectSocket, (char*)data, size, 0 );
if (iResult == SOCKET_ERROR)
printf("send failed with error: %d\n", WSAGetLastError());
}
This is about all the information I can give. Any help is appreciated.

There are multiple problems:
1) deserialization:
receivedData->ParseFromArray((char*)m_TCPRecvbuf, receivedData->packetsize());
You call packetSize before it gets filled by parseFromArray, use iResult instead.
2) LoginData:
LoginData data = message.logindata();
This reads a copy of messages empty loginData, you never change message. Use
LoginData* data = message.mutable_logindata();
to get a pointer to messages loginData an modify it within message.
3) serialization:
int size = message.ByteSize();
void* buffer = malloc(size);
this uses the size of an empty message (since loginData is not set). So you never send the full message.
You have to call ByteSize() after all properties are set. This contradicts with the packetSize within your message.
Since you can't use it for deserialization anyway (see 1) you should remove this entry.

Related

Not all data is transfered via Socket

I can't send too large data packets over my setup (currently sending to 127.0.0.1), at about 30kB this functionality starts to fail. For testing I have an application that just starts a Receiver and a Sender, starts two threads, one for the sending, one for receiving, and when both have finished, compares if the sending string is the same as the received string.
void SenderThread(int count)
{
messageOut = "";
messageOut.append(count, 'A');
sender->sendData(messageOut);
}
void ReceivingThread()
{
receiver->ReceiveData(message);
}
main()
{
receiver = new utility::Receiver();
sender = new utility::Sender();
receiver->startSocket(9000);
sender->connectToSocket("127.0.0.1", 9000);
receiver->accept();
for (int count = 100; count < 1024 * 1024; count += 100)
{
std::thread sendThread(SenderThread, count);
std::thread recvThread(ReceivingThread);
sendThread.join();
recvThread.join();
printf("Sent data of length %d ", messageOut.length());
if (message == messageOut)
printf("successfully.\n");
else
{
printf("not successfully.\n");
printf("Length of original message: %d, Length of received message: %d.\n", messageOut.length(), message.length());
break;
}
}
delete receiver;
delete sender;
}
I have following code for my sending socket:
bool utility::Sender::sendData(const std::string & message)
{
int numBytes = 0;
int totalSent = 0;
// Break condition: send() fails, or whole message was transfered
while (totalSent < message.length() && send(message.substr(totalSent).c_str(), message.length() - totalSent, numBytes))
{
totalSent += numBytes;
}
return false;
}
bool utility::Sender::send(const char* pBuffer, int32_t lengthOfBuffer, int32_t &numBytes)
{
numBytes = ::send(connectSocket, pBuffer, lengthOfBuffer, 0);
if (numBytes == SOCKET_ERROR)
return false;
return true;
}
The receiving side:
bool utility::Receiver::ReceiveData(std::string& message)
{
int32_t numBytes = 0;
char data[defaultBufferLength];
// Set to blocking for the first data package
u_long iMode = 0;
ioctlsocket(tcpSocket, FIONBIO, &iMode);
bool success = receive(data, defaultBufferLength, numBytes);
message = std::string(data, numBytes);
// Set to non-blocking for the rest of the journey
iMode = 1;
ioctlsocket(tcpSocket, FIONBIO, &iMode);
while (numBytes == defaultBufferLength && receive(data, defaultBufferLength, numBytes))
{
message.append(data, numBytes);
}
return success;
}
bool utility::Receiver::receive(char* pBuffer, int32_t lengthOfBuffer, int32_t& numBytes)
{
int32_t flags = 0;
numBytes = recv(tcpSocket, pBuffer, lengthOfBuffer, flags);
if (numBytes == -1)
{
numBytes = 0;
if (errno == EAGAIN || errno == EWOULDBLOCK)
return false;
else
close();
}
return true;
}
The output I am getting is
Sent data of length 39200 successfully.
Sent data of length 39300 successfully.
Sent data of length 39400 successfully.
Sent data of length 39500 successfully.
Sent data of length 39600 successfully.
Sent data of length 39700 successfully.
Sent data of length 39800 successfully.
Sent data of length 39900 successfully.
Sent data of length 40000 successfully.
Sent data of length 40100 successfully.
Sent data of length 40200 successfully.
Sent data of length 40300 successfully.
Sent data of length 40400 successfully.
Sent data of length 40500 not successfully.
Length of original message: 40500, Length of received message: 29200.
The thing which is the most irritating, and probably the cause of this, is the ::send(...). I can give it 2 MB of char*, and it will just send it in one swoop (but the receiver fails miserably). What can I do about that?
TCP is a byte-oriented protocol, not message oriented.
send does not create a message. recv does not receive a message. They work on blocks of bytes, and multiple send calls can be combined at the network layer (for efficiency) or broken into multiple TCP packets. In practice, even if you turn off Nagle's algorithm, if a frame is lost at the physical layer and TCP has to retry the transmission, the retransmit will include as much data added to the buffer afterward as it can fit in an outgoing datagram.
So you can't rely on any particular mapping between send calls and recv calls. The only guarantee is that the bytes are delivered to your socket in the same order they were sent. If boundaries are important, you have to create them yourself. Length prefixes are popular in combination with TCP, special framing sequences less so.
You do already have a loop for reassembling messages... but you break out of the loop when you see EAGAIN / EWOULDBLOCK or a partly filled buffer, and continue processing. That's a problem, because you only have a partial message at that point. You need a way to delay processing until you have a complete message.
Adding to ben-voigt answer, you need to create an higher level message system for your socket, so you can send to the server the message size and on your socket receive method create a session or buffer storage that you append the received data till the message size match the total data received, once that requirement is met then you can process the data

c++ Socket receive takes a long time

I am writing the client side of the Socket. When there is something to read my code works fine but when there is nothing to read, the recv never returns. Help please.
Code:
m_socket = socket(AF_INET, SOCK_STREAM, 0);
struct sockaddr_in dest;
if ( m_socket )
{
memset(&dest, 0, sizeof(dest)); /* zero the struct */
dest.sin_family = AF_INET;
dest.sin_addr.s_addr = inet_addr(address); /* set destination IP number */
dest.sin_port = htons(port);
if (connect(m_socket, (struct sockaddr *)&dest, sizeof(struct sockaddr)) == SOCKET_ERROR)
{
return false;
}
else
{
std::vector<char> inStartup1(2);
int recvReturn = recv(Socket, &inStartup1.at(0), inStartup1.size(), 0);
}
recv is a blocking call. This would help you:-
The recv() call is normally used only on a connected socket.It returns the length of the message on successful completion. If a message is too long to fit in the supplied buffer, excess bytes may be discarded DEPENDING on the type of socket the message is received from.
If no messages are available at the socket, the receive calls wait for a message to arrive, unless the socket is nonblocking, in which case the value -1 is returned and the external variable errno is set to EAGAIN or EWOULDBLOCK. The receive calls normally return any data available, up to the requested amount, rather than waiting for receipt of the full amount requested.
Taking this one step further, on a server this is how you would correctly handle a connection (socket or serial port does not matter):
make the socket/port non-blocking: this is the first important step; it means that recv() will read what is available (if anything) and return the number of read bytes or -1 in case of an error.
use select(), with a timeout, to find out when data becomes available. So now you wait for a certain amount of time for data to become available and than read it.
The next problem to handle is making sure you read the full message. Since there is no guarantee that the whole message will be available when you call recv(), you need to save whatever is available and go back to select() and wait for the next data to become available.
Put everything in a while(cond) construct to make sure you read all the data.
The condition in the while is the only thing left to figure out - you either know the length of the expected message or you use some delimiters to mark the end of the message.
Hope this helps!

WinSock2: handling accepted incoming connections in separate threads with recv and send

I am implementing a Windows-based web server handling multiple specific HTTP requests from clients using WinSock2. I have a class to start and stop my server. It looks something like this:
class CMyServer
{
// Not related to this question methods and variables here
// ...
public:
SOCKET m_serverSocket;
TLM_ERROR Start();
TLM_ERROR Stop();
static DWORD WINAPI ProcessRequest(LPVOID pInstance);
static DWORD WINAPI Run(LPVOID pInstance);
}
where TLM_ERROR is a type definition for my server's errors enumeration.
bool CMyServer::Start() method starts the server creating a socket listening on configured port and creating a separate thread DWORD CMyServer::Run(LPVOID) to accept incoming connections like described here:
// Creating a socket
m_serverSocket = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (m_serverSocket == INVALID_SOCKET)
return TLM_ERROR_CANNOT_CREATE_SOCKET;
// Socket address
sockaddr_in serverSocketAddr;
serverSocketAddr.sin_family = AF_INET; // address format is host and port number
serverSocketAddr.sin_addr.S_un.S_addr = inet_addr(m_strHost.c_str()); // specifying host
serverSocketAddr.sin_port = htons(m_nPort); // specifying port number
// Binding the socket
if (::bind(m_serverSocket, (SOCKADDR*)&serverSocketAddr, sizeof(serverSocketAddr)) == SOCKET_ERROR)
{
// Error during binding the socket
::closesocket(m_serverSocket);
m_serverSocket = NULL;
return TLM_ERROR_CANNOT_BIND_SOCKET;
}
// Starting to listen to requests
int nBacklog = 20;
if (::listen(m_serverSocket, nBacklog) == SOCKET_ERROR)
{
// Error listening on socket
::closesocket(m_serverSocket);
m_serverSocket = NULL;
return TLM_ERROR_CANNOT_LISTEN;
}
// Further initialization here...
// ...
// Creating server's main thread
m_hManagerThread = ::CreateThread(NULL, 0, CTiledLayersManager::Run, (LPVOID)this, NULL, NULL);
I use ::accept(...) to wait for incoming client connections in CMyServer::Run(LPVOID), and after new connection has been accepted I create a separate thread CMyServer::ProcessRequest(LPVOID) to receive a data from a client and send a response passing the socket returned by ::accept(...) as part of thread function's argument:
DWORD CMyServer::Run(LPVOID pInstance)
{
CMyServer* pTLM = (CMyServer*)pInstance;
// Initialization here...
// ...
bool bContinueRun = true;
while (bContinueRun)
{
// Waiting for a client to connect
SOCKADDR clientSocketAddr; // structure to store socket's address
int nClientSocketSize = sizeof(clientSocketAddr); // defining structure's length
ZeroMemory(&clientSocketAddr, nClientSocketSize); // cleaning the structure
SOCKET connectionSocket = ::accept(pTLM->m_serverSocket, &clientSocketAddr, &nClientSocketSize); // waiting for client's request
if (connectionSocket != INVALID_SOCKET)
{
if (bContinueRun)
{
// Running a separate thread to handle this request
REQUEST_CONTEXT rc;
rc.pTLM = pTLM;
rc.connectionSocket = connectionSocket;
HANDLE hRequestThread = ::CreateThread(NULL, 0, CTiledLayersManager::ProcessRequest, (LPVOID)&rc, CREATE_SUSPENDED, NULL);
// Storing created thread's handle to be able to close it later
// ...
// Starting suspended thread
::ResumeThread(hRequestThread);
}
}
// Checking whether thread is signaled to stop...
// ...
}
// Waiting for all child threads to over...
// ...
}
Testing this implementation manually gives me the desired results. But when I send multiple requests generated by JMeter I can see that some of them are not handled properly by DWORD CMyServer::ProcessRequest(LPVOID). Looking at log file created by ProcessRequest I determine 10038 WinSock error code (meaning that ::recv call was tried on nonsocket), 10053 error code (Software caused connection abort) or even 10058 error code (Cannot send after socket shutdown). But the 10038th error occurs more often than others mentioned.
It looks like a socket was closed somehow but I close it only after ::recv and ::send have been called in ProcessRequest. I also thought that it can be an issue related to using ::CreateThread instead of ::_beginthreadex but as I can get it could only lead to memory leaks. I don't have any memory leaks detected by the method described here so I have doubts that it is the reason. All the more, ::CreateThread returns a handle that can be used in ::WaitForMultipleObjects to wait for threads to be over, and I need it to stop my server properly.
Could these errors occur due to a client doesn't want to wait for response anymore? I am out of ideas, and I will thank you if you tell me what I am missing or doing/understanding wrong. By the way, both my server and JMeter run on the localhost.
Finally, here is my implementation of ProcessRequest method:
DWORD CMyServer::ProcessRequest(LPVOID pInstance)
{
REQUEST_CONTEXT* pRC = (REQUEST_CONTEXT*)pInstance;
CMyServer* pTLM = pRC->pTLM;
SOCKET connectionSocket = pRC->connectionSocket;
// Retrieving client's request
const DWORD dwBuffLen = 1 << 15;
char buffer[dwBuffLen];
ZeroMemory(buffer, sizeof(buffer));
if (::recv(connectionSocket, buffer, sizeof(buffer), NULL) == SOCKET_ERROR)
{
stringStream ss;
ss << "Unable to receive client's request with the following error code " << ::WSAGetLastError() << ".";
pTLM->Log(ss.str(), TLM_LOG_TYPE_ERROR);
::SetEvent(pTLM->m_hRequestCompleteEvent);
return 0;
}
string str = "HTTP/1.1 200 OK\nContent-Type: text/plain\n\nHello World!";
if (::send(connectionSocket, str.c_str(), str.length(), 0) == SOCKET_ERROR)
{
stringStream ss;
ss << "Unable to send response to client with the following error code " << ::WSAGetLastError() << ".";
pTLM->Log(ss.str(), TLM_LOG_TYPE_ERROR);
::SetEvent(pTLM->m_hRequestCompleteEvent);
return 0;
}
::closesocket(connectionSocket);
connectionSocket = NULL;
pTLM->Log(string("Request has been successfully handled."));
::SetEvent(pTLM->m_hRequestCompleteEvent);
return 0;
}
You pass a pointer to the REQUEST_CONTEXT to every newly created thread. However this is an automatic variable, allocated on the stack. Hence its lifetime is limited to its scope. It ends right after you call ResumeThread.
Practically what happens is that the same memory for REQUEST_CONTEXT is used in every loop iteration. Now imagine you accept 2 connections in a short time internal. It's likely that at the time the first thread starts execution its REQUEST_CONTEXT will already be overwritten. So that you actually have 2 threads serving the same socket.
The easiest fix is to allocate the REQUEST_CONTEXT dynamically. That is, allocate it upon new accept, pass its pointer to the new thread. Then during the thread termination don't forget to delete it.
When creating the thread to handle requests, you give the address to a local variable as argument to the thread. The data of this pointer will not be valid as soon as the local variable is out of scope. Create it dynamically with new and delete it in the thread.

Multipe Send()'s and Recv()'s using Winsock2

I am working on a small networking project using Winsock2. I am using a TCP connection and actually am working with IRC as an example since IRC is fairly simple. What I am doing is connecting to the server and sending an initial buffer so the server recognizes a connection. This works fine.
What concerns me is that I cannot write to the socket again. It seems my program hangs if I do not use shutdown() (on SD_SEND) after I send the initial buffer.
So the next data (based on RFC 1459) I want to send is the USER and NICK information, however, I feel like using shutdown() is what is causing my current issue. Is there a way to reinitialize the write socket?
Thanks!
ADDED CODE
Note that these are located within a class so it still may be slightly obscured. I am writing it into a simpler example using the elements I have. Everything is properly defined, so if I forget to define things, I apologize, but many of my recurring variables are defined for the scope of the class.
int main(int argc,char *argv[])
{
int iResult;
SOCKET Connection;
iResult = WSAStartup(MAKEWORD(2,2), &wsaData);
if(iResult != 0)
throw "Startup failed!";
// Prep stuff
ZeroMemory(&hints,sizeof(hints)); // This struct is defined addrinfo
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
// Now resolve server addr
iResult = getaddrinfo(argv[1],argv[2],&hints,&result);
if(iResult != 0)
throw "getaddrinfo() failed!";
// Now try to connect
for(ptr=result;ptr != NULL;ptr = ptr->ai_next)
{
Connection = socket(ptr->ai_family, ptr->ai_socktype, ptr->ai_protocol); // defined in that "hints" struct. argument number 2
if(Connection == INVALID_SOCKET)
{
freeaddrinfo(result);
WSACleanup();
throw "Error at socket();";
}
// Connect to server
iResult = connect(Connection, ptr->ai_addr, (int)ptr->ai_addrlen);
if(iResult != 0)
{
closesocket(Connection);
Connection = INVALID_SOCKET;
continue;
}
break;
}
freeaddrinfo(result);
// Send initial buffer so server know you're there :)
iResult = send(Connection, "", 1, 0);
if(iResult == SOCKET_ERROR)
{
close();
throw "Could not send initial buffer!";
}
// Close this connection for the inital buffer
iResult = shutdown(Connection, SD_SEND);
if(iResult == SOCKET_ERROR)
{
close();
throw "Could not close initial buffer socket!";
}
bool connected = true;
// This is taken from my read function within the class
// BEGIN READ FUNCTION
iResult = 0; // Reset
std::string data = ""; // Capture the output and send it all at once!
// This only works if we're connected sweet cakes <3
if(connected)
{
do
{
iResult = recv(socket, recvbuf, BUFLEN, 0);
if(iResult > 0)
{
// Working properly
// Save all data even if there is more than BUFLEN sent
continue;
}
else if(iResult == 0)
// Connection closed properly
break;
else
printf("ERROR!");
} while(iResult > 0);
}
data += recvbuf;
ZeroMemory(&recvbuf,sizeof(recvbuf));
// Function returns std::string but essentially this is what happens
printf("%s",data.c_str());
// END READ FUNCTION
// BEGIN WRITE FUNCTION
iResult = 0; // Reset
SOCKET socket = Connection; // Write function arg 1
char *data; // Write function arg 2
iResult = send(socket,data,(int)strlen(data),0);
if(iResult == SOCKET_ERROR)
{
close();
printf("Could not write data: %ld",WSAGetLastError());
return 1;
}
// Data sent, let's close the write socket
iResult = shutdown(socket, SD_SEND);
if(iResult != 0)
{
close();
printf("Could not close write socket!");
return 1;
}
//return iResult;
// END WRITE FUNCTION
// Now that will produce "Could not write data: 0" for any value of data
// So realistically I want to send the USER and NICK data, then read
// and probably process a PING string from the server and send my PONG response
return 0;
}
I hope that clarifies things!
EDIT
I think I have figured out what is going wrong. I made the corrections listed below to my code; thanks guys. However, it's my read loop which is messing with things. Even after it has all the information it seems that it is waiting for the connection to be closed before it sends the output. Any ideas? My output currently looks like this (the bytes written/total is something I added to make sure everything was going down the wire correctly)
Bytes Written: 41
Bytes Total: 41
Data: ERROR :Closing Link: raged123[127.0.0.1] 6667 (Ping timeout)
...
:irc.foonet.com NOTICE AUTH :*** Found your hostname (cached)
PING :2ED39CE5
[A bunch of funny characters]WinSock 2.0
So it appears to have timed out because the PING did not receive PONG in time, however, I cannot send the PONG without first processing the PING request which means I would need to be able to read the output before the connection is closed. Any ideas?
May I suggest a fun document on the subject? Chapter's 6 and 7 of Beej's Guide to Network Programming
It has several examples.
There shouldn't be any need to send an "initial buffer" like you've done. The server will receive notification when a client connects, it doesn't depend on the client actually sending anything. (And in particular, the IRC protocol says that the server will start sending you stuff as soon as you connect.)
The call to shutdown() is highly suspicious. Why did you expect to need to do this? Shutting down a socket is something you do when you're done with the connection, not when you're just starting. You should remove this completely.
I'm not sure what type recvbuf is, but it looks like you're using it incorrectly. Something that can be appended to a std::string probably can't also have ZeroMemory() called on it, without one or the other of those being wrong. You also aren't using iResult which is the actual number of bytes received from the server.
Your write function also contains a call to shutdown(), which you should remove.
According to man send(2)
On success, these calls return the
number of characters sent. On error,
-1 is returned, and errno is set appropriately.
What happens is probably that send does not send the full buffer at once, you must use a loop around it.
This might not be your actual problem however since you,re sending an empty string...
I'd highly recommend using Wireshark so you can check what goes down to the wire
data += recvbuf;
This can't work. There's no way string::operator+= to know how many bytes have been received. This function expects a C-style string, not an arbitrary chunk of bytes.
But you also have a very fundamental design problem. You're expecting your program to speak the IRC protocol, but it contains no implementation of that protocol whatsoever. For example, the IRC protocol specifies a particular way that messages are delimited, and you have no code whatsoever to parse those messages.
As a result, your transition from reading to writing occurs at essentially a random time determined by the vagaries of TCP timing and how the server chooses to segment its output. Since the server is permitted to segment its output however it pleases (the protocol is clear that the client cannot rely on segmentation to parse the protocol but instead must rely on the line-oriented nature), your program's behavior is unpredictable.

Winsocks Send and Receive

I'm using the WSAEventSelect I/O model in Windows Sockets and now I want to know that how may I know that my send and receive operations have sent and received all of the data?
After I know that, how should I design a way so that it sends the data fully? Any examples would be really appreciated.
Here is the code (sample code from the book I'm learning from):
SOCKET SocketArray [WSA_MAXIMUM_WAIT_EVENTS];
WSAEVENT EventArray [WSA_MAXIMUM_WAIT_EVENTS],
NewEvent;
SOCKADDR_IN InternetAddr;
SOCKET Accept, Listen;
DWORD EventTotal = 0;
DWORD Index, i;
WSANETWORKEVENTS NetworkEvents;
// Set up socket for listening etc...
// ....
NewEvent = WSACreateEvent();
WSAEventSelect(Listen, NewEvent,
FD_ACCEPT │ FD_CLOSE);
listen(Listen, 5);
SocketArray[EventTotal] = Listen;
EventArray[EventTotal] = NewEvent;
EventTotal++;
while(TRUE)
{
// Wait for network events on all sockets
Index = WSAWaitForMultipleEvents(EventTotal,
EventArray, FALSE, WSA_INFINITE, FALSE);
Index = Index - WSA_WAIT_EVENT_0;
// Iterate through all events to see if more than one is signaled
for(i=Index; i < EventTotal ;i++
{
Index = WSAWaitForMultipleEvents(1, &EventArray[i], TRUE, 1000,
FALSE);
if ((Index == WSA_WAIT_FAILED) ││ (Index == WSA_WAIT_TIMEOUT))
continue;
else
{
Index = i;
WSAEnumNetworkEvents(
SocketArray[Index],
EventArray[Index],
&NetworkEvents);
// Check for FD_ACCEPT messages
if (NetworkEvents.lNetworkEvents & FD_ACCEPT)
{
if (NetworkEvents.iErrorCode[FD_ACCEPT_BIT] != 0)
{
printf("FD_ACCEPT failed with error %d\n",
NetworkEvents.iErrorCode[FD_ACCEPT_BIT]);
break;
}
// Accept a new connection, and add it to the
// socket and event lists
Accept = accept(
SocketArray[Index],
NULL, NULL);
NewEvent = WSACreateEvent();
WSAEventSelect(Accept, NewEvent,
FD_READ │ FD_CLOSE);
EventArray[EventTotal] = NewEvent;
SocketArray[EventTotal] = Accept;
EventTotal++;
printf("Socket %d connected\n", Accept);
}
// Process FD_READ notification
if (NetworkEvents.lNetworkEvents & FD_READ)
{
if (NetworkEvents.iErrorCode[FD_READ_BIT] != 0)
{
printf("FD_READ failed with error %d\n",
NetworkEvents.iErrorCode[FD_READ_BIT]);
break;
}
// Read data from the socket
recv(SocketArray[Index - WSA_WAIT_EVENT_0],
buffer, sizeof(buffer), 0);
// here I do some processing on the data received
DoSomething(buffer);
// now I want to send data
send(SocketArray[Index - WSA_WAIT_EVENT_0],
buffer, sizeof(buffer), 0);
// how can I be assured that the data is sent completely
}
// FD_CLOSE handling here
// ......
// ......
}
}
}
What I thought, that I would set a boolean flag to determine that the receive has completed (the message will have its length prefixed) and then start processing that data. But what about send()? Can you please tell me the possibilities.
**EDIT:**See the FD_READ event part
Unless the protocol (application layer) you are handling gives you any information about how many data you're about to receive, the only way to determine if there is nothing more to received is when the peer disconnects. If the server simply stop sending, you can't determine if its the end or its just busy. It ends when it ends. You also can't determine if the server disconnected because its the end or because the connection was broken.
Thats why most protocols inform the peer about how many bytes it is going to be sent before sending it, or by placing a boundary in the end of the data.
About sending, you must be aware of the buffer you're using. When you send(), it goes to a buffer (with 64KB by default). send() returns the number of bytes placed in the buffer, if its less then the bytes you were trying to send, you have to manage it to try again in the next time you receive a FD_WRITE event.
You can't have sure about how much data was already received by the peer unless it keeps you informed (mIRC DDC does that).
Not sure it clearfyed your doubts, hope it helped :)
When you are doing the recv, you need to save the return status to determine if the data was received. recv returns the number of bytes received, and I would use the flag MSG_WAITALL instead of zero for the fourth parameter to receive all of the message (based on the buffer size). If the status recv returns is negative, there was an error of some nature, such as connection was close from the other end or there was some other issue.
As for the send, you should save the return value as it also as the status, but in this case, there is not a flag to have all the data sent before returning. You will have to determine the amount send and adjust the buffer and send size based on the value. As with recv, a negative value indicates an error has occurred.
I would read the function descriptions on the Microsoft website for recv and send for more information on the return values and flags.