Migrating C/C++ Network code to C#
.NET is able to leverage the same Winsock API functions as
C/C++ code, and it is equally able to utilize the Winsock ActiveX control,
which may be used in some Visual C++ applications, however, it has it’s own
native socket handling mechanisms, which are much simpler to use and offers
better performance.
The native .NET socket programming models fall broadly into
two categories, asynchronous and synchronous. Both of these models, are
explained fully, and with ample source code in the book Network
Programming in .NET
(Buy at Amazon UK)
(Buy at Amazon US)
.
Again, it cannot be stressed enough, Do not use the
Winsock API in .NET, it will greatly overcomplicate your application, for no
benefit.
C/C++ Network code can fall into a large range of possible
models, it is important to recognize which model your application uses, so that
when migrating the application to C# (or VB.NET) you can tell which .NET socket
model most closely matches your design.
The main models are:
- Blocking
Model
- Select
Model
- Windows
Message Model
- Event
Model
- Overlapped
I/O Model
- Completion
port Model
Blocking Model
The blocking model in C/C++ is the simplest technique to
use, and is virtually identical in performance characteristics as the
Synchronous Model in .NET.
However it is not very scalable, and offers the lowest
performance of all models.
A blocking model server, after accepting a client would
follow these steps
- Create
a new Thread
- In
this thread:
- Call
Recv (blocking)
- Optionally
create a new thread to process the data
- Repeat
step 1 until data is exhausted.
Usually found in: Prototype code, non-production
applications.
For this model: Easy to understand, and use
Against this model: Every client consumes a thread, thus
there would be a significant performance hit due to thread switching.
Select model
This model has its roots in UNIX network code, and it has
been preserved to help interoperability between Windows C, and UNIX C code. It
does not offer very good performance, due to the required polling structure,
However, it is more similar to .NET’s asynchronous model than the previous
model.
A select model server, after accepting a client would follow
these steps
- Add
a socket to the read set using FD_SET
- Call
Select
- Loop
through all sockets in the set calling FD_ISSET
- If
FD_ISSET returns true, call Recv on the socket.
- Repeat
until data is exhausted
Usually found in: Code derived from legacy UNIX applications
For this model: Possible to multiplex connections and I/O on
many connections from one thread, up to a maximum of 1024 (not guaranteed)
Against this model: Substantial performance losses due to
polling and socket creation with large socket sets.
Windows Message Model
This model was one of the simpler models to use when
developers used to be expected to handle the windows message loop (winProc) for
their application. Nowadays, in modern languages such as C# or VB.NET, the
underlying windows forms architecture handles this for you. The cSocket object
in MFC actually uses this technique – Anyway, in this model, events are passed
back to the application as windows messages. Again, its closest relative is the
.NET asynchronous model.
A windows message model server, after accepting a client would
follow these steps
- Call
WSAStartup
- Bind
the socket
- Call
WSAAsyncSelect
In the message loop, where a WM_SOCKET message was detected:
- Call
WSAGETSELECTEVENT(lParam)
- If
this matches FD_READ then call Recv
Usually found in: Client network applications with a windows
GUI
For this model: Efficient detection of network events.
Against this model: Cannot Read & Write simultaneously
from the same thread.
Event Model
The event model is similar to the windows message model, but
it does not use the windows message loop, so therefore can be used in
applications without a user interface. It is closest to .NET’s asynchronous
model
An event model server, after accepting a client would follow
these steps
- Call
WSACreateEvent
- Binds
the socket
- Call
WSAEventSelect
- Call
WSAWaitForMultipleEvents
- For
each event –
- Call
WSAEnumNetworkEvnts
- Check
if _WSANETWORKEVENTS.lNetworkEvents masks FD_READ
- If
so, call Recv
Usually found in: Windows 9x network applications with no
user interface
Pros: No polling required, up to 64 sockets per thread.
Cons: Can’t read and write simultaneously from the same
thread
Cons: Uses user supplied buffers rather than system buffers,
meaning that data needs to be copied prior to processing.
Overlapped I/O Model
The overlapped I/O Model is new to Winsock 2.0, and is thus
is best suited to Windows 2000, NT 4, XP and Longhorn. It has higher
performance than the previous stated methods, but is also more complex. Note:
The ReadFile and WriteFile methods can be used to simulate Overlapped I/O with
Winsock 1.1 in Windows NT, but this should not be used for compatibility
reasons. It maps most closely to the asynchronous model in .NET.
An overlapped I/O model server, after accepting a client
would follow these steps:
- Call
WSACreateEvent
- Bind
socket
- Call
WSARecv (non blocking)
- Call
WSAWairForMultipleEvents (blocking)
- Call
WSAResetEvent
- Call
WSAGetOverlappedResult
- Process
data
- Repeat
from WSARecv
Usually found in: High performance clients on Windows NT /
2000
Pros: Can Read & Write simultaneously in the same thread
Pros: Uses internal system buffers for added performance
Cons: Limited to 64 events
I/O Completion port Model
The I/O completion port model offers the best performance,
and, in fact, this is what .NET uses ‘under the hood’, to provide it’s
asynchronous model
An I/O Completion port server, after accepting a client
would follow these steps:
- Bind
socket
- Call
WSARecv (non-blocking)
- Call
WSAWairForMultipleEvents (blocking)
In the call back function
- Process
data
- Call
WSARecv
Usually found in: High performance servers.
Pros: Can Read & Write simultaneously on the same socket
Pros: Uses internal system buffer for added performance
Pros: Callback is invoked from underlying system for instant
event notification
Cons: Limited to 64 events