Library tutorials & articles
Socket Programming in C# - Part 2
- Introduction
- Getting Started
- Multiple Sockets
- Server Side
- Conclusion
Getting Started
You can argue that one can overcome these shortcomings by multithreading meaning that one can spawn a new thread and let that thread do the polling which then notifies the main thread of the data. This concept could work well, but even if you create a new thread it would require your main thread to share the CPU time with this new thread. Windows operating system (Windows NT /2000 /XP) provide what is called Completion Port IO model for doing overlapped ( asynchronous) IO.
The details of IO Completion port are beyond the scope of the current discussion, but to make it simple you can think of IO Completion Ports as the most efficient mechanism for doing asynchronous IO in Windows that is provided by the Operating system. Completion Port model can be applied to any kind of IO including the file read /write and serial communication.
The .NET asynchronous socket programming helper class's Socket provides the similar model.
BeginReceive
.NET framework's Socket class provides BeginReceive method to receive data asynchronously i.e., in an non-blocking manner The BeginReceive method has following signature:
public IAsyncResult BeginReceive( byte[] buffer, int offset, int size, SocketFlags
socketFlags, AsyncCallback callback, object state );
The way BeginReceive function works is that you pass the function a buffer
, a callback function (delegate) which will be called whenever data arrives.
The last parameter, object, to the BeginReceive can be any class derived from
object ( even null ) .
When the callback function is called it means that the BeginReceive function
completed which means that the data has arrived.
The callback function needs to have the following signature:
void AsyncCallback( IAsyncResult ar);
As you can see the callback returns void and is passed in one parameter ,
IAsyncResult interface , which contains the status of the asynchronous receive
operation.
The IAsyncResult interface has several properties. The first parameter - AsyncState - is an object which is same as the last parameter that you passed to BeginReceive().
The second property is AsyncWaitHandle which we will discuss in a moment. The
third property indicates whether the receive was really asynchronous or it
finished synchronously. The important thing to follow here is that it not necessary
for an asynchronous function to always finish asynchronously - it can complete
immediately if the data is already present. Next parameter is IsComplete which
indicates whether the operation has completed or not.
If you look at the signature of the BeginReceive again you will note that
the function also returns IAsyncResult. This is interesting. Just now I said
that I will talk about the second peoperty of the IAsyncResult in a moment.
Now is that moment. The second parameter is called AsyncWaitHandle.
The AsyncWaitHandle is of type WaitHandle, a class defined in the System.Threading namespace. WaitHandle class encapsulates a Handle (which is a pointer to int
or handle ) and provides a way to wait for that handle to become signaled.
The class has several static methods like WaitOne ( which is similar to WaitForSingleObject ) WaitAll ( similar to WaitForMultipleObjects with waitAll true ) , WaitAny etc. Also there are overloads of these functions available with timeouts.
Coming back to our discussion of IAsyncResult interface, the handle in AsyncWaitHandle (WaitHandle) is signalled when the receive operation completes. So if we wait
on that handle infinitely we will be able to know when the receive completed.
This means if we pass that WaitHandle to a different thread, the different
thread can wait on that handle and can notify us of the fact that the data
has arrived and so that we can read the data. So you must be wondering if we
use this mechanism why would we use callback function. We won't. Thats right.
If we choose to use this mechanism of the WaitHandle then the callback function
parameter to the BeginReceive can be null as shown here:
//m_asynResult is declared of type IAsyncResult and assumming that m_socClient
has made a connection.
m_asynResult = m_socClient.BeginReceive(m_DataBuffer,0,m_DataBuffer.Length,SocketFlags.None,null,null);
if ( m_asynResult.AsyncWaitHandle.WaitOne () )
{
int iRx = 0 ;
iRx = m_socClient.EndReceive (m_asynResult);
char[] chars = new char[iRx + 1];
System.Text.Decoder d = System.Text.Encoding.UTF8.GetDecoder();
int charLen = d.GetChars(m_DataBuffer, 0, iRx, chars, 0);
System.String szData = new System.String(chars);
txtDataRx.Text = txtDataRx.Text + szData;
}
Even though this mechanism will work fine using multiple threads, we will for now stick to our callback mechanism where the system notifies us of the completion of asynchronous operation which is Receive in this case .
Lets say we made the call to BeginReceive and after some time
the data arrived and our callback function got called.Now question is where's
the data? The
data is now available in the buffer that you passed as the first parameter
while making call to BeginReceive() method . In the following example the
data will be available in m_DataBuffer :
BeginReceive(m_DataBuffer,0,m_DataBuffer.Length,SocketFlags.None,pfnCallBack,null);
But before you access the buffer you need to call EndReceive() function on the socket. The EndReceive will return the number of bytes received . Its not legal to access the buffer before calling EndReceive. To put it all together look at the following simple code:
byte[] m_DataBuffer = new byte [10];
IAsyncResult m_asynResult;
public AsyncCallback pfnCallBack ;
public Socket m_socClient;
// create the socket...
public void OnConnect()
{
m_socClient = new Socket (AddressFamily.InterNetwork,SocketType.Stream
,ProtocolType.Tcp );
// get the remote IP address...
IPAddress ip = IPAddress.Parse ("10.10.120.122");
int iPortNo = 8221;
//create the end point
IPEndPoint ipEnd = new IPEndPoint (ip.Address,iPortNo);
//connect to the remote host...
m_socClient.Connect ( ipEnd );
//watch for data ( asynchronously )...
WaitForData();
}
public void WaitForData()
{
if ( pfnCallBack == null )
pfnCallBack = new AsyncCallback (OnDataReceived);
// now start to listen for any data...
m_asynResult =
m_socClient.BeginReceive (m_DataBuffer,0,m_DataBuffer.Length,SocketFlags.None,pfnCallBack,null);
}
public void OnDataReceived(IAsyncResult asyn)
{
//end receive...
int iRx = 0 ;
iRx = m_socClient.EndReceive (asyn);
char[] chars = new char[iRx + 1];
System.Text.Decoder d = System.Text.Encoding.UTF8.GetDecoder();
int charLen = d.GetChars(m_DataBuffer, 0, iRx, chars, 0);
System.String szData = new System.String(chars);
WaitForData();
}
The OnConnect function makes a connection to the server and then makes a call
to WaitForData. WaitForData creates the callback function and makes a call
to BeginReceive passing a global buffer and the callback function. When data
arrives the OnDataReceive is called and the m_socClient's EndReceive is called
which returns the number of bytes received and then the data is copied over
to a string and a new call is made to WaitForData which will call BeginReceive again and so on. This works fine if you have one socket in you application.
Related articles
Related discussion
-
Concurrency violation: the UpdateCommand affected 0 of the expected 1 records
by virtualking (0 replies)
-
How to optimize mysql subquery performance?
by Jayaram P (0 replies)
-
C# video Editing/rendering
by pkuchaliya (0 replies)
-
How to Fill DataSet with more records (around 1 lakh) in a faster way
by Jayaram P (0 replies)
-
Can't print on the network with MSADESS ??
by anatha1 (2 replies)
Related podcasts
-
Object-Oriented Programming in Ruby
In this episode, I talk with Scott Bellware about object-oriented programming in Ruby, and Ruby's object model. This is taken from a private conversation, and the audio quality suffers at times. Much thanks to Scott for allowing this to be released.This episode of the Alt.NET Podcast is bro...
I'll also post the whole code for the Client and Server classes. It may be useful for someone. I spend several hours researching and testing to come up with this...
I know we are in a C# area, but the code is very easy to translate.
Private Sub ReceiveCallback(ByVal ar As IAsyncResult)
Try
'Retrieve the state object and the client socket
'from the asynchronous state object.
Dim state As StateObject = CType(ar.AsyncState, StateObject)
Dim client As Socket = state.workSocket
'Read data from the remote device.
Dim bytesRead As Integer = client.EndReceive(ar)
If bytesRead > 0 Then
'There might be more data, so store the data received so far.
state.sb.Append(Encoding.ASCII.GetString(state.buffer, 0, bytesRead))
'Get the rest of the data.
client.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0, New AsyncCallback(AddressOf ReceiveCallback), state)
'Check if there is no more data to be received. If not an OK message is sent to the client.
If Not client.Poll(1000000, SelectMode.SelectRead) Then
Send(client, "OK" & vbCrLf)
End If
Else
'All the data has arrived;
'Signal that all bytes have been received.
receiveDone.Set()
InvokeDelegate(state.sb.ToString)
End If
Catch ex As Exception
Console.WriteLine(ex.Message)
End Try
End Sub
###See the complete solution below:
_____________________________________________________________________________
Public Delegate Sub StringReceivedHandlerDelegate(ByVal sRemoteAddress As String)
Public Class Server
Private _PortNumber As Integer
Private DataReceived As StringReceivedHandlerDelegate
Private listener As Socket
Sub New(ByVal PortNumber As Integer)
_PortNumber = PortNumber
End Sub
Public Sub StartServer()
Listen()
End Sub
Public Sub StopServer()
If Not listener Is Nothing Then
listener.Close()
End If
End Sub
Public Class StateObject
'Client socket.
Public workSocket As Socket = Nothing
'Size of receive buffer.
Public Const BufferSize As Integer = 8192
'Receive buffer.
Public buffer() As Byte = New Byte(BufferSize - 1) {}
'Received data string.
Public sb As New StringBuilder
End Class
'ManualResetEvent instances signal completion.
Private Shared connectDone As New ManualResetEvent(False)
Private Shared sendDone As New ManualResetEvent(False)
Private Shared receiveDone As New ManualResetEvent(False)
Private Sub Listen()
Try
Dim remoteEP As New IPEndPoint(IPAddress.Any, _PortNumber)
listener = New Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)
listener.Bind(remoteEP)
listener.Listen(10)
listener.BeginAccept(New AsyncCallback(AddressOf ConnectCallback), listener)
Catch ex As Exception
Console.WriteLine(ex.Message)
End Try
End Sub
Private Sub ConnectCallback(ByVal ar As IAsyncResult)
Try
'Retrieve the socket from the state object.
Dim client As Socket = CType(ar.AsyncState, Socket)
'Complete the connection.
client = client.EndAccept(ar)
Console.WriteLine("Socket connected to {0}", client.RemoteEndPoint.ToString())
'Start Receiving
Receive(client)
'Signal that the connection has been made.
connectDone.Set()
listener.BeginAccept(New AsyncCallback(AddressOf ConnectCallback), listener)
Catch ex As Exception
Console.WriteLine(ex.Message)
End Try
End Sub
Private Sub Receive(ByVal client As Socket)
Try
'Create the state object.
Dim state As New StateObject
state.workSocket = client
'Begin receiving the data from the remote device.
client.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0, New AsyncCallback(AddressOf ReceiveCallback), state)
Catch ex As Exception
Console.WriteLine(ex.Message)
End Try
End Sub
Private Sub ReceiveCallback(ByVal ar As IAsyncResult)
Try
'Retrieve the state object and the client socket
'from the asynchronous state object.
Dim state As StateObject = CType(ar.AsyncState, StateObject)
Dim client As Socket = state.workSocket
'Read data from the remote device.
Dim bytesRead As Integer = client.EndReceive(ar)
If bytesRead > 0 Then
'There might be more data, so store the data received so far.
state.sb.Append(Encoding.ASCII.GetString(state.buffer, 0, bytesRead))
'Get the rest of the data.
client.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0, New AsyncCallback(AddressOf ReceiveCallback), state)
'Check if there is no more data to be received an OK message is sent to the client
If Not client.Poll(1000000, SelectMode.SelectRead) Then
Send(client, "OK" & vbCrLf)
End If
Else
'All the data has arrived;
'Signal that all bytes have been received.
receiveDone.Set()
InvokeDelegate(state.sb.ToString)
End If
Catch ex As Exception
Console.WriteLine(ex.Message)
End Try
End Sub
Private Sub Send(ByVal client As Socket, ByVal data As String)
Try
'Convert the string data to byte data using ASCII encoding.
Dim byteData As Byte() = Encoding.ASCII.GetBytes(data)
'Begin sending the data to the remote device.
client.BeginSend(byteData, 0, byteData.Length, 0, New AsyncCallback(AddressOf SendCallback), client)
Catch ex As Exception
Console.WriteLine(ex.Message)
End Try
End Sub
Private Sub SendCallback(ByVal ar As IAsyncResult)
Try
'Retrieve the socket from the state object.
Dim client As Socket = CType(ar.AsyncState, Socket)
'Complete sending the data to the remote device.
Dim bytesSent As Integer = client.EndSend(ar)
Console.WriteLine("Sent {0} bytes to server.", bytesSent)
'Signal that all bytes have been sent.
sendDone.Set()
Catch ex As Exception
Console.WriteLine(ex.Message)
End Try
End Sub
Public Sub SetStringInputHandler(ByVal pMethod As StringReceivedHandlerDelegate)
Try
Monitor.Enter(Me)
If DataReceived Is Nothing Then
DataReceived = pMethod
End If
Monitor.Exit(Me)
Catch ex As Exception
Console.WriteLine(ex.Message)
End Try
End Sub
Private Sub InvokeDelegate(ByVal sData As String)
Try
DataReceived.Invoke(sData)
Catch ex As Exception
Console.WriteLine(ex.Message)
End Try
End Sub
End Class
____________________________________________________________________________
Public Class Client
Private _RemoteHost As String
Private _RemotePort As Integer
Private _NetworkStream As NetworkStream
Dim _TCPClient As TcpClient
Sub New(ByVal RemoteHost As String, ByVal RemotePort As Integer)
_RemoteHost = RemoteHost
_RemotePort = RemotePort
End Sub
Public Function SendStringMessage(ByVal Message As String) As String
Try
_TCPClient = New TcpClient(_RemoteHost, _RemotePort)
_NetworkStream = _TCPClient.GetStream()
'_NetworkStream.WriteTimeout = 10000
'_NetworkStream.ReadTimeout = 10000
Dim strResponse As String
' Send a string (newline terminated) to the server.
Dim writer As New System.IO.StreamWriter(_NetworkStream)
Dim reader As New System.IO.StreamReader(_NetworkStream)
writer.Write(Message)
writer.Flush()
' Read server response (up to a newline).
Try
strResponse = reader.ReadLine
Catch ex As Exception
strResponse = Nothing
End Try
'Close
writer.Close()
reader.Close()
_NetworkStream.Close()
Return strResponse
Catch ex As Exception
Return Nothing
Finally
If Not _TCPClient Is Nothing Then
_TCPClient.Close()
_TCPClient = Nothing
End If
End Try
End Function
End Class
Holpe it helped.
Regards,
Afas.
Hi
how can i acknowledege an sending message in asynchronous socket program????
thank you
I'm currently working on the both the server and multiple clients with tcp port connections. A port has been opened at server side for listening. Have tried multiple clients connections upto 120 clients. But in certain times, new client connections to the server are failed, but the existing client connection to server side are stil working fine if they're still connected, once they disconnected it, they cant do reconnection.
Meaning to say that the server side doesnt respond to any new client tcp connection after some times. No specific error message could be found. Do you have any idea on this? Any possibilities for this matter to happen?
Look forward to your helps. Thanks in advance.
Cheers.
Hi,
I am new to this forum but I am trying to do exactly what you may have achieved!
I am trying to connect multiple clients to Server!
Any Help is appriciated
AG
i've been developing and small network app and this article was very helpfull but i have problems implementing the comunication part, i can connect, receive and send data the problem arise when i try to send several message or objects(via serialization) i found that i get half of the object on the receiving stream, making imposible de deserialization of the object, i don't know if there is a workaround to this or i need to implement tokens in the message to know when to deserialize the object, any help or ideas will be apreciate :)
also i like to know if there is a way to detect when the remotehost have been disconnected after calling the Socket.BeginReceive() method.
Sorry for the bad english... Duke
The following source code example has some un-safe thread issues.
Within the public method OnDataReceived(IAsyncResult asyn)
txtDataRx.Text = txtDataRx.Text + szData;
hi,
I try to build a network sniffer in .net framework 2
and use the socketname.beginReceive(buffer,0,bufferLength,......);
like raw socket.
but when I convert the value in the buffer in to string ,there are meaningless staffs.
Can you help me please..
That's Great! But, How can you show me the way to transaction betwen two computer over internet. Especially, in this example! Can you modify code for me to have connection betwen Server and Client over Internet! :(
Any more, I have some question want to have any help from you for design : :)
First: Game Online! Tell me the way to solve this kind of Programming! Server - Client!
Second: Mobile sendding data between PC and Mobile like Yahoo! Please help me explain my question in detail! :rolleyes:
Thank you so much for you reading!
Hi,
Iam doing a socket application,facing some problem in that.The issue is the remote host is sending some 10 messages means the client machine is able to capture only 3 to 4 messages.
i need help terribly.anyone pls help.thanks in advance
Hello -
Using your code - i sometimes come across an issue.
During the operation of the application, the CPU will
max out at 99%.
It stays like that until i end the app (of course)
I'm not sure where the code block is maxing out at.
has anyone come across this issue before?
thanks
tony
In your topic, suppose that I don't want to send charater, but I want to send a binary file. To do this, I read an image from harddisk and then transfer it to binary array.
When I send the Image binary array, I got a problem. When I know the server finish receiving data ?
Do you have any solution?
Rekcut-
You're not very good at the hacking game, mate! We can find you wherever you go.
private void cmdListenClick(object sender, System.EventArgs e)
{
try
{
//create the listening socket...
msocListener = new Socket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp);
[B]IPEndPoint ipLocal = new IPEndPoint ( IPAddress.Any ,8221);[/B]
//bind to local IP Address...
msocListener.Bind( ipLocal );
//start listening...
msocListener.Listen (4);
// create the call back for any client connections...
m_socListener.BeginAccept(new AsyncCallback ( OnClientConnect ),null);
cmdListen.Enabled = false;
}
catch(SocketException se)
{
MessageBox.Show ( se.Message );
}
}
see the red bold line change the
When ever you put a socket on the listening state you have to bind that socket with ip address and port number so you can mention the ip address and port before starting the listning process
If you want to know that
1)
Server sends data and many different clients get that data at same time instead of sending data to each client one by one then use the UDP (Universal datagram protocol) socket instead of TCP socket
Read or search about "connection less UDP socket connections"
2) Server receives data from more that one client at the same time then YES! this can be done using threads or multitasking
3) Server receives data in from different clients from different ports then again Yes! you have to set another socket to listen state in you want server to get data from different port from different users
Hi Ken,
I am having the same problem...
Did you get it to work?
Thanks,
Andre
Hi!
is it possible that the server manage more accesses in parallel?
I'm sorry for my bad English
/* declare a public variable */
// Recording "cmdListen" button is "Start Listening" or "Stop Listening" so far.
private bool isListening = false;
private void cmdListen_Click(object sender, System.EventArgs e) {
try {
if ( isListening ) {
/* closing m_socListener */
m_socListener.Close();
cmdListen.Text = "Start Listening";
btnSend.Enabled = false;
isListening = false;
} else {
/* constructing m_socListener */
//create the listening socket...
m_socListener = new Socket AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp);
IPEndPoint ipLocal = new IPEndPoint ( IPAddress.Any ,8221);
//bind to local IP Address...
m_socListener.Bind( ipLocal );
//start listening...
m_socListener.Listen(4);
// create the call back for any client connections...
m_socListener.BeginAccept(new AsyncCallback ( OnClientConnect ),null);
cmdListen.Text = "Stop Listening";
btnSend.Enabled = true;
isListening = true;
}
} catch(SocketException se) {
MessageBox.Show ( se.Message );
}
}
My client is a Windows machine. From this I create a socket connection to an IP and a port on a server machine. On the client machine I can manually telnet to the server, Send/Receive message like this:
Send: telnet IP Port
Receive: Connected to IP...
Send: Operation=TotalRecords
Receive: TotalRecords=1000
The fact that I can connect/Send/Receive suggests there is no access issue (the server is a linux machine).
Now here is my socket code. I can Connect and Send ("Operation=TotalRecords"). But Receive keep on waiting but never getting the message "TotalRecords=1000".
How do I code to receive the reply? Here is my code in C#:
IPAddress remoteIPAddress = IPAddress.Parse(LinuxIPAddress);
EndPoint ep = new IPEndPoint(remoteIPAddress, 4321);
Socket sock = new Socket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp);
string query="Operation=TotalRecords";
sock.Connect(ep);
Encoding ASCII = Encoding.ASCII;
Byte[] ByteGet = ASCII.GetBytes(query);
Byte[] RecvBytes = new Byte[256];
int iTx=sock.Send(ByteGet, ByteGet.Length, 0); //the code worked to this point
Int32 bytes = sock.Receive(RecvBytes, RecvBytes.Length, 0); //this will wait forever...
Thank you in advance.
Ken
Not responses as answers, responses as people asking the same question that I asked. This is a neat looking solution but it has a logicl problem and will only work with one client, that is not exceptable for an async socket. Here is a good solution that I have used for an async sockets.
http://www.theukwebdesigncompany.com/articles/asynchronous-socket-utility-classes.php
Sorry ... where is a response ?? .. thanks
IPEndPoint ipLocal = new IPEndPoint ( IPAddress.Any ,8221);
Sorry I did not look at the complete response list and see that this question was already posted.
I tried to running the server app and client app. Worked great, when I only had one client. If I run two clients and try to connect to the server I only see messages from the first client to connect. In fact, if I run one client and connect, then disconnect, and then reconnect, I get no connection refused error, but I do not get any messages comming across to the server. Is there some socket clean up that is not happening? I like this scheme of using async sockets but if only one person can connect, well thats a big problem, please enlighten me.
I can run the server application successfully with port 8221 but I get an error when I changed the port number to 80, 8100 or others.
Following is the exception:
Only one usage of each socket address (protocol/network address/port) is normally permitted.
Does anyone have any idea of how I can resolve this problem??
Managed to figure out the problem with the help of Zane on the microsoft.public.dotnet.languages.csharp newsgroup. Basically it comes down to ping being a UDP packet that does not have a socket connection and so have to use .BeginReceiveFrom rather than BeginReceive.
Ciao
Rekcut
Hi all,
I am currently trying to use the client code to simultaneously send icmp packets to several IPs from different threads. i.e. I have the client code as a class and I instantiate a new instance for each different IP. Within the class I implement an asynchronous delegate to run the client code, so that in the mean time I can return to the main thread and implement a new instance of the class and send a different icmp packet.
Thus at any one time I have several instances of the client code running on different threads, waiting for icmp packets to be received on the OnDataReceived callback.
My problem is that I am finding icmp reply packets from one thread in the receive databuffer of another thread. i.e.
the CSocketPacket object returned as a reference in "IAsyncResult asyn" is not coherent. In other words the data in theSockId.dataBuffer does not correspond to the theSockId.thisSocket socket.
Is this happening because this code is not threadsafe?? or does anyone have any idea of how I can resolve this problem??
Regards
Rekkie
regards,
tuco
I get an error when the client closes the connection, and then no new connections can be made. Does anybody have a solution
Also. Did anybody get it to work with multiple clients?
Did any of you get the program to work with multiple clients?
If so, how did you do it?
i want to ask if you can send me the program work ( multi client /server )
can you send to me .
my email : hamzahwh@yahoo.com
i want to ask if you can send me the program work ( multi client /server )
can you send to me .
my email : hamzahwh@yahoo.com
i want to ask if you can send me the program work ( multi client /server )
can you send to me .
my email : hamzahwh@yahoo.com
First, let me thank you for the great article - it has proved really valuable for me in my current project.
I am currently trying to extend the example to enable transfer of XML-data (or any data). However, I am not sure how to manipulate the recieved data as a complete string to parse using another function.
Your example revieces a byte at a time and appends this to the content of a text box. Your example uses an "iterative" function to recieve each byte and then calls WaitForData() to wait for the next byte. But to be able to manipulate this data I need to somehow collect it in a string and when the data/command from the client has been received - do something like sending a answer back.
The setup could be:
Client sends command: "GetAmountInAccount"
Server recieves this, checks the and replyes: "You are broke"
but how do I detect when the entire command from the client has been received?
Thanks,
Firstly I would just like to say thanks for the great example code. Very helpful. I am now trying to have more than 1 client listen/communicate with the server. I am getting some very strange results though. Can anybody please give me some advice. I have altered the code on the server side to go back to "listening" after the first client connects. This works, but when the second client connects the first client gets kicked out (after a few seconds?) somehow.
Look forward to any information.
Thanks
Jason
Grettings!
This thread is for discussions of Socket Programming in C# - Part 2.