Cross Thread Call in Client Server App

Jun 3 2010 6:07 AM
Hi all, I'm beginner here, please suggest some ideas and if possib complete code.. There are 2 application clientApp and serverApp. In ServerApp I have added for loop to delay response time of server(OnDataRecieved) when client sends message. In client side I need to write code to get send time and reply time so that I can calculate server response time. I tried using Diagnostic Stopwatch but got problem indicating Cross-thread operation not valid: Control 'richTextRxMessage' accessed from a thread other than the thread it was created on

Please help out..

serverApp

 public partial class ServerApp : Form
{


public delegate void UpdateRichEditCallback(string text);
public delegate void UpdateClientListCallback();

public AsyncCallback pfnWorkerCallBack ;
private Socket m_mainSocket;

// An ArrayList is used to keep track of worker sockets that are designed
// to communicate with each connected client. Make it a synchronized ArrayList
// For thread safety
private System.Collections.ArrayList m_workerSocketList =
ArrayList.Synchronized(new System.Collections.ArrayList());

// The following variable will keep track of the cumulative
// total number of clients connected at any time. Since multiple threads
// can access this variable, modifying this variable should be done
// in a thread safe manner
private int m_clientCount = 0;



public ServerApp()
{
InitializeComponent();

// Display the local IP address on the GUI
textBoxIP.Text = GetIP();
}


void ButtonStartListenClick(object sender, System.EventArgs e)
{
try
{
// Check the port value
if(textBoxPort.Text == "")
{
MessageBox.Show("Please enter a Port Number");
return;
}
string portStr = textBoxPort.Text;
int port = System.Convert.ToInt32(portStr);
// Create the listening socket...
m_mainSocket = new Socket(AddressFamily.InterNetwork,
SocketType.Stream,
ProtocolType.Tcp);
IPEndPoint ipLocal = new IPEndPoint (IPAddress.Any, port);
// Bind to local IP Address...
m_mainSocket.Bind( ipLocal );
// Start listening...
m_mainSocket.Listen(4);
// Create the call back for any client connections...
m_mainSocket.BeginAccept(new AsyncCallback (OnClientConnect), null);

UpdateControls(true);

}
catch(SocketException se)
{
MessageBox.Show ( se.Message );
}

}
private void UpdateControls( bool listening )
{
buttonStartListen.Enabled = !listening;
buttonStopListen.Enabled = listening;
}
// This is the call back function, which will be invoked when a client is connected
public void OnClientConnect(IAsyncResult asyn)
{
try
{
// Here we complete/end the BeginAccept() asynchronous call
// by calling EndAccept() - which returns the reference to
// a new Socket object
Socket workerSocket = m_mainSocket.EndAccept (asyn);

// Now increment the client count for this client
// in a thread safe manner
Interlocked.Increment(ref m_clientCount);

// Add the workerSocket reference to our ArrayList
m_workerSocketList.Add(workerSocket);

// Send a welcome message to client
string msg = "Welcome client " + m_clientCount +"\n";
SendMsgToClient(msg, m_clientCount);
// Update the list box showing the list of clients (thread safe call)
UpdateClientListControl();

// Let the worker Socket do the further processing for the
// just connected client
WaitForData(workerSocket, m_clientCount);

// Since the main Socket is now free, it can go back and wait for
// other clients who are attempting to connect
m_mainSocket.BeginAccept(new AsyncCallback ( OnClientConnect ),null);
}
catch(ObjectDisposedException)
{
System.Diagnostics.Debugger.Log(0,"1","\n OnClientConnection: Socket has been closed\n");
}
catch(SocketException se)
{
MessageBox.Show ( se.Message );
}

}
public class SocketPacket
{
// Constructor which takes a Socket and a client number
public SocketPacket(System.Net.Sockets.Socket socket, int clientNumber)
{
m_currentSocket = socket;
m_clientNumber = clientNumber;
}
public System.Net.Sockets.Socket m_currentSocket;
public int m_clientNumber;
// Buffer to store the data sent by the client
public byte[] dataBuffer = new byte[1024];
}
// Start waiting for data from the client
public void WaitForData(System.Net.Sockets.Socket soc, int clientNumber)
{
try
{
if ( pfnWorkerCallBack == null )
{
// Specify the call back function which is to be
// invoked when there is any write activity by the
// connected client
pfnWorkerCallBack = new AsyncCallback (OnDataReceived);
}
SocketPacket theSocPkt = new SocketPacket (soc, clientNumber);

soc.BeginReceive (theSocPkt.dataBuffer, 0,
theSocPkt.dataBuffer.Length,
SocketFlags.None,
pfnWorkerCallBack,
theSocPkt);
}
catch(SocketException se)
{
MessageBox.Show (se.Message );
}
}
// This the call back function which will be invoked when the socket
// detects any client writing of data on the stream
public void OnDataReceived(IAsyncResult asyn)
{
SocketPacket socketData = (SocketPacket)asyn.AsyncState ;
try
{
// Complete the BeginReceive() asynchronous call by EndReceive() method
// which will return the number of characters written to the stream
// by the client
int iRx = socketData.m_currentSocket.EndReceive (asyn);
char[] chars = new char[iRx + 1];
// Extract the characters as a buffer
System.Text.Decoder d = System.Text.Encoding.UTF8.GetDecoder();
int charLen = d.GetChars(socketData.dataBuffer,
0, iRx, chars, 0);

System.String szData = new System.String(chars);
string msg = "" + socketData.m_clientNumber + ":";
AppendToRichEditControl(msg + szData);

double num = 3000000;
double sum = 0;

for (int i = 0; i <= num; i++)
{
sum = sum + i;
}

// Send back the reply to the client
string replyMsg = "Server Reply:" + szData.ToUpper();
// Convert the reply to byte array
byte[] byData = System.Text.Encoding.ASCII.GetBytes(replyMsg);

Socket workerSocket = (Socket)socketData.m_currentSocket;
workerSocket.Send(byData);

// Continue the waiting for data on the Socket
WaitForData(socketData.m_currentSocket, socketData.m_clientNumber );

}
catch (ObjectDisposedException )
{
System.Diagnostics.Debugger.Log(0,"1","\nOnDataReceived: Socket has been closed\n");
}
catch(SocketException se)
{
if(se.ErrorCode == 10054) // Error code for Connection reset by peer
{
string msg = "Client " + socketData.m_clientNumber + " Disconnected" + "\n";
AppendToRichEditControl(msg);

// Remove the reference to the worker socket of the closed client
// so that this object will get garbage collected
m_workerSocketList[socketData.m_clientNumber - 1] = null;
UpdateClientListControl();
}
else
{
MessageBox.Show (se.Message );
}
}
}
// This method could be called by either the main thread or any of the
// worker threads
private void AppendToRichEditControl(string msg)
{
// Check to see if this method is called from a thread
// other than the one created the control
if (InvokeRequired)
{
// We cannot update the GUI on this thread.
// All GUI controls are to be updated by the main (GUI) thread.
// Hence we will use the invoke method on the control which will
// be called when the Main thread is free
// Do UI update on UI thread
object[] pList = {msg};
richTextBoxReceivedMsg.BeginInvoke(new UpdateRichEditCallback(OnUpdateRichEdit), pList);
}
else
{
// This is the main thread which created this control, hence update it
// directly
OnUpdateRichEdit(msg);
}
}
// This UpdateRichEdit will be run back on the UI thread
// (using System.EventHandler signature
// so we don't need to define a new
// delegate type here)
private void OnUpdateRichEdit(string msg)
{
richTextBoxReceivedMsg.AppendText(msg);
}

private void UpdateClientListControl()
{
if (InvokeRequired) // Is this called from a thread other than the one created
// the control
{
// We cannot update the GUI on this thread.
// All GUI controls are to be updated by the main (GUI) thread.
// Hence we will use the invoke method on the control which will
// be called when the Main thread is free
// Do UI update on UI thread
listBoxClientList.BeginInvoke(new UpdateClientListCallback(UpdateClientList), null);
}
else
{
// This is the main thread which created this control, hence update it
// directly
UpdateClientList();
}
}
void ButtonSendMsgClick(object sender, System.EventArgs e)
{
try
{
string msg = richTextBoxSendMsg.Text;
msg = "Server Msg: " + msg + "\n";
byte[] byData = System.Text.Encoding.ASCII.GetBytes(msg);
Socket workerSocket = null;
for(int i = 0; i < m_workerSocketList.Count; i++)
{
workerSocket = (Socket)m_workerSocketList[i];
if(workerSocket!= null)
{
if(workerSocket.Connected)
{
workerSocket.Send (byData);
}
}
}
}
catch(SocketException se)
{
MessageBox.Show (se.Message );
}
}

void ButtonStopListenClick(object sender, System.EventArgs e)
{
CloseSockets();
UpdateControls(false);
}

String GetIP()
{
String strHostName = Dns.GetHostName();

// Find host by name
IPHostEntry iphostentry = Dns.GetHostByName(strHostName);

// Grab the first IP addresses
String IPStr = "";
foreach(IPAddress ipaddress in iphostentry.AddressList)
{
IPStr = ipaddress.ToString();
return IPStr;
}
return IPStr;
}
void ButtonCloseClick(object sender, System.EventArgs e)
{
CloseSockets();
Close();
}
void CloseSockets()
{
if(m_mainSocket != null)
{
m_mainSocket.Close();
}
Socket workerSocket = null;
for(int i = 0; i < m_workerSocketList.Count; i++)
{
workerSocket = (Socket)m_workerSocketList[i];
if(workerSocket != null)
{
workerSocket.Close();
workerSocket = null;
}
}
}
// Update the list of clients that is displayed
void UpdateClientList()
{
listBoxClientList.Items.Clear();
for(int i = 0; i < m_workerSocketList.Count; i++)
{
string clientKey = Convert.ToString(i+1);
Socket workerSocket = (Socket)m_workerSocketList[i];
if(workerSocket != null)
{
if(workerSocket.Connected)
{
listBoxClientList.Items.Add(clientKey);
}
}
}
}
void SendMsgToClient(string msg, int clientNumber)
{
// Convert the reply to byte array
byte[] byData = System.Text.Encoding.ASCII.GetBytes(msg);

Socket workerSocket = (Socket)m_workerSocketList[clientNumber - 1];
workerSocket.Send(byData);
}

private void btnClear_Click(object sender, System.EventArgs e)
{
richTextBoxReceivedMsg.Clear();
}
}

