?
Solved

CAsyncSocket and Multithreading

Posted on 2003-03-04
22
Medium Priority
?
1,047 Views
Last Modified: 2013-11-20
Hi guys!

I have a small problem. In my server application I accept the connecting clients when OnAccept is called from the main thread on my server socket. I then create the client socket and put it into a list where they are globally accessed by calling threads. Meaning that Send() on the socket might be called from any thread in the application. This doesn’t’ sound good but it seem to work until now.

I have started to experience problems, the send() function is sometimes returning WSAEWOULDBLOCK which never goes away on some client sockets. Might this have anything to do with me calling the Send() function from different threads?

What is the correct way to handle WSAEWOULDBLOCK? Is it to queue the data and wait for a OnSend() ?

Any special fall pits I should have in mind when working with multithreaded applications and CAsynSocket?
0
Comment
Question by:mathiasf
[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
  • 9
  • 7
  • 3
22 Comments
 
LVL 3

Accepted Solution

by:
cmaryus earned 600 total points
ID: 8069854
Basically the WSAEWOULDBLOCK error is issued oon a non-blocking socket. A socket can be in one of these 2 states:
- blocking - in this case when you execute any operation on a socket, the function you called (send, recv, connect, etc) will return ONLY when the operation was completed or you have an error
- non-blocking - in this case the functions will return imediatly after you call them, but in case the operation was not completed you will receive a WSAEWOULDBLOCK error. The best way to handle it is: when you receive WSAEWOULDBLOCK error put the socket in the blocking mode and send the data then switch again in the non-blocking mode.

Example:
// on your server side...
CAsyncSocket clientSocket; - this is the socket created on accept()

..
// put it in the blocking mode
DWORD arg = 0;
clientSocket.IOCtl(FIONBIO, &arg);

// put it in the non-blocking mode
DWORD arg = 1;
clientSocket.IOCtl(FIONBIO, &arg);

About socket in multithreading: i have an application with 2 threads that use the same socket, and i have no problems with that.You have to be carrefully though cause when you switch the socket in the blocking/non-blocking mode in one thread, in the other threads will be changed too.
0
 

Author Comment

by:mathiasf
ID: 8070316
The client socket (on the server side) goes in to an eternal loop. The WSAEWOULDBLOCK never goes away, below is the function that I use on the server side to send using the client socket.

Works well if the socket on the client side is created in the main windows thread and the data is being send using send() in the same thread. However if I do the connection on the client side, to the server, from another thread then the socket has no problem sending data to the server but the server side hangs in this SendAll() function:

int CClientSocket::SendAll(const void* lpBuf, int nBufLen, int nFlags)
{
     int cb               = 0;
     for (int cbWritten = 0; cbWritten < nBufLen; cbWritten += cb)
     {
          cb = Send((const BYTE*) lpBuf + cbWritten, nBufLen - cbWritten, nFlags);

          if (SOCKET_ERROR == cb)
          {
               if (GetLastError() == WSAEWOULDBLOCK)
               {
                    cb     = 0;
                    Sleep(10);
                    continue;
               }
               else
               {
                    return SOCKET_ERROR;
               }
          }
     }
0
 

Author Comment

by:mathiasf
ID: 8070321
What happens if Send is called at the same time from two diffirent threads?
0
VIDEO: THE CONCERTO CLOUD FOR HEALTHCARE

Modern healthcare requires a modern cloud. View this brief video to understand how the Concerto Cloud for Healthcare can help your organization.

 
LVL 3

Expert Comment

by:cmaryus
ID: 8070360
While you send data on a socket (or receive) the socket cannot perform another send/receive. You can see the socket as a bidirectional pipe, trough you can only send or receive data at a time.I don't have the problem with send because in my project a thread only sends data the other only receives. I suggest to lock the SendAll function, so it will be accesed by only one thread at a time.

int CClientSocket::SendAll(const void* lpBuf, int nBufLen, int nFlags)
{
     HANDLE hSyncro = CreateEvent(
                              NULL,     // no security
                              FALSE,     // automatic reset
                              TRUE,     // initial state
                              "Protect event");

     WaitForSingleObject(hSyncro, 100); // the function will be locket 100 msec, if you want to you can increase this timeout

     int cb               = 0;
     for (int cbWritten = 0; cbWritten < nBufLen; cbWritten += cb)
     {
           cb = Send((const BYTE*) lpBuf + cbWritten, nBufLen - cbWritten, nFlags);

           if (SOCKET_ERROR == cb)
           {
                 if (GetLastError() == WSAEWOULDBLOCK)
                 {
                       cb     = 0;
                       Sleep(10);
                       continue;
                 }
                 else
                 {
                       return SOCKET_ERROR;
                 }
           }
     }

     SetEvent(hSyncro);
}
0
 

Author Comment

by:mathiasf
ID: 8070397
Even though if that is the case the function should return eventualy, but it dosen't Send() is returning WSAEWOULDBLOCK for like 5 min untill I kill it...
0
 
LVL 3

Expert Comment

by:cmaryus
ID: 8070422
you have 2 options:
- either switch to blocking mode while sending
- when you get WSAEWOULDBLOCK DON'T set cb to 0 !!!
if (GetLastError() == WSAEWOULDBLOCK)
                {
                      //cb     = 0;
                      Sleep(10);
                      continue;
                }
0
 

Author Comment

by:mathiasf
ID: 8070543
Not setting cb to zero means that I'll skipp sending that chunk of data right?

Is there any way to find out why I get WSAEWOULDBLOCK? So I know who is using the socket or if it's blocked because of something else.
0
 
LVL 3

Expert Comment

by:cmaryus
ID: 8070568
second option -> my mistake ...cb = 0 is ok
WSAEWOULDBLOCK - like i said this means that the operation could not be completed imediatly

cb = Send((const BYTE*) lpBuf + cbWritten, nBufLen - cbWritten, nFlags);

          if (SOCKET_ERROR == cb)
          {
                if (GetLastError() == WSAEWOULDBLOCK)
                {
                      cb     = 0;
                      Sleep(10);
                       // switch to blocking mode
                      DWORD arg = 0;
                      IOCtl(FIONBIO, &arg);

                      continue;
                }
                else
                {
                      return SOCKET_ERROR;
                }
          }
          else
          {
                       // switch back to non-blocking mode
                      DWORD arg = 1;
                      IOCtl(FIONBIO, &arg);

          }
0
 

Author Comment

by:mathiasf
ID: 8070602
Already tried that IOCtl(FIONBIO, &arg); will return FALSE and it will just continue as before, for eternal
0
 
LVL 3

Expert Comment

by:cmaryus
ID: 8072011
Before IOCtl do:
AsyncSelect(0);
0
 

Author Comment

by:mathiasf
ID: 8072159
Should I set anything back? I want to at least know when the socket is closed. Why do I have to do this?
0
 
LVL 3

Expert Comment

by:cmaryus
ID: 8072224
Because the IOCtl will fail if any AsyncSelect were made before. That's way you disable AsyncSelect by calling:
AsyncSelect(0). You must do this only when you want to switch to blocking mode. If the socket is close on the client side you will receive an error, even if the socket is in the blocking mode.
0
 

Author Comment

by:mathiasf
ID: 8072652
When I try to do AsyncSelect(0) I get an ASSERT on line 337 in sockcore.cpp ( ASSERT(pState->m_hSocketWindow != NULL); ) any idea what that means?
0
 
LVL 3

Expert Comment

by:cmaryus
ID: 8078067
I have no ideea. did you forget to call AfxSocketInit() in the initialisation?
0
 
LVL 49

Assisted Solution

by:DanRollins
DanRollins earned 600 total points
ID: 8094995
You need to have one socket for each thread and need to call CAsyncSocetk::Create() in each thread.  And finally, you must not access common U/I elements, such as List Control from within worker threads (only from the main U/I thread).

>> get an ASSERT on line 337 in sockcore.cpp
A hidden window is created for handling socket events.  This ASSERT means that that window did not get created correctly.  The window is created in the AttachHandle function and if that failed, you should have seen a message in the debug output window:

   Warning: unable to create socket notify window!

Check it out.
-- Dan
0
 

Author Comment

by:mathiasf
ID: 8100646
Everything has been working ok until now when I have start to experience some problems. The application works, but in some rare cases it might hang like the problem described above. So accessing my CAsyncSocket objects between threads works ok but it might hang in some rare cases. Might the solution be to just put a CriticalSection in my SendAll function? Or is it a bad design to begin with?
0
 
LVL 49

Expert Comment

by:DanRollins
ID: 8100716
The alternative of using synchronous sockets -- each on its own thread -- is simple and straightforward.  I think you are just creating headaches for yourself trying to do otherwise.

-- Dan
0
 

Author Comment

by:mathiasf
ID: 8101083
Everything has been working ok until now when I have start to experience some problems. The application works, but in some rare cases it might hang like the problem described above. So accessing my CAsyncSocket objects between threads works ok but it might hang in some rare cases. Might the solution be to just put a CriticalSection in my SendAll function? Or is it a bad design to begin with?
0
 
LVL 49

Expert Comment

by:DanRollins
ID: 8106645
?  The refresh button causes a resend of the previous post.  Please try to avoid that.
0

Featured Post

On Demand Webinar: Networking for the Cloud Era

Did you know SD-WANs can improve network connectivity? Check out this webinar to learn how an SD-WAN simplified, one-click tool can help you migrate and manage data in the cloud.

Question has a verified solution.

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

Here is how to use MFC's automatic Radio Button handling in your dialog boxes and forms.  Beginner programmers usually start with a OnClick handler for each radio button and that's just not the right way to go.  MFC has a very cool system for handli…
If you use Adobe Reader X it is possible you can't open OLE PDF documents in the standard. The reason is the 'save box mode' in adobe reader X. Many people think the protected Mode of adobe reader x is only to stop the write access. But this fe…
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 video you will find out how to export Office 365 mailboxes using the built in eDiscovery tool. Bear in mind that although this method might be useful in some cases, using PST files as Office 365 backup is troublesome in a long run (more on t…
Suggested Courses

764 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