C++ socket only receives first send? - c++

I've been looking at this for a few hours. I've tried everything I can think of, and frankly It doesn't make sense. I actively send and receive with the socket with no problems, but as soon as I change the data to a different message, same style, it stops recieving. I'm using TCP. I have a manager process send up to N router messages with table data. I later send a packet, same style, it receive it, and then stops receiving.... The code gets back to the top of the loop, but just doesn't get any more data.
Oh the networking code I'm using is a copy and paste of beejs TCP server client code. http://beej.us/guide/bgnet/output/html/multipage/clientserver.html
Manager thread, this part works
for(vector< vector<int> >::iterator it = table.begin(); it!=table.end(); ++it ){
vector< int > d = *it;
for(vector<int>::iterator itA = d.begin(); itA!=d.end(); ++itA ){
cout << "Sending... "<< *itA << endl;
s <<*itA<<" ";
}
if (send(new_fd, s.str().c_str(), 13, 0) == -1)
perror("Serv:send");
sleep(2);
logs << "Sent to router " << i <<":\n" << s.str();
writeLog(logs.str().c_str());
s.str("");
logs.str("");
}
s<<"done";
if (send(new_fd, s.str().c_str(), 13, 0) == -1)
perror("Serv:send");
writeLog(s.str().c_str());
manage 2, where only the first message gets through
for(vector <vector <int > >::iterator it = toSendPackets.begin(); it != toSendPackets.end(); ++it){
sleep(3);
vector<int> tsp = *it;
int a,b,c = 0;
for(vector<int>::iterator itr = tsp.begin(); itr != tsp.end(); ++itr){
if(c==0){
a = *itr;
}
if(c==1){
b = *itr;
}
c++;
}
ss.str("");
ss << a << " " << b;
for(int i = 0; i < numN; i++){
int curSoc = socketList[i];
stringstream sl;
sl<<"sent:"<< ss.str().c_str();
cout << "sending.. " << ss.str() << " to " << i << endl;
if (send(curSoc, "HOP", strlen("HOP")+1, 0) == -1)
perror("Serv:send");
sleep(2);
if (send(curSoc, ss.str().c_str(), strlen(ss.str().c_str())+1, 0) == -1)
perror("Serv:send");
writeLog(sl.str().c_str());
sleep(1);
}
}
Router code.
The manager code above and manager code 2 both send to this part of the code.
It gets the first send, in this case "HOP" and then nothing? I removed the HOP packet parsing, so it litterally should only state that something was read.
if(tid == 0){// TCP
stringstream s;
bool proc = true;
while(!doneFlag){
proc = true;
cout << "TCP RECEIVING... " << endl;
int numbytes = 0;
while(numbytes==0){
if ((numbytes = recv(sockfd, buf, MAXDATASIZE, 0)) == -1) {
perror("recvROUTERThread0");
exit(1);
}
}
buf[numbytes] = '\0';
numbytes = 0;
if(strcmp("Quit",buf)==0){
writeLog("Quit read",outName);
doneFlag = true;
close(net.sockfd);
floodUDP("Quit");
pthread_exit(NULL);
}
else if(strcmp("HOP",buf)==0){
cout << "HOP READ" << endl;
numbytes = 0;
while(numbytes==0){
if ((numbytes = recv(sockfd, buf, MAXDATASIZE, 0)) == -1) {
perror("recvROUTERThread0");
exit(1);
}
}
s << id << "R: Receiving a routing command! " << buf;
cout << s.str().c_str() << endl;
writeLog(s.str().c_str(),outName);
HOPpacket hpo = genHopOrig(s.str().c_str());
if(hpo.s == atoi(id)){
printHOP(hpo);
// cout << "PACKET " << pr << endl;
stringstream sl;
char* hop = generateHopPacket(hpo);
sl << "Generating HOP packet and sending.. " << hop;
writeLog(sl.str().c_str(),outName);
sendHOP(hop);
}
}
else{
cout << "Table row data from manager" << endl;
s.str("");
s << id << "R: MANAGER MESSAGE: " << buf << endl;
cout << s.str() << endl;
writeLog(s.str().c_str(),outName);
int intID = atoi(id);
vector <int> tr = processTR(buf,intID,basePN);
table.push_back(tr);
}
}
}
My output. In this case there are 10 routers running. Note I didn't change my prints to state that it was sending HOP then 0 5 ..
sending.. 0 5 to 0
HOP READ
WRITTING Manager log:12-11-23::4:6:26:
sent:0 5
sending.. 0 5 to 1
HOP READ
WRITTING Manager log:12-11-23::4:6:29:
sent:0 5
sending.. 0 5 to 2
HOP READ
WRITTING Manager log:12-11-23::4:6:32:
sent:0 5
sending.. 0 5 to 3
HOP READ
WRITTING Manager log:12-11-23::4:6:35:
sent:0 5
sending.. 0 5 to 4
HOP READ
WRITTING Manager log:12-11-23::4:6:38:
sent:0 5
sending.. 0 5 to 5
HOP READ
WRITTING Manager log:12-11-23::4:6:41:
sent:0 5
sending.. 0 5 to 6
HOP READ
WRITTING Manager log:12-11-23::4:6:44:
sent:0 5
sending.. 0 5 to 7
HOP READ
WRITTING Manager log:12-11-23::4:6:47:
sent:0 5
sending.. 0 5 to 8
HOP READ
WRITTING Manager log:12-11-23::4:6:50:
sent:0 5
sending.. 0 5 to 9
HOP READ
WRITTING Manager log:12-11-23::4:6:53:
sent:0 5
sending.. 3 9 to 0
WRITTING Manager log:12-11-23::4:6:59:
sent:3 9
sending.. 3 9 to 1
WRITTING Manager log:12-11-23::4:7:2:
sent:3 9
sending.. 3 9 to 2
WRITTING Manager log:12-11-23::4:7:5:
sent:3 9
sending.. 3 9 to 3
WRITTING Manager log:12-11-23::4:7:8:
sent:3 9
sending.. 3 9 to 4
WRITTING Manager log:12-11-23::4:7:11:
sent:3 9
sending.. 3 9 to 5
WRITTING Manager log:12-11-23::4:7:14:
sent:3 9
sending.. 3 9 to 6
WRITTING Manager log:12-11-23::4:7:17:
sent:3 9
sending.. 3 9 to 7
WRITTING Manager log:12-11-23::4:7:20:
sent:3 9
sending.. 3 9 to 8
WRITTING Manager log:12-11-23::4:7:23:
sent:3 9
sending.. 3 9 to 9
WRITTING Manager log:12-11-23::4:7:26:
sent:3 9