clientApp..

public partial class ClientApp : Form
    {
        public ClientApp()
        {
            InitializeComponent();
            textBoxIP.Text = GetIP();
            

        }

        byte[] m_dataBuffer = new byte[10];
        IAsyncResult m_result;
        public AsyncCallback m_pfnCallBack;
        public Socket m_clientSocket;
        public DateTime startTime, endTime;

       


        void ButtonCloseClick(object sender, System.EventArgs e)
        {
            if (m_clientSocket != null)
            {
                m_clientSocket.Close();
                m_clientSocket = null;
            }
            Close();
        }

        void ButtonConnectClick(object sender, System.EventArgs e)
        {
            // See if we have text on the IP and Port text fields
            if (textBoxIP.Text == "" || textBoxPort.Text == "")
            {
                MessageBox.Show("IP Address and Port Number are required to connect to the Server\n");
                return;
            }
            try
            {
                UpdateControls(false);
                // Create the socket instance
                m_clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

                // Cet the remote IP address
                IPAddress ip = IPAddress.Parse(textBoxIP.Text);
                int iPortNo = System.Convert.ToInt16(textBoxPort.Text);
                // Create the end point
                IPEndPoint ipEnd = new IPEndPoint(ip, iPortNo);
                // Connect to the remote host
                m_clientSocket.Connect(ipEnd);
                if (m_clientSocket.Connected)
                {

                    UpdateControls(true);
                    //Wait for data asynchronously
                    WaitForData();
                }
            }
            catch (SocketException se)
            {
                string str;
                str = "\nConnection failed, is the server running?\n" + se.Message;
                MessageBox.Show(str);
                UpdateControls(false);
            }
        }
        void ButtonSendMessageClick(object sender, System.EventArgs e)
        {
            try
            {
                string msg = richTextTxMessage.Text;
                // New code to send strings
                NetworkStream networkStream = new NetworkStream(m_clientSocket);
                System.IO.StreamWriter streamWriter = new System.IO.StreamWriter(networkStream);
                streamWriter.WriteLine(msg);
                startTime = DateTime.Now;
                streamWriter.Flush();

                //watch.Start();

                /* Use the following code to send bytes
                byte[] byData = System.Text.Encoding.ASCII.GetBytes(objData.ToString ());
                if(m_clientSocket != null){
                    m_clientSocket.Send (byData);
                }
                */
            }
            catch (SocketException se)
            {
                MessageBox.Show(se.Message);
            }
        }
        public void WaitForData()
        {
            try
            {
                if (m_pfnCallBack == null)
                {
                    m_pfnCallBack = new AsyncCallback(OnDataReceived);
                }
                SocketPacket theSocPkt = new SocketPacket();
                theSocPkt.thisSocket = m_clientSocket;
                // Start listening to the data asynchronously
                m_result = m_clientSocket.BeginReceive(theSocPkt.dataBuffer,
                                                        0, theSocPkt.dataBuffer.Length,
                                                        SocketFlags.None,
                                                        m_pfnCallBack,
                                                        theSocPkt);
            }
            catch (SocketException se)
            {
                MessageBox.Show(se.Message);
            }

        }
        public class SocketPacket
        {
            public System.Net.Sockets.Socket thisSocket;
            public byte[] dataBuffer = new byte[1024];
        }

        public void OnDataReceived(IAsyncResult asyn)
        {
            try
            {
                SocketPacket theSockId = (SocketPacket)asyn.AsyncState;
                int iRx = theSockId.thisSocket.EndReceive(asyn);
                char[] chars = new char[iRx + 1];
                System.Text.Decoder d = System.Text.Encoding.UTF8.GetDecoder();
                int charLen = d.GetChars(theSockId.dataBuffer, 0, iRx, chars, 0);
                System.String szData = new System.String(chars);

                //watch.Stop();

                richTextRxMessage.Text = richTextRxMessage.Text + szData;
                endTime = DateTime.Now;
                TimeSpan elapsedTime=endTime - startTime;

                if (startTime < endTime)
                {
                    richTextRxMessage.Text = richTextRxMessage + Convert.ToString(elapsedTime);
                }

                WaitForData();
            }
            catch (ObjectDisposedException)
            {
                System.Diagnostics.Debugger.Log(0, "1", "\nOnDataReceived: Socket has been closed\n");
            }
            catch (SocketException se)
            {
                MessageBox.Show(se.Message);
            }
        }
        private void UpdateControls(bool connected)
        {
            buttonConnect.Enabled = !connected;
            buttonDisconnect.Enabled = connected;
            string connectStatus = connected ? "Connected" : "Not Connected";
            textBoxConnectStatus.Text = connectStatus;
        }
        void ButtonDisconnectClick(object sender, System.EventArgs e)
        {
            if (m_clientSocket != null)
            {
                m_clientSocket.Close();
                m_clientSocket = null;
                UpdateControls(false);
            }
        }
        //----------------------------------------------------    
        // This is a helper function used (for convenience) to
        // get the IP address of the local machine
        //----------------------------------------------------
        String GetIP()
        {
            String strHostName = Dns.GetHostName();

            // Find host by name
            IPHostEntry iphostentry = Dns.GetHostByName(strHostName);

            // Grab the first IP addresses
            String IPStr = "";
            foreach (IPAddress ipaddress in iphostentry.AddressList)
            {
                IPStr = ipaddress.ToString();
                return IPStr;
            }
            return IPStr;
        }

        private void btnClear_Click(object sender, System.EventArgs e)
        {
            richTextRxMessage.Clear();
        }
    }
}

Attachment: MainCode.rar

Answers (1)