I've been trying to get my head wrapped around socket communications and I've put together a small Windows Forms application as a test. This is basically a client that will connect to a server, send some bytes and disconnect. Ultimately, I'll receive a response from the server, too, but I've mostly stripped that out for now.
As best I can tell, this code works properly but since the sending of the data occurs as a result of a button click in my UI, it needs to be asynchronous. I have a method called SendDataToServer(byte[] bytesToSend)
that connects to the server and transmits the data. In my button click event handler, I created a backgroundworker which will call this method.
If my server isn't up and running, I'll obviously get a socket exception and certainly there are other reasons that an exception could get thrown during the process of attempting to connect and transfer data. With the backgroundworker and the async socket callbacks (ClientConnectCallback()
and ClientSendCallback()
), what's the best way to make sure that any exceptions get bubbled up and displayed properly in my UI?
Right now I have MessageBoxes inside the catch blocks within the async callbacks themselves but I'm wondering if this is really the place to be displaying the messages or if they should be passed back up?
Here's what my code looks like:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.Windows.Forms;
namespace ClientTest
{
public partial class MyForm : Form
{
private string ServerIpAddress = "x.x.x.x";
private int ServerPort = 59176;
public Socket ClientSocket;
private static ManualResetEvent connectDone = new ManualResetEvent(false);
private static ManualResetEvent sendDone = new ManualResetEvent(false);
// state object for reading client data asynchronously
public class StateObject
{
// client socket
public Socket socket = null;
// size of receive buffer
public const int BufferSize = 1024;
// receive buffer
public byte[] buffer = new byte[BufferSize];
// all bytes received get added to this
public List<byte> bytes = new List<byte>();
}
public MyForm()
{
InitializeComponent();
}
private void ClientConnectCallback(IAsyncResult asyncResult)
{
try
{
// retrieve the socket from the state object
Socket client = (Socket)asyncResult.AsyncState;
// complete the connection
client.EndConnect(asyncResult);
// signal that the connection has been made
connectDone.Set();
}
catch (SocketException sockEx)
{
// if the server isn't running, we'll get a socket exception here
MessageBox.Show(sockEx.Message, Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Error);
}
catch (ObjectDisposedException)
{
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
private void ClientSendCallback(IAsyncResult asyncResult)
{
try
{
// retrieve the socket from the state object
Socket client = (Socket)asyncResult.AsyncState;
// complete sending the data to the server
int bytesSent = client.EndSend(asyncResult);
// signal that all bytes have been sent
sendDone.Set();
}
catch (ObjectDisposedException objDispEx)
{
Debug.WriteLine(objDispEx.Message);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
public void SendDataToServer(byte[] bytesToSend)
{
try
{
connectDone.Reset();
sendDone.Reset();
ClientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
IPAddress ipAddress = IPAddress.Parse(ServerIpAddress);
IPEndPoint remoteEndPoint = new IPEndPoint(ipAddress, ServerPort);
ClientSocket.BeginConnect(remoteEndPoint, new AsyncCallback(ClientConnectCallback), ClientSocket);
connectDone.WaitOne();
ClientSocket.BeginSend(bytesToSend, 0, bytesToSend.Length, 0, new AsyncCallback(ClientSendCallback), ClientSocket);
sendDone.WaitOne();
}
catch (ObjectDisposedException objDispEx)
{
Debug.WriteLine(objDispEx.Message);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Error);
}
finally
{
ClientSocket.Shutdown(SocketShutdown.Both);
ClientSocket.Close();
}
}
private void buttonSendDataToServer_Click(object sender, EventArgs e)
{
BackgroundWorker bwSendDataToServer = new BackgroundWorker();
bwSendDataToServer.DoWork += bwSendDataToServer_DoWork;
bwSendDataToServer.RunWorkerCompleted += bwSendDataToServer_RunWorkerCompleted;
bwSendDataToServer.RunWorkerAsync();
}
private void bwSendDataToServer_DoWork(object sender, DoWorkEventArgs e)
{
byte[] bytesToSend = new byte[100];
SendDataToServer(bytesToSend);
}
private void bwSendDataToServer_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
Debug.WriteLine("BackgroundWorker has completed.");
}
}
}