There is a problem when you recv data, TCP is a stream based socket not a message based one, so if you use:
send( sock, buf1, len1, 0 ); // Send HOP, since it is small, you OS merge this
send( sock, buf2, len2, 0 ); // with next send!
and then try to receive data using recv it is not guaranteed that you receive data in 2 separate calls to recv, so you may receive both sent buffers in one call to recv:
recv( sock, buf, len, 0 ); // This may receive both buffers in one call
so your next call to recv will be blocked for data that already received in first call! Also they may be another problem for when you send large buffer, then recv may receive less data than a single message passed using send.
You must define a protocol that define end of message in the received stream and then receive your data according to that protocol. for example, you may first send length of message or define something that indicate end of it(for example \0 or \r\n).
Sorry for my incomplete description of the error. In your comment you say that you have increased the HOP message size! But it certainly isn't a good practice, also increased size is so small that never force OS to send it immediately( actually there is no certain size that force OS do that ). If you want OS to send your data immediately, you should disable Nagle algorithm using TCP_NO_DELAY option, but before doing that take a look at How do I use TCP_NODELAY?. Doing this is not a good practice either and beside that while doing this cause your packet sent immediately as you call send but it never force OS on receiver side to receive messages separately!! so what is the correct way of doing this?
I explain the problem in detail:
// I don't know exact value of MAXDATASIZE but I will assume it is 128
char buf[ MAXDATASIZE ];
int numbytes = recv( sock, buf, MAXDATASIZE, 0 );
if( numbyte == -1 ) {
// Handle error
}
// I assume HOP_MSG is a defined constant that contain value of HOP message
if( strcmp(buf, HOP_MSG) == 0 ) { // <-- (1)
while( (numbytes = recv(sock, buf, MAXDATASIZE, 0)) != -1 ) { // <-- (2)
if( numbytes == 0 ) break;
}
if( numbytes == -1 ) {
// Handle error
}
}
But wait! in line that marked with (1) I assumed recv read HOP_MSG completely and only HOP_MSG, but why?? As I said before TCP is a stream protocol and there is no message boundary in it, so it may read only 2 bytes!! or it read 1KB( that is certainly more than HOP_MSG, so what should I do??
The working answer is something like follow:
int receive_till_zero( SOCKET sock, char* tmpbuf, int& numbytes ) {
int i = 0;
do {
// Check if we have a complete message
for( ; i < numbytes; i++ ) {
if( buf[i] == '\0' ) {
// \0 indicate end of message! so we are done
return i + 1; // return length of message
}
}
int n = recv( sock, buf + numbytes, MAXDATASIZE - numbytes, 0 );
if( n == -1 ) {
return -1; // operation failed!
}
numbytes += n;
} while( true );
}
void remove_message_from_buffer( char* buf, int& numbytes, int msglen ) {
// remove complete message from the buffer.
memmove( buf, buf + msglen, numbytes - msglen );
numbytes -= msglen;
}
void main() {
SOCKET s;
char buf[ MAXDATASIZE ];
int numbytes = 0, msglen;
// Initialize socket and connect to server, you already do that
while( true ) {
msglen = receive_till_zero( s, buf, numbytes );
if( msglen == -1 ) {/* Handle error */}
if( !strcmp(buf, HOP_MSG) ) {
remove_message_from_buffer( buf, numbytes, msglen );
msglen = receive_till_zero( s, buf, numbytes );
if( msglen == -1 ) {/* Handle error */}
std::cout << "Message received from server: " << buf << std::endl;
remove_message_from_buffer( buf, numbytes, msglen );
}
}
}
By debugging this code you will certainly understand its purpose, receive_till_zero assume there is already some pending data in the buffer from previous call to recv, so it will first check if there is a complete message in the buffer or not and also it never assume receiving data completed just by one call to recv so it will call recv in a loop until it see a \0 in the buffer. After we finished with data in the buffer we call remove_message_from_buffer to eat that data and only that data, and not just start receiving from the start of buffer, since they may already some data in the buffer.
As you see code is a little complicated, for a better programming model and a better C++ code you may use boost::asio that have a very good design and work perfectly with C++ and iostream

Related

How to read all the data in socket programming (c++)?

I am learning socket programming in C++. I have initialised by buffer value at 10. I have used select() function to monitor the socket. When the client sends data which is greater than my buffer can accommodate, it will read buffer size worth of data and will again go through the all my clients and read the remaining data. How can I read all the data at once without parsing through the loop again to read the remaining data ?
Thank you for your help.
for(i = 0;i < max_clients; i++){
sd = clients_list[i];
if(FD_ISSET(sd,&temp)){
char buf[10];
int n = recv(sd, buf, sizeof(buf),0);
if(n == -1){
perror("recv");
}else if(n == 0){
cout << "Client is GONE " << endl;
close(sd);
clients_list[i] = 0;
}
else{
buf[n] = '\0';
cout << "From the node: " << buf << endl;
}
}
}
Just to complete my comment - this code snippet may help to understand
not tested code
for (i = 0;i < max_clients; i++) {
sd = clients_list[i];
if (FD_ISSET(sd,&temp)) {
char buf[10];
int n;
int flag;
do {
n = recv(sd, buf, sizeof(buf),0);
if (n > 0) {
buf[n] = '\0';
cout << "From the node: " << buf << endl;
ioctl(sd, FIONREAD, &flag);
} else {
flag = 0;
}
} while (flag > 0);
if (n < 0) {
perror("recv");
// in case of an error you may actively close the socket
// and end transmission by server
close(sd);
clients_list[i] = 0;
} else {
cout << "Client has currently no further transmission " << endl;
// don't close socket maybe later new transmission
// active close socket by server process only if
// e.g. a timeout has reached
}
}
}
By my knowledge you can't determine the buffer size that was sent by the other side. I think your way is the best way to deal with it, but you always can use some tricks.
1. Determine the buffer size that will be sent as a constant and thus you may get the wanted amount every time.
2. A way which I think is the best: Use a self made protocol to determine the length of the message that was sent. You may for example read the 4 first bytes to determine the size of your message and then read the data off the buffer by the given size.
You can use the next transformation:
char* c = new char[4];
recv(sd, c, sizeof(c), 0);
int len = (*(int*)c);
delete[] c;
char* but = new char[len];
recv(sd, but, len, 0);
Firstly, the network is unreliable and unpredictable, so we can not know how many bytes we will receive, that is the start point of designing a server.
Secondly, I think if you want a more elegent server, you may want to try epoll or IOCP, they still have loops, but the I/O multiplexing performance are much better.
Thirdly, if you want to accept all the data, you may try to construct a buffer. In real network, we always buffer the data using kafka or some other apps.
How can I read all the data at once without parsing through the loop
again to read the remaining data ?
Generally speaking, you can't, since all of the data might not be available yet. If you want, you can tell recv() to block until sizeof(buf) bytes are available, by passing in MSG_WAITALL instead of 0 to its final ("flags") argument... but note that even then it is possible for recv() to return fewer than sizeof(buf) bytes in some cases (e.g. if a signal is caught, or the connection is closed before sizeof(buf) bytes are received, and so on). So even if you use MSG_WAITALL you will still want to implement short-read-handling logic in order to get 100% correct behavior.
Also note that the sender is under no obligation to send all of the bytes you are expecting in a timely manner, nor is the network under any obligation to deliver all of the bytes in a timely manner. So it's perfectly possible that a sender might send sizeof(buf)-1 bytes to you, then wait 15 minutes before sending the last byte, and if you are blocked in a recv() call waiting for that last byte, then your server will be unresponsive to all of its other clients during that long period.
Therefore when implementing a multiplexed/single-threaded server like this, it's usually best to set the sockets to non-blocking mode, and keep a separate received-data buffer for each client's socket. That way you can loop through your sockets list, recv() as much data as you can from each socket (without blocking), and append that data to that socket's associated buffer, then check the buffer to see if you have enough data in it yet to handle the next chunk, and if you do, handle the chunk and then remove the data from the buffer, and continue. That way client B never has to wait for client A to finish sending a message to the server.
I have used ioctl() function with FIONREAD to check whether there is more data to read it or not and it has been working.
Here is the code:
for(i = 0;i < max_clients; i++){
sd = clients_list[i];
if(FD_ISSET(sd,&temp)){
receive_more:
char buf[10];
int arg = 0;
int n = recv(sd, buf, sizeof(buf),0);
if(n == -1){
perror("recv");
}else if(n == 0){
cout << "Client is GONE " << endl;
close(sd);
clients_list[i] = 0;
}
else{
buf[n] = '\0';
cout << "From the node: " << buf << endl;
ioctl(sd,FIONREAD,&arg);
if(arg > 0){
goto receive_more;
}
}
}
}

Winsock RIO: RIOReceive returns immediately with no bytesTransferred

I'm having problems with getting winsock RIO working.
It seems that every time I post a RIOReceive it returns immediately with 0 bytes transferred, and my peer can't get a message through.
After posting a RIOReceive, I wait on the RIODequeCompletion, which deques immediately with numResults = 1, but when I inspect the bytesTransferred of the RIORESULT struct, it's 0. This tells me that I'm not setting this thing up properly, but I can't find docs or examples that tell me what else I should be doing.
The internet seems to have very little on RIO. I've looked through MSDN, Len Holgate with TheServerFramework, this site, and two GitHub RIO servers.
RIOEchoServer and RIOServer_sm9 are on GitHub, but I can't post more than two links (this is my first question on this site).
This code is just to get things proven. It's currently not set to use the sendCQ, doesn't handle errors well, etc...
Here's the prep work:
void serverRequestThread() {
//init buffers
//register big buffers
recvBufferMain = rio.RIORegisterBuffer(pRecvBufferMain, bufferSize);
sendBufferMain = rio.RIORegisterBuffer(pSendBufferMain, bufferSize);
if (recvBufferMain == RIO_INVALID_BUFFERID) {
cout << "RIO_INVALID_BUFFERID" << endl;
}
if (sendBufferMain == RIO_INVALID_BUFFERID) {
cout << "RIO_INVALID_BUFFERID" << endl;
}
//create recv buffer slice
recvBuffer1.BufferId = recvBufferMain;
recvBuffer1.Offset = 0;
recvBuffer1.Length = 10000;
//create send buffer slice
sendBuffer1.BufferId = sendBufferMain;
sendBuffer1.Offset = 0;
sendBuffer1.Length = 10000;
//completion queue
recvCQ = rio.RIOCreateCompletionQueue(CQsize, NULL);
sendCQ = rio.RIOCreateCompletionQueue(CQsize, NULL);
if (recvCQ == RIO_INVALID_CQ) {
cout << "RIO_INVALID_CQ" << endl;
}
if (sendCQ == RIO_INVALID_CQ) {
cout << "RIO_INVALID_CQ" << endl;
}
//start a loop for newly accept'd socket
while (recvCQ != RIO_INVALID_CQ && sendCQ != RIO_INVALID_CQ) {
//get accept'd socket
struct sockaddr_in saClient;
int iClientSize = sizeof(saClient);
acceptSocket = accept(listenSocket, (SOCKADDR*)&saClient, &iClientSize);
if (acceptSocket == INVALID_SOCKET) {
cout << "Invalid socket" << endl;
printError();
}
//register request queue
requestQueue = rio.RIOCreateRequestQueue(
acceptSocket, //socket
10, //max RECVs on queue
1, //max recv buffers, set to 1
10, //max outstanding sends
1, //max send buffers, set to 1
recvCQ, //recv queue
recvCQ, //send queue
pOperationContext //socket context
);
if (requestQueue == RIO_INVALID_RQ) {
cout << "RIO_INVALID_RQ" << endl;
printError();
}
I now post a RIOReceive:
//start a loop to repin recv buffer for socket
while (acceptSocket != INVALID_SOCKET) {
//pin a recv buffer to wait on data
recvSuccess = rio.RIOReceive(
requestQueue, //socketQueue
&recvBuffer1, //buffer slice
1, //set to 1
RIO_MSG_WAITALL, //flags
0); //requestContext
if (recvSuccess == false) {
cout << "RECV ERROR!!!!!!!!\n";
printError();
}
//wait for recv to post in queue
//std::this_thread::sleep_for(std::chrono::milliseconds(3000));
As soon as I call RIODequeCompletion, it returns 1:
numResults = 0;
while (numResults == 0) numResults = rio.RIODequeueCompletion(recvCQ, recvArray, 10);
if (numResults == RIO_CORRUPT_CQ) {
cout << "RIO_CORRUPT_CQ" << endl;
} else if (numResults == 0) {
cout << "no messages on queue\n";
} else if (numResults > 0) {
but when I inspect the bytesTransferred of the RIORESULT, it's always 0:
if (recvArray[0].BytesTransferred > 0) {
//process results
if (pRecvBufferMain[0] == 'G') {
//set respnose html
strcpy(pSendBufferMain, responseHTTP);
sendSuccess = rio.RIOSend(
requestQueue, //socketQueue
&sendBuffer1, //buffer slice
1, //set to 1
0, //flags
0); //requestContext
} else if (pRecvBufferMain[0] == 'P') {
//process post
} else {
//recv'd a bad message
}
} //end bytesTransferred if statement
//reset everything and post another recv
}//end response if statement
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}//end while loop for recv'ing
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}//end while loop for accept'ing
}// end function
Like I said, I'm probably not using RIOReceive correctly, and/or I'm not setting the correct socket options that I need to (none right now).
I appreciate any help with this.
Try removing RIO_MSG_WAITALL. There may be a bug whereby you're only getting the close notification (bytes == 0) rather than getting a completion with the data in it. Either way it would be interesting to see if the code works without the flag.
Do my example servers and tests work on your hardware?
I encountered a similar issue of having zero bytesReceived in my dequeued completion result while using RioReceive with RioNotify and RioDequeueCompletion. I would also see the 'Status' value of WSAEINVAL (Invalid Parameter = 10022) in my dequeued completion result, this seems to indicate the WSA error code for the Receive call.
The particular reason I had the error is because I had allocated memory for a receiveBuffer and I was trying to pass that buffer pointer as my buffer handle in the RIO_BUFFER_SEGMENT given to RioReceive instead of passing the IntPtr returned by RioRegisterBuffer.
I fully blame myself for using too many untyped IntPtrs and losing type checking. :)

How to get the exact message from recv() in winsock programming?

I'm developing a server-client application using Winsock in c++ and have a problem.
For getting the message from the client by the server I use the code below.
int result;
char buffer[200];
while (true)
{
result = recv(client, buffer, 200, NULL);
if (result > 0)
cout << "\n\tMessage from client: \n\n\t" << message << ";";
}
I send the message "Hello" from the client to the server. However the buffer is actually this:
HelloÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌ
What am I missing?
Since recv might not receive as many bytes as you told it, you typically use a function
like this to receive specified number of bytes. Modified from here
int receiveall(int s, char *buf, int *len)
{
int total = 0; // how many bytes we've received
int bytesleft = *len; // how many we have left to receive
int n = -1;
while(total < *len) {
n = recv(s, buf+total, bytesleft, 0);
if (n <= 0) { break; }
total += n;
bytesleft -= n;
}
*len = total; // return number actually received here
return (n<=0)?-1:0; // return -1 on failure, 0 on success
}
It's up to you to null terminate the string if you receive string which is not null terminated.
The result tells you how many bytes were received. recv doesn't add a terminator since, in general, network data is binary data which might not be usable as a C-style string.
You can add a terminator yourself, if you know the message won't contain the termination character:
buffer[result] = 0; // make sure the buffer is large enough
or make a string (or vector, or whatever) from it:
std::string message_str(message, result);
Note that what you receive might not be a single "message", especially if you're uses a stream protocol like TCP. It might contain more than one message, or just the start of one.
memset(&receive[0], 0, sizeof(receive));
To clear the buffer
You didn't initialize your buffer
char buffer[200] = {0};
while (true)
{
result = recv(client, buffer, 200, NULL);
if (result > 0)
cout << "\n\tMessage from client: \n\n\t" << message << ";";
memset(buffer, 0, 200);
}

problem with sending bytes from client to server using TCP

My send() and recv() looks like this:
int Send(const char* buffer, int size)
{
cout << "SIZE: " << size << endl;
int offset;
while(offset < size)
{
int n = ::send(getSocket(), buffer + offset, size - offset, 0);
if(n == SOCKET_ERROR)
{
break;
}
offset += n;
if(offset != size)
{
Sleep(1);
}
}
return offset;
}
int Recv(char* buffer, int size)
{
int n = ::recv(getSocket(), buffer, size, 0);
if(n == SOCKET_ERROR)
{
cout << "Error receiving data" << endl;
}
if(n == 0)
{
cout << "Remote host closed connection" << endl;
}
return n;
}
But my output show kind of many bytes sent that seems strange to me:
Received from client: 669
Sent to web server: 3990336
So it should supose to sent 669 bytes, so from where did it get 3990336 ? It is some kind of error or ?
Thanks.
Did you notice that int offset; is not initialize ?
You have to initialize offset with zero. Otherwise it could be any random value.
You do not need Sleep as send call is blocking.
Buffer that you are sending could be split. So if you send, for example, 2K buffer, you could get it in two parts - 1.5K and 0.5K, so you have to perform multiple reads on a client side. MTU is usually set to 1500 bytes.
Maybe it's just your (stripped down?) example code, but you never actually initialize offset. It might have any value, e.g. -5000 and will cause the loop to send 5669 bytes.

unix domain stream sockets sending more data then it should be

I have two simple programs set up that share data through a unix domain socket. One program reads data out of a Queue and sends it to the other application. Before it is sent each piece of data is front-appended by four bytes with the length, if it is less then four bytes the left over bytes are the '^' symbol.
The client application then reads the first four bytes, sets a buffer to the appropriate size and then reads the rest. The problem that I'm having is that the first time through the message will be sent perfectly. Every other time after that there is extra data being sent so a message like "what a nice day out" would come out like "what a nice day out??X??". So I feel like a buffer is not being cleared correctly but I can't seem to find it.
Client code:
listen(sock, 5);
for (;;)
{
msgsock = accept(sock, 0, 0);
if (msgsock == -1)
perror("accept");
else do
{
char buf[4];
bzero(buf, sizeof(buf));
if ((rval = read(msgsock, buf, 4)) < 0)
perror("reading stream message");
printf("--!%s\n", buf);
string temp = buf;
int pos = temp.find("^");
if(pos != string::npos)
{
temp = temp.substr(0, pos);
}
int sizeOfString = atoi(temp.c_str());
cout << "TEMP STRING: " << temp << endl;
cout << "LENGTH " << sizeOfString << endl;
char feedWord[sizeOfString];
bzero(feedWord, sizeof(feedWord));
if ((rval = read(msgsock, feedWord, sizeOfString)) < 0)
perror("reading stream message");
else if (rval == 0)
printf("Ending connection\n");
else
printf("-->%s\n", feedWord);
bzero(feedWord, sizeof(feedWord));
sizeOfString = 0;
temp.clear();
}
while (rval > 0);
close(msgsock);
}
close(sock);
unlink(NAME);
Server Code
pthread_mutex_lock(&mylock);
string s;
s.clear();
s = dataQueue.front();
dataQueue.pop();
pthread_mutex_unlock(&mylock);
int sizeOfString = strlen(s.c_str());
char sizeofStringBuffer[10];
sprintf(sizeofStringBuffer, "%i", sizeOfString);
string actualString = sizeofStringBuffer;
int tempSize = strlen(sizeofStringBuffer);
int remainder = 4 - tempSize;
int x;
for(x =0; x < remainder; x++)
{
actualString = actualString + "^";
}
cout << "LENGTH OF ACTUAL STRING: " << sizeOfString << endl;
actualString = actualString + s;
cout << "************************" << actualString << endl;
int length = strlen(actualString.c_str());
char finalString[length];
bzero(finalString, sizeof(finalString));
strcpy(finalString, actualString.c_str());
if (write(sock, finalString, length) < 0)
perror("writing on stream socket");
Rather than padding your packet length with '^', you'd be far better off just doing:
snprintf(sizeofStringBuffer, 5, "%04d", sizeOfString);
so that the value is 0 padded - then you don't need to parse out the '^' characters in the receiver code.
Please also edit out your debug code - there's only one write() in the current code, and it doesn't match your description of the protocol.
Ideally - split your sending routine into a function of its own. You can also take advantage of writev() to handle coalescing the string holding the "length" field with the buffer holding the actual data and then sending them as a single atomic write().
Untested code follows:
int write_message(int s, std::string msg)
{
struct iovec iov[2];
char hdr[5];
char *cmsg = msg.c_str();
int len = msg.length();
snprintf(hdr, 5, "%04d", len); // nb: assumes len <= 9999;
iov[0].iov_base = hdr;
iov[0].iov_len = 4;
iov[1].iov_base = cmsg;
iov[1].iov_len = len;
return writev(s, iov, 2);
}
You have to check return values of both write and read not only for -1 but for short (less then requested) writes/reads. You also seem to just continue after printing an error with perror - do an exit(2) or something there.
Two things:
First - on the Server side you are writing off the end of your array.
char finalString[length];
bzero(finalString, sizeof(finalString));
strcpy(finalString, actualString.c_str());
The strcpy() will copy length+1 characters into finalString (character pull the null terminator).
Second (and most likely to be the problem) - on the client side you are not null terminating the string you read in, therefore the printf() will print your string, and then whatever is on the stack up to the point it hits a null.
Increase both buffers by one, and you should be in better shape.