Introduction
Well, first off let me say this is my first article that I am posting here so please
forgive any terrible errors that I make. I scanned over many of the articles in the networking
section of the Code Project and couldn't find any that were relevant
to using Winsock2 Network Events.
Unfortunately this came at a time when I was looking to implement some
functionality in my program that used them.
That being the case, I bit the bullet and cracked open
the books. (Along with faithful MSDN of course.)
My Assumptions
There are some assumptions that I am making about the readers of this article. They are:
- Familiar with MFC 4.2 and VC. (These are obviously a must)
- Understand the difference between synchronous and asynchronous
- A basic understanding of socket programming. Nothing extremely heavy is required but
you should already know how to set up a socket and about the different
states it can be in, etc...
Why Network Events?
When I first examined the problem I was faced with, I had three different options to use.
The first was to use regular socket calls under
Winsock 1.1.�� The next approach I came across, which I might add only lasted as
long as it took me to read it, was to use notification of network "events"
through windows messages. This poised a very big problem considering the program I was
integrating this code into had no message pump. This brings us to the
solution: Notification through WSA Events.
How do they Work?
The idea is that there are a limited number of things that most people do
on a certain socket. You send data, receive data, connect to another
socket, accept incoming connect requests and you close a socket. There
are perhaps a couple more but we will only focus on the major ones
for this article.� When one of these things occurs, an event you associated
with it will signal and you do what ever is necessary. The
network events that can be handled are as follows (again, these are only the
ones we are going to be discussing):
-
FD_ACCEPT
-
FD_READ
-
FD_WRITE
-
FD_CLOSE
-
FD_CONNECT
Lets set up a small sample to demonstrate.�The first thing you need
to do is initialize the winsock2 library. There is probably more than one way to do this
but here is how I go about it.
WSADATA wsd;
LPFN_WSASTARTUP lpf =
(LPFN_WSA_STARTUP)::GetProcAddress(
::LoadLibrary("WS2_32.DLL"), "WSAStartup");
lpf(0x0202, &wsd);
The initialization can be done anywhere, as far as I know, provided
that it is done before you use any calls to
winsock functions. The next important thing to do is to create the event
we want to use to determine when one of network
events takes place. To do this, use the Winsock2 API call
to ::WSACreateEvent(); After the event is created
it must be associated with the socket that the events will
occur on and which events it is to handle. That will be done
with WSAEventSelect(...). For the following we
are setting up an event to signal on the arrival of a request
to connect. This is usually done on a listening socket.�
For us, this socket is SOCKET m_listen .
It will look like this:�
WSAEVENT hEvent = WSA_INVALID_EVENT;
hEvent = WSACreateEvent();
::WSAEventSelect(m_listen, hEvent, FD_ACCEPT);
If the socket in question was our data transfer socket,
say for example, SOCKET m_socket, that it would more
likely
look like this:
WSAEVENT hDataEvent = WSA_INVALID_EVENT;
hDataEvent = WSACreateEvent();
::WSAEventSelect(m_socket, hDataEvent, FD_WRITE | FD_READ | FD_CLOSE);
It should be noted that it is not possible to use two
different event objects to listen for different network events
on the same socket. You cannot do the following: �
WSAEVENT hEvent1 = WSA_INVALID_EVENT;
WSAEVENT hEvent2 = WSA_INVALID_EVENT;
hEvent1 = WSACreateEvent();
hEvent2 = WSACreateEvent();
::WSAEventSelect(m_socket, hEvent1, FD_READ);
::WSAEventSelect(m_socket, hEvent2, FD_WRITE);
Handling the Event Notifications
Now that the events we are going to use are set up,
we need a way of waiting on and handling them. The events are actually just regular
Win32 events which makes them a HANDLE.
The function we can use to wait for these events to occur
is WSAWaitForMultipleEvents(...). This
function will just cause the thread it is in to sleep
until a network event that we are handling
occurs. An example of this would be:
WSAEVENT hEvent1 = WSACreateEvent();
WSAEVENT hEvent2 = WSACreateEvent();
::WSAEventSelect(m_listen, hEvent1, FD_ACCEPT);
::WSAEventSelect(m_data, hEvent2, FD_READ | FD_CLOSE);
WSAEVENT* pEvents = (WSAEVENT*)::calloc(2, WSAEVENT);
pEvents[0] = hEvent1;
pEvents[1] = hEvent2;
int nReturnCode = ::WSAWaitForMultipleEvents(2, pEvents,
FALSE, INFINITE, FALSE);
If waiting for only one event the same function can be used.
Just alter it to look something like:
int nReturnCode = ::WSAWaitForMultipleEvents(1, &hEvent1,
FALSE, INFINITE, FALSE);
The first parameter is the number of events that you want to wait on.
The second is a pointer to an array of the events you want to
wait on. The third event is a
BOOL value that determines whether or not the wait
function should continue to sleep until all events have signaled.
This usually will be
false but you may find some need to wait on all events.
The fourth parameter is how long you want to wait. Since I usually put
this functionality in another thread I leave it at infinite. If
you have this in your main thread, you may want to limit it to 5 seconds
or some other timeout that relates to your application. The fourth
parameter is whether or not you want it to be
alertable. Now once an event fires there should be something that handles each event.
The first thing that needs to be done is to figure out exactly which
event fired. For this we can use the function
::WSAEnumNetworkEvents(...).
One of the parameters for this function is a structure
called WSANETWORKEVENTS . Moving along with the code
we had above, we would proceed to do the following:
WSANETWORKEVENTS hConnectEvent;
WSANETWORKEVENTS hProcessEvent;
::WSAEnumNetworkEvents(m_listen, hConnectEvent, &wsaConnectEvents);
::WSAEnumNetworkEvents(m_data, hProcessEvent, &wsaProcessEvents);
After that has been completed you have the event that
fired on one of the sockets. Now we need to break it down
and handle it per event so that the correct action is taken
for the correct event. This is done as follows:
if(
(wsaConnectEvents.lNetworkEvents & FD_ACCEPT) &&
(wsaConnectEvents.iErrorCode[FD_ACCEPT_BIT] == 0) )
{
}
This manner of checking can be done for each WSAEVENT you
have set up and for each network event that the
WSAEVENT will signal for. All that you must do it to change the
FD_ACCEPT to whatever network event you are handling and
change the error check bit to the appropriate variable.
Conclusion
I hope this helps somebody writing network applications.
I have tried to compact a lot of information into a small article
so I know that it may not answer all questions. Feel free to email me
or post questions below and I will try to answer all that I can.
I would also like to apologize for not having some sort of demo app
to go along with this article but I am very busy right now and all
the source that I currently have that uses network events is owned by my company.
| You must Sign In to use this message board. |
|
|
 |
|
 |
you are going to have to be a bit more descriptive than that. What error did you get back. What happened? How did you write your source ?
Joseph Dempsey jdempsey@cox.rr.com Joseph.Dempsey@thermobio.com "Software Engineering is a race between the programmers, trying to make bigger and better fool-proof software, and the universe trying to make bigger fools. So far the Universe in winning." --anonymous
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
I Write the test program, first I test it in debug mode so I can do step by step. It works find, all events a captured. But when I run test program without debug, connect and close the socket, the server side only have accept event not the close event.
Can you help me?
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
Any samples how to use WSAEnumProtocols() to gain that "protocol independance" thing?
I have this socket library I want to convert from 1.1 to 2.2, but I'm not sure how to go about it.
davygrvy@pobox.com
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
If the hEvents produced by WSACreateEvent are usable by system functions like WaitForMultipleObjects then this is a *very* nice approach for client apps that use a worker thread (or threads). The point of using a worker thread is to simplify code. The problem with it is that there is all sorts of complexity added around controlling the thread (particularly if it's blocked). One way around the control-a-blocked-thread problem is to use WaitForMultipleObjects to wait for events that include ones that direct the thread as well as the one the thread is actually blocking on. I use this approach a lot with producer/consumer threads in drivers where I can't have an unresponsive thread but still need to wait on events. I guess I'll write some test code to see if the events actually are interchangable - I'd be curious to hear if I'm wasting my time before I do, though...
|
| Sign In·View Thread·PermaLink | 3.00/5 |
|
|
|
 |
|
 |
they do. no real need to right test code beside to satisfy your own curiosity. I do this quite often. In fact if you tread into the SDK files you will see a simple typedef done on the EVENT handle. It is nothing more than a HANDLE.
Joseph Dempsey jdempsey@cox.rr.com Joseph.Dempsey@thermobio.com "Software Engineering is a race between the programmers, trying to make bigger and better fool-proof software, and the universe trying to make bigger fools. So far the Universe in winning." --anonymous
|
| Sign In·View Thread·PermaLink | 2.00/5 |
|
|
|
 |
 | bug  rhanda | 1:42 18 Oct '01 |
|
 |
Handling the Event Notifications:
WSAEVENT* pEvents = (WSAEVENT*)::calloc(2, WSAEVENT);
you have to use WSAEVENT* pEvents = (WSAEVENT*)::calloc(2, sizeof(WSAEVENT)); instead
|
| Sign In·View Thread·PermaLink | 1.00/5 |
|
|
|
 |
|
 |
Why do aren't you using IOCompletionPorts. Microsoft states that this is the only way to create true scalable network applications!
|
| Sign In·View Thread·PermaLink | 2.00/5 |
|
|
|
 |
|
 |
The simple reason is that I did this while on a strict timeline to get our product to market.... Had to do something that I already knew enough about that it wouldn't impact my development time by too much.
Joseph Dempsey jdempsey@cox.rr.com Joseph.Dempsey@thermobio.com "Software Engineering is a race between the programmers, trying to make bigger and better fool-proof software, and the universe trying to make bigger fools. So far the Universe in winning." --anonymous
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
Could you please give me an url of a good article about IOCompletionPorts ? Thank you very much best regards Guy LECOMTE
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
Guy http://www.codeproject.com/internet/winsockiocp.asp If you have any queries let me know and I'll try and answer them. Also I'm currently updating the article which I will post when I get free time.
Regards
Norm
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
When you want to do IO you have 3 choices:
threads -- limited, lame, and not good (1 thread per conn) (people often think this is the way to go)
select -- limited, but fine for few connections
IOCP -- the only way to write a server to handle 100s or 1000s of connections.
The select() model is good for client applications, where you are dealing with a small and limited set of incoming TCP connections (64 or less). Select offers a range of variations, but all of the variations have a strict 64 handle limit. If you want to do more, then you need to juggle. The importance of select() is a way to "put aside" your socket handles, and check which have traffic on them. But 64 is quite a limit when you are writing a server.
IOCP on the other hand has some very good qualities to it. Namely, you can easily juggle many 100s of connections -- and sometimes do it with limited data structure support. [create handler block of memory, associate it with a handle -- you call a "event" method which dequeues and event with your memory block associated with it]. Anyhow IOCP is quite nice in that way. BUT A WORD OF WARNING HERE -- IOCP is not perfect -- there is also limited support from MS, and very little information on how to do it -- but if you figure it out, it can save your life. And you get, in the end, a high performance server.
My advice: * read a lot -- there is excellent info on this site. * read some more * and read even more. * prepare for frustration
In the end, the performance of IOCP is worth the anguish.
|
| Sign In·View Thread·PermaLink | 5.00/5 |
|
|
|
 |
|
 |
Peter Weyzen wrote: The select() model is good for client applications, where you are dealing with a small and limited set of incoming TCP connections (64 or less). Select offers a range of variations, but all of the variations have a strict 64 handle limit
The variable FD_SETSIZE determines the maximum number of connections. The default value of FD_SETSIZE is 64, which can be modified by defining FD_SETSIZE to another value before including Winsock2.h
I vote pro drink 
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
Note that Io Completion Ports are also not very portable. It will only work on NT 4 and higher, and not on Windows 95, 98 and ME.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
though -- the other side is "why write a high-performance server to run on Windows98?" I wouldn't consider writing a IOCP server on a machine that's not fit to run as a server. So, it is really portable to machines that are capable of being a workstation/server.
|
| Sign In·View Thread·PermaLink | 2.00/5 |
|
|
|
 |
|
|
 |
|
 |
I will hopefully get a sample project up sometime in the near future but as of right now my schedule is to busy to grant me time to sit down and build one. The project I had that used this topic is owned by my company and hence I can not share. Look for a sample sometime down the road. In the meantime feel free to ask any questions you might have.
Joseph Dempsey jdempsey@cox.rr.com Joseph.Dempsey@thermobio.com "Software Engineering is a race between the programmers, trying to make bigger and better fool-proof software, and the universe trying to make bigger fools. So far the Universe in winning." --anonymous
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
 | Typos  Derek Lakin | 22:48 21 May '01 |
|
 |
Two of your examples for using WSAEventSelect have the socket and event handle parameters the wrong way round. They should be: WSAEVENT hEvent1 = WSA_INVALID_EVENT; WSAEVENT hEvent2 = WSA_INVALID_EVENT;
hEvent1 = WSACreateEvent(); hEvent2 = WSACreateEvent();
::WSAEventSelect(m_socket, hEvent1, FD_READ); ::WSAEventSelect(m_socket, hEvent2, FD_WRITE);
and Assume m_listen and m_data are both vaild sockets: WSAEVENT hEvent1 = WSACreateEvent(); WSAEVENT hEvent2 = WSACreateEvent();
::WSAEventSelect(m_listen, hEvent1, FD_ACCEPT); ::WSAEventSelect(m_data, hEvent2, FD_READ | FD_CLOSE);
WSAEVENT* pEvents = (WSAEVENT*)::calloc(2, WSAEVENT); pEvents[0] = hEvent1; pEvents[1] = hEvent2;
int nReturnCode = ::WSAWaitForMultipleEvents(2, pEvents, FALSE, INFINITE, FALSE);
A useful article nonetheless.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
Thanx, I have fixed them....
Joseph Dempsey jdempsey@cox.rr.com Joseph.Dempsey@thermobio.com "Software Engineering is a race between the programmers, trying to make bigger and better fool-proof software, and the universe trying to make bigger fools. So far the Universe in winning." --anonymous
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
Hi Joseph ,
I am no Advanced Programmer, but I saw this at first glance...I think you wanted to have:
WSAEVENT hEvent = WSA_INVALID_EVENT; hEvent = WSACreateEvent(); ::WSAEventSelect(m_listen, hEvent, FD_ACCEPT);
instead of:
WSAEVENT hEvent = WSA_INVALID_EVENT; hEvent = WSACreateEvent(); ::WSAEventSelect(m_listen, m_hEvent, FD_ACCEPT);
Correct? Or am I really showing everyone that I am a beginner?
Thanks in advance ,
Dan
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
 |
|
|