Winsock callback, help?

Started by
2 comments, last by CruxMihiAncora 22 years, 5 months ago
I am trying to design a network solution for a game engine that uses Winsock and UDP. Unfortunately, I have not been able to find a way to give winsock a pseudo-callback function to make winsock more event-based. Is there a way to provide winsock a function to call when there is a network event that occurs? We are trying to avoid using the windows message handler if possible and use a callback function that is dedicated to the network component. Any help would be appreciated!!
Advertisement
What''s wrong with using Windows messages?
There''s not much wrong with using the main windows messages loop that I know of. I wanted to create a way to use a "local" callback function within the networking module so that it can be an object that can stand on its own and simply be "plugged in" into the game engine. Does this make sense? I know of a way that I could create a window class that has a local callback function and then create the window but never show it at all and use the HWND for that window to pass to WSAASyncSelect(sp?) in order to use as a callback function. I''m not sure if this will affect performance though and if it is viewed as a poor programming practice to get into?

I hope this made sense. I''m sorry, but I am not the most experienced C++ programmer in the real world so I am not sure how this can be "properly" programmed. Any more help would be much appreciated!!
Firstly, what kind of events are you trying to trap?

You should really take a look at asynchronous socket i/o using completion routines (this is different from i/o completion ports.) Completion routines are supported on Win95 right through to WinXP (including WinNT 3.51+.)

Basically you call one of the WSAxxx functions, passing it an overlapped I/O data block and an address of a completion routine (that you define in your code.)

The prototype for a completion routine is:

      void CALLBACK CompletionRoutine(    DWORD dwError,    DWORD cbTransferred,     LPWSAOVERLAPPED lpOverlapped,     DWORD dwFlags);    



Where CompetionRoutine can be any name you want.

Then you pass this function during a call to one of the WSAxxx functions like so:

                    WSASend(somesocket,   // socket must be created with WSA_FLAG_OVERLAPPED        1,        &wsabuf,        &bytessent,        0,        &overlapped,        CompletionRoutine);  // pass your completion routine here  


WSASend will then return with 0 or SOCKET_ERROR. If it returns with SOCKET_ERROR check the error code (WSAGetLastError()) and if its WSA_IO_PENDING then the call completed successfully.

The next step is to put your thread in what is known as an "alertable wait state." This is where you tell the Windows Kernel that your thread wants to go to sleep but should be woken up if some event occurs (completion routine, user APC etc).

To do this is simple:

SleepEx(sometimeout, TRUE); // timeout can even be 0 ms

Where TRUE is used to tell SleepEx to tell Windows that the thread should sleep in an alertable wait state.

Once your thread is in an alertable wait state, your completion routine can be called. So when WSASend actually finishes sending data, your completion routine will be called as soon as the thread that posted the I/O call is in an alertable wait state. This is important to note. If you call one of the WSAxxx functions on thread 1 and then put thread 2 in an alertable state, your completion routine will *not* be called. The thread that calls the async function is the one that receives the completion notification. So that calling thread must be set in an alertable state at some point.

-------------

Using Windows messaging is an OK solution as well. Since most games have a Windows messaging pump already, this solution is OK. On the other hand using windows messages for events is not very efficient.

One solution is to have your game running on one thread and another thread specifically set to processing network I/O completion routines. When an event is processed in the completion routine a network message gets added to some "network packet queue" of your own design. Then as the Thread 1 continues executing, handling mouse movements, keyboard, sound, rendering, it can pickup network events from the queue and process them. Basically Thread 2 looks like:

              void CALLBACK Thread2CompletionRoutine(DWORD dwError,DWORD cbTransferred, LPWSAOVERLAPPED lpOverlapped, DWORD dwFlags){   // do something with the data (like adding it to a queue)}DWORD WINAPI Thread2(PVOID parameter){    while(!g_GameShutdown)    {        // if no I/O completion occurs Thread2 won't wake up        SleepEx(INFINITE, TRUE);    }}      


The sneaky part of this method is getting Thread2 to post the asynchronous I/O calls. You typically would do this by queuing a UserAPC to Thread2 using the Win32 API function QueueUserAPC.

You can find more information about user APCs in the Win32 SDK. They are very simple to understand.

------------

So the choice is yours. Using windows messaging is OK, maybe not overly efficient, but it works very well. If you are writing a Win32 server, you should use Completion Routines. If you are writing a server for Windows NT, 2000, or XP then you should use I/O Completion Ports. That is another can of worms entirely

I hope this helped a bit.

Best regards,



Dire Wolf
www.digitalfiends.com

Edited by - Dire.Wolf on November 7, 2001 2:59:01 PM
[email=direwolf@digitalfiends.com]Dire Wolf[/email]
www.digitalfiends.com

This topic is closed to new replies.

Advertisement