?
Solved

OnReceive() not called

Posted on 2003-03-10
41
Medium Priority
?
979 Views
Last Modified: 2013-11-20
I'm writting a client with CSocket. Everything goes fine until , for no apparent reasons, OnReceive() isn't triggered anymore. (Can take a while, 30 minutes or more) You would think the connection failed or something like that, but sending still works.

Is there a way I can debug that, or is there anyone that heared of something similar? Anyone?

I just upgraded to VC 7, and I didn't notice this before, when I was using VC 6.

I set this at easy for now, tell me if it's not enough.
0
Comment
Question by:Djof
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 26
  • 15
41 Comments
 
LVL 12

Expert Comment

by:williamcampbell
ID: 8113572


  Who is doing the sending? Have they run out of memory or crashed?
0
 

Author Comment

by:Djof
ID: 8113803
Ok, here in detail how my app works.

1) Create the socket
2) Connect the socket
3) Handshake the server (Sending some characters with CSocket::Send and receiving the anwser with CSocket::Receive)
4) Create a CSocketFile and two CArchive, one loading, one storing.

From this point, I use two object of a custom CObject derived class to manage the serialisation of the transmissions. OnReceive triggers a function in my document to serialise the reception object, while user actions triggers the the serialisation and sending of outgoing data.

It works very well, until, like I told, for no apparent reasons, OnReceive simply isn't called anymore. Sending still works at that point. Nothing crashes and the connection still works, and it can stay that way for at least another half an hour. (At which point I stopped testing because I wanted to recompile. But the point is it doesn't affect the connection.)

Thanks.
0
 
LVL 12

Expert Comment

by:williamcampbell
ID: 8114332

  As a test you could setup a timer and if you havn't received anything for say 2 minutes close the socket and reconnect.

  If you still don't receive after the 'reset' then you know it's probably your client. Worst case you have a workaround for now.
0
Will your db performance match your db growth?

In Percona’s white paper “Performance at Scale: Keeping Your Database on Its Toes,” we take a high-level approach to what you need to think about when planning for database scalability.

 

Author Comment

by:Djof
ID: 8115025
I know it's the client. The server hasn't been coded by me, and I know the it works.

So I think I can say it's a problem with the socket. If I ever catch my client stalling again, I will double-proof that with a packet sniffer.
0
 

Author Comment

by:Djof
ID: 8115479
Yup, data is sent from the server.

Increased value to 100 points.
0
 
LVL 12

Expert Comment

by:williamcampbell
ID: 8115511

 Do you have memory leak ... check using Task Manager. Are you out of disk space.

 Next step is to eliminate code until the problem goes away.

 Can I take a look at these

 'From this point, I use two object of a custom CObject derived class to manage the serialisation of the transmissions.'

 
0
 

Author Comment

by:Djof
ID: 8115782
Here is the outgoing serialisation of them. Remember this code works. The connection does not fail, the server still sends everything, I can still send, etc, etc. It's only the OnReceive() locking randomly.

================================================
From the inside of the serialization function, without all the TRACE() macros:

if (ar.IsStoring())
   {
      // Sending code
   }
   else
   {
      WORD w;
      DWORD dw;

      // Receive transaction header    
      ar >> w;
      m_nIsReply = ntohs(w);
      ar >> w;
      m_nId = ntohs(w);
      ar >> dw;
      m_nTaskId = ntohl(dw);
      ar >> dw;
      m_nIsError = ntohl(dw);
      ar >> dw;
      ar >> dw;
      m_nDataSize = ntohl(dw);
      ar >> w;
      m_nObjectNb = ntohs(w);

      // If the transaction is a reply, and Id is 0, check if we can get the original Id
      if (0 == m_nId && 1 == m_nIsReply)
         m_nId = m_pDoc->GetTaskId(m_nTaskId);

      // Object stuff
      int nObjId, nObjSize;
      int nNumber;
      CString sString;
      char* pBuff = NULL;
      ObjectUser usr;

      // Receive objets          
      for (int iObj = 1; iObj <= m_nObjectNb; iObj++)
      {
         ar >> w;
         nObjId = ntohs(w);
         ar >> w;
         nObjSize = ntohs(w);

         switch (ObjType(nObjId))
         {
         case 'N':
            if (2 >= nObjSize)
            {
               ar >> w;
               nNumber = ntohs(w);
            }
            else
            {
               ar >> dw;
               nNumber = ntohl(dw);
            }
            AddObj(nObjId, nNumber, true);
            break;
         case 'C':
            pBuff = new char[nObjSize + 1];   //Allocate
            ZeroMemory(pBuff, nObjSize + 1);     // Zero it
            ar.Read(pBuff, nObjSize);   // Receive it
            sString = pBuff;
            delete [] pBuff;   // Desallocate it
            pBuff = NULL;   // NULL the pointer
            AddObj(nObjId, sString, false, true);
            sString.Empty();
            break;
         case 'U':
            short nSocket, nIcon, nColor, nUserSize;
            ar >> w;
            nSocket = ntohs(w);
            ar >> w;
            nIcon = ntohs(w);
            ar >> w;
            nColor = ntohs(w);
            ar >> w;
            nUserSize = ntohs(w);
            pBuff = new char[nUserSize + 1];   // Allocate
            ZeroMemory(pBuff, nUserSize + 1);   // Zero it
            ar.Read(pBuff, nUserSize);   // Receive it
            sString = pBuff;
            delete [] pBuff;   // Desallocate it
            pBuff = NULL;   // NULL the pointer
                   
            m_pDoc->m_nSocket = nSocket;   // For self user
                   
            usr.Socket = nSocket;
            usr.Icon = nIcon;
            usr.Color = nColor;
            usr.User = sString;
            usr.IsUserList = true;

            m_pDoc->DisplayUsr(usr);
            sString.Empty();
            break;
         default:
            BYTE b;
            for (int i = 0; i < nObjSize; i++)
            {
               ar >> b;
               // Trace unsupported objects
            }
         }
      }
   }
}
0
 
LVL 12

Expert Comment

by:williamcampbell
ID: 8116175
           
  Nothing pops out unless in the default case nObjSize is something huge.

  In case U do you reuse that Socket handle?

  Since it worked in VC6 and you are now using VC7 is your suspicion starting to tend that way?

 
0
 

Author Comment

by:Djof
ID: 8116272
The socket is member of my document, if the connection dies, the document is closed (MDI).

And to be honest, I've got no idea where to put my suspitions.

Thanks to be still helping.
0
 
LVL 12

Expert Comment

by:williamcampbell
ID: 8120862

 Heres the MFC Code

case FD_READ:
{
DWORD nBytes;
if (!pSocket->IOCtl(FIONREAD, &nBytes))
       nErrorCode = WSAGetLastError();
if (nBytes != 0 || nErrorCode != 0)
       pSocket->OnReceive(nErrorCode);
}
break;

 So you will get no Receive if there are no bytes in the packet (I think you checked this and the server is sending)

 or

 The IOCtl call fails which could mean you NetCard is bogus or the driver has a bug!

 Have you tried a different Net card?

 

0
 

Author Comment

by:Djof
ID: 8121262
Upgraded my drivers, I'll see if it still hapens.
0
 

Author Comment

by:Djof
ID: 8121854
I had high hopes, but after an hour and a half, with 3 coonections, one got stuck. (See, it's totaly random) I'm going to have someone else test it though. I'm trying to get desperate about this.
0
 

Author Comment

by:Djof
ID: 8121868
Sorry, I meant "I'm getting desperate about this".
0
 
LVL 12

Expert Comment

by:williamcampbell
ID: 8122744

 We had a problem like this and it took months to solve. Eventually we changed the Net Card to solve the problem, it occured in high traffic situations a connection just closed for no reason.

 I suggest trying 2 or 3 cards (looks like you trying that)
0
 

Author Comment

by:Djof
ID: 8125158
I barely have another one, and it's really cheap. I will try though.
0
 
LVL 12

Expert Comment

by:williamcampbell
ID: 8129281


 As a test write a small program that just receives data over and over ... write a server side program that keeps pumping data. Even make it multi port. Let it run overnight
..if it doesn't fail then you will be closer to narrowing down the culprit.
0
 

Author Comment

by:Djof
ID: 8134471
Did that.

The server was sending a short, containing a number from 0 incremented to 255, then started over (each time calling OnReceive). It looped 65535 times without a problem. That's 16 776 960 call. I think it's a pretty good sample. ;)

That means the problem is with my client. ugh.
0
 
LVL 12

Expert Comment

by:williamcampbell
ID: 8137928


 Ok next remove all the serializing code.
0
 

Author Comment

by:Djof
ID: 8138132
I can only remove th reception code, because I need to login into the server. I'll try that now though.
0
 

Author Comment

by:Djof
ID: 8140646
Um, forgot that won't work either, the server won't send anything more unless I've received everything.

In sockcore.cpp there is this:

if ((nReady == 1) || (nErrorCode != 0))
     pSocket->OnReceive(nErrorCode);

I'll put a break point there next time I catch the problem.

Thanks to be still helping by the way...
0
 
LVL 12

Expert Comment

by:williamcampbell
ID: 8140684

 No Prob have a good weekend .. (drink heavily)
0
 

Author Comment

by:Djof
ID: 8141564
Mm, can't do that since the server won't send anything if I don't receive it, and it will close the connection after a while anyway.

Okay, the CAsyncSocket::DoCallBack function from which you paste copied a part earlier earlier isn't called at all, and thus, OnReceive isn't either. I'll try to go up the lather that way.
0
 

Author Comment

by:Djof
ID: 8143986
Funny how the server will continue to send data when OnReceive and DoCallBack aren't called, but not when I just don't accept to receive anything.
0
 
LVL 12

Expert Comment

by:williamcampbell
ID: 8153021

 When you stop receiving put a breakpoint in the function

 CSocket::PumpMessages

 and see where it leads.

 
0
 

Author Comment

by:Djof
ID: 8188368
Nothing goes there. :(
0
 

Author Comment

by:Djof
ID: 8213207
If the connection is closed remotly, OnClose gets called though.
0
 

Author Comment

by:Djof
ID: 8243572
Okay. How come CArchive::IsBufferEmpty() is NEVER not empty? Even if I check at the beggining of OnReceive.

The reception loop is based on the fact that IsBufferEmpty should be false if there is still something to receive. OnReceive shouldn't be called unless there is some NEW data in the buffer, so you have to empty the Archive first. But how do you know when there is still something if IsBufferEmpty is of no help?
0
 
LVL 12

Expert Comment

by:williamcampbell
ID: 8247601

 Have you tried CArchive::Flush?
0
 

Author Comment

by:Djof
ID: 8248246
Isn't Flush only for storing archives?

Here is my OnReceive.

void CXSocket::OnReceive(int nErrorCode)
{
     TRACE("IsBufferFull\t%d\n", !m_pDoc->m_pArchiveIn->IsBufferEmpty());
     
     BOOL b;

     do
     {
          b = m_pDoc->Get();
          TRACE("IsBufferFull\t%d\n", b);
     }
     while(b);    
}

The Get function returns !IsBufferEmpty(). Both the trace shows the IsBufferEmpty is always true, thus always empty, even if it should be false at least on the first trace. (Since it there is something in it I'm about to get.)

Then I'm pretty sure the locking happens because the !IsBufferEmpty() returned by the Get() doesn't make the loop restart, even when it should, because again, it doesn't tell there is something in the archive.

grr.
0
 
LVL 12

Expert Comment

by:williamcampbell
ID: 8250016


  For the sake of experimentation (and sanity) how about using a CFile instead of CArchive. You only have 5 items to store right?

  So change to a CFile and see if the problem goes away .. which we think is realted to CArchive.
0
 

Author Comment

by:Djof
ID: 8250171
I'm using the CArchives with a CSocketFile. How would I use a CFile to write to a socket?
0
 
LVL 12

Expert Comment

by:williamcampbell
ID: 8255462

  Use CAsyncSocket and CFile

  assuming you have 5 DWORD variables

  DWORD val[5];

  CAsyncSocket cs;
  cs.Connect ( ... )
  cs.Receive ( &val, sizeof DWORD * 5 );

       CFile cf;
     cf.Open ( "SocketData", CFile::modeCreate | CFile::modeWrite );

     //val[0] = 1;
     //val[1] = 2;
     //val[2] = 3;
     //val[3] = 4;
     //val[4] = 5;

     cf.Write ( val, sizeof DWORD *5 );
     cf.Close ();

     cf.Open ( "SocketData", CFile::modeRead );
     DWORD _val[5];
     cf.Read ( &_val, sizeof DWORD * 5 );
     cf.Close ();

        cs.Send ( &_val, sizeof DWORD * 5 );

 This might change depending on the data are you sending back and forth

 
0
 

Author Comment

by:Djof
ID: 8281923
I'd rather fix in archive implementation, as they tell everywhere it's the best thing. I've also used successfully direct Send and Receive on the socket before.

Now, as you suggested, you CAN use Flush on loading archives, however, as I understand it, it simply empties the buffer.

if (IsLoading())
{
     // unget the characters in the buffer, seek back unused amount
     if (m_lpBufMax != m_lpBufCur)
          m_pFile->Seek(-(int(m_lpBufMax - m_lpBufCur)), CFile::current);
     m_lpBufCur = m_lpBufMax;    // empty
}

Loading archives start buffering at m_lpBufMax, and the IsBufferEmpty function returns the bool of (m_lpBufCur == m_lpBufMax), so I'm having problem figuring why that doesn't work.
0
 

Author Comment

by:Djof
ID: 8352123
I've been using an external buffer to so I could trace it. This exposed that only strings are stored in the buffer. Why? Most probably because I read them with CArchive::Read(...). It makes sense that this is what causes the problem because I've been using this only since I switched to VC++ .net, and that's when I started having reception troubles.

There is nothing in the description of the function at MSDN that says there should be a problem using this function for network purpose but I'll change that and see if it fixes my problem.
0
 
LVL 12

Expert Comment

by:williamcampbell
ID: 8354757

  Would be nice to fix this one.
0
 

Author Comment

by:Djof
ID: 8575409
Wasn't that. Both MSDN CSocket sample programs (CHATTER and its server) also have the IsBufferEmpty() function always telling true.

I'm totaly lost and desperate.
0
 
LVL 12

Accepted Solution

by:
williamcampbell earned 300 total points
ID: 8592117

  Time to break down the code to its simplist form and start adding to it bit by bit until the problem occurs.
0
 

Author Comment

by:Djof
ID: 8592394
I'll work on that, I still have to use all my network code though, because I need to stick to the protocol.

On another side, after my friend did some testing with me, we found another interresting information about that bug, even if it doesn't help me figure where it happens in my code. The connection I use for testing is a 5Mbit/s cable, and his is a 56kbit modem connecting at around 35-40kbits due to poor phone line quality. We've found out that he is affected a lot more than me. Most of the time, he gets the bug within 10 minutes while it can take hours for it to happen to me. That would confirm that it has to do with the buffer, I think. The lag of his connection would make it more likely that multiple transmission get into the buffer as once, or something like that.
0
 

Author Comment

by:Djof
ID: 8658863
I finaly found out I wasn't alone with this problem.

"I am using the NDK under VC7 on an XP machine to do a client/server program. My problem is, that after awhile, my Client stops receiving messages from the server."

The NDK is a group of client-server classes based on CSocket/CArchive. Check the posts at the buttom of the page:
http://www.codetools.com/internet/ndk.asp?df=100&forumid=1156&select=516242&msg=516242#xx516242xx
0
 

Author Comment

by:Djof
ID: 8704186
Fixed.

Quote from Microsoft Knowledge Base Article - 185728
(http://support.microsoft.com/default.aspx?scid=kb;en-us;185728)
----------

In Windows Sockets, you should not make multiple recv calls within an FD_READ notification unless you are willing to disable FD_READ notifications prior to calling recv. However, CSocket and CAsyncSocket make no provision for doing so. Therefore, you should make only one Receive call per OnReceive function. Under high data transmission rate, if you make more than one Receive call in the OnReceive function, the application might lose FD_READ, have fake FD_READ, or have no FD_READ (hanging).

You can use CSocket with CArchive and CSocketFile to directly receive and send MFC CObject-derived objects. However, under high data transmission rates, you should not use CSocket with CArchive and CSocketFile within the OnReceive function because they might internally generate multiple Receive calls.
----------

So a simple fix is to disable FD_READ notifications during the span of the On Receive callback.

void CMySocket::OnReceive(int nErrorCode)
{
      // Remove read notification
      VERIFY(AsyncSelect(/*FD_READ | */FD_WRITE | FD_OOB | FD_ACCEPT | FD_CONNECT | FD_CLOSE));

      // ... Receive using CArchvive & CSocketFile

      // Set notifications to default
      VERIFY(AsyncSelect());
}

Note that if you disconnect and delete the socket during the your reception code, make sure you do not call AsyncSelect(), or you will assert.

williamcampbell, thanks for support. Just post another comment, and I will give you points for your help.
0
 

Author Comment

by:Djof
ID: 9533998
Looks like I've lost you, so I'll accept your last comment
0

Featured Post

Get MongoDB database support online, now!

At Percona’s web store you can order your MongoDB database support needs in minutes. No hassles, no fuss, just pick and click. Pay online with a credit card. Handle your MongoDB database support now!

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Introduction: Dialogs (1) modal - maintaining the database. Continuing from the ninth article about sudoku.   You might have heard of modal and modeless dialogs.  Here with this Sudoku application will we use one of each type: a modal dialog …
Exception Handling is in the core of any application that is able to dignify its name. In this article, I'll guide you through the process of writing a DRY (Don't Repeat Yourself) Exception Handling mechanism, using Aspect Oriented Programming.
This video will show you how to get GIT to work in Eclipse.   It will walk you through how to install the EGit plugin in eclipse and how to checkout an existing repository.
In this brief tutorial Pawel from AdRem Software explains how you can quickly find out which services are running on your network, or what are the IP addresses of servers responsible for each service. Software used is freeware NetCrunch Tools (https…

801 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question