• Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 411
  • Last Modified:

Win32 Socket,MultiThreading and Event - how to use call back?

I have a Win32 socket application- multithreaded using synchronization object event. Socket has its own class and Thread has its own class. From MFC Dialog onBnClickedStart(), I am creating server socket. he starts, binds and listens and a loop for receive(), that is where i am supposed to create a new thread object and do receive() there and send() etc. I am having problem using thread::callback and pass clientsocket object. Here it is so far -
 
//CServerDlg
void CCServerDlg::OnBnClickedStart()
{
   // TODO: Add your control notification handler code here
   // TODO: Add your specialized code here and/or call the base class
   CPacket pack( PACKFILENAME );
      pack.loadPacket();
      char* ipServerAddress = pack.getServerIPAddress();
   int port = pack.getPortNum();
   
   SetDlgItemText( IDC_SERVERADDRESS, ipServerAddress );

   CSocket sock;
   int ret=sock.initSock( ipServerAddress, port );
   if( ret ){
      ret = sock.listenSock();
      if( ret){
       
         //Accept loop, accept any number of client connection
         while(1){
             
            //?????????????????????????????????????????????????????????
              SOCKET clientSocket = sock.acceptSock();
             CThread myThread;
             
         }

      }
   }
}

//Thread class
#include "stdafx.h"
#include "thread.h"

static int threadCount=0;

CThread::CThread()
{
      threadID=0;
      threadHandle = NULL;
   bThreadRunning = 0;
}


int CThread::createThread()
{
      if( bThreadRunning) return 0;

//????????????????????????????????????
//Not sure I should pass threadEvent here, should pass CLIENT SOCKET * i  think, i can create the event object inside the call back - for derived class function code()

   threadHandle = CreateThread( NULL, 0, (LPTHREAD_START_ROUTINE)CThread::threadProc, &threadEvent,0, &threadID );
   if( threadHandle == NULL) return 0;
   bThreadRunning = true;
   return 1;
}

void CThread::closeThread()
{
   bThreadRunning=false;
}

int CThread::createEvent( char* eventName)
{
   threadEvent = CreateEvent( NULL,
                              TRUE,//Manual
                              TRUE,//Signalled initially
                              eventName
                              );

   if( threadEvent == NULL) return 0;
   return 1;
}

void CThread::setEvent( HANDLE event )
{
   SetEvent( event );
}

void CThread::resetEvent( HANDLE event )
{
   ResetEvent( event );
}

void CThread::threadProc( void* ptr )
{
   //Call the acutal thread code
   ((CThread*)ptr)->code();

   //After code() returns, kill the thread object
   delete (CThread*)ptr;

}

SOCKET CSocket::acceptSock()
{
   SOCKET acceptSocket = accept( listenSocket, NULL,NULL); //(SOCKADDR*)&service, size );
   if( acceptSocket == INVALID_SOCKET ){
      closesocket( listenSocket );
      cleanupSock();
      return 0;
   }
   return acceptSocket;
}
0
openujs
Asked:
openujs
  • 4
  • 3
1 Solution
 
alexcohnCommented:
You should not run while(1) on the UI thread - the thread where MFC OnBtnClicked() is called. This casues your application stop listening to UI events.

Apart from this, there is no indication how you are going to use the event.
0
 
openujsAuthor Commented:
Ok, I changed the code little bit as follows  -

//Made CSocket derived from CThread class.
//Converted to CSocket *, i was getting 'Pure virtual ' runtime error without CSocket* pSocket, NOt sure why? Both with pointer or stack declaration calls thread::code()
 CSocket * pSock= new CSocket;
   int ret=pSock->initSock( ipServerAddress, port );
   if( ret ){
      ret = pSock->listenSock();
      if( ret){
       
         pSocket->CreateThread();
             
        }

So when thread is created and sock::Code() which accepts and recv(), send() inside a loop, that part is working. but i need another loop for accept(), for multiple client connection, where should i use that loop, if i cant use inside OnBnClickedStart(), onIdle() or something?
0
 
openujsAuthor Commented:
Also, How about this ?  inside OnBnClickedStart(),  keep the while(1) loop for accept multiple clients, but add following code to process window messages, would that work?

 if (::PeekMessage(&message, NULL, 0, 0, PM_REMOVE)) {
// periodically (every 10000 cycles) check for messages
// in particular the Timer message
// Notice this is the message pump  we say earlier in WinMain loop!
            ::TranslateMessage(&message);
            ::DispatchMessage(&message);
        }
0
Independent Software Vendors: We Want Your Opinion

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

 
alexcohnCommented:
Common sense dictates to create a worker thread in OnBnClickedStart(), and perform accept() in that thread proc. Note that you must anyway decide what you do if the Bn is clicked twice: you could ignore the second click, or stop your accept() thread, or anything else... What you should not do, is run a second while(1) loop, and you also should not create a second worker thread.

The idea of PeekMessage() from inside OnBnClickedStart() is not a good idea, even if it may seem to work in some cases. Let me provide just one example why it is problematic: if some UI action happens to take longer than expected, the accept() will wait.
0
 
openujsAuthor Commented:
Ok, I am disabling START_Button as socket is successfully initiated. Enabling with OnClickStopButton(),

Also, took out PeekMessage() , it made my CPU level go up.

Ok, inside ThreadFunc which actually calls virtual Code(), I have -

void CSocket::code()
{
   //0 : it only checks the status of the eventHandle
   while( ::WaitForSingleObject( this->threadEvent, 0) == WAIT_TIMEOUT ){
   
      acceptSocket = acceptSock();

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//Need to create another thread instead of receiveSock() here,

CreateThread();

INSTEAD OF - /*
      char recBuffer[512];
      DWORD ret = receiveSock( acceptSocket, recBuffer );
      recBuffer[ret] = '\0';

      */

      if( ::WaitForSingleObject( this->threadEvent, 0 ) == WAIT_OBJECT_0 ){
         break;
      }
   }

Now my question is - Every thread dispatched to handle Recv() and send() for each client, shares the CSocket class object. Also, shares ThreadEvent and response to that when from Dialog (StopButton), setEvent( ThreadEvent) is launched.

Because - my Create thread is -

int CThread::createThread()
{
      if( bThreadRunning) return 0;
   //threadHandle = CreateThread( NULL, 0, (LPTHREAD_START_ROUTINE)CThread::threadProc, &threadEvent,0, &threadID );
   threadHandle= (HANDLE)_beginthread( CThread::threadProc, 0, this );
   if( threadHandle == NULL) return 0;
   bThreadRunning = true;
   createEvent("MyEvent");
   threadCount++;
   return 1;
}

void CThread::threadProc( void* ptr )
{
   //Call the acutal thread code
   ((CThread*)ptr)->code();

   //After code() returns, kill the thread object
   delete (CThread*)ptr;

}

If that is the case, I need to disable if( bThreadRunning) return 0, since one instance of CThread::bThreadRunning will be shared by all the threads. or shall I create new copy of CSocket at each new Thread creation??????
remember class CSocket: public CThread

0
 
alexcohnCommented:
I think that if CSocket is a thread, it should not be shared; each one will handle one client.
0
 
openujsAuthor Commented:
I thought I accepted your answer before.sorry for the delay.
0

Featured Post

Get free NFR key for Veeam Availability Suite 9.5

Veeam is happy to provide a free NFR license (1 year, 2 sockets) to all certified IT Pros. The license allows for the non-production use of Veeam Availability Suite v9.5 in your home lab, without any feature limitations. It works for both VMware and Hyper-V environments

  • 4
  • 3
Tackle projects and never again get stuck behind a technical roadblock.
Join Now