Question

I'm currently working on a chat-programm. It contains one Form(MainWindow) and one class(TCPServerConnector). The MainWindow is the UI which contains two textboxes, two buttons and one listbox.

In the first textbox you have to write the servers ip you want to connect with, than you have to click the button "Connect". The second textbox contains your written text and the second button sends this text. The listbox should display all chats/texts the program received from the server, but i dont get it to work.

The Main-Form initializes an object of the TCPServerConnector and passes the IP and the text to it. But when it comes to the point where you receive some text the class have to pass the received texts to the Form.

I tried to use delegates (never used them before) which didn't resolve my problem, i just got some exceptions about cross-threading..

Also creating a new instance of the MainWindow because it doesn't reference to the old instance of the Main-Form.

I've added the code of the classes below, hopefully someone could handle my problem ;)


Main-Form:

public partial class MainWindow : Form
{

    public MainWindow()
    {
        InitializeComponent();
    }

    TCPServerConnector TestConnection = new TCPServerConnector();

    private void Form1_Load(object sender, EventArgs e)
    {
    }

    private void srvConnect_Click(object sender, EventArgs e)
    {
        TestConnection.ConnectToServer(srvConnectionIP.Text.ToString());
    }

    private void srvSend_Click(object sender, EventArgs e)
    {
        TestConnection.SendEnteredString(srvTextToSend.Text.ToString());
    }

    private void FormMain_Closing(object sender, System.ComponentModel.CancelEventArgs e)
    {
        TestConnection.CloseConnection();
    }
}

In the Method "OnRecievedData" I've added an Messagebox to show the received text, because for now, I can't make it work to add this to the Forms listbox.

TCPServerConnector-Class:

class TCPServerConnector
{
    private Socket m_sock;
    private byte[] m_byBuff = new byte[256];

    public void ConnectToServer(string ServerAddress)
    {
        try
        {
            if (m_sock != null && m_sock.Connected)
            {
                m_sock.Shutdown(SocketShutdown.Both);
                System.Threading.Thread.Sleep(10);
                m_sock.Close();
            }

            m_sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

            IPEndPoint epServer = new IPEndPoint(IPAddress.Parse(ServerAddress), 399);

            m_sock.Blocking = false;
            AsyncCallback onconnect = new AsyncCallback(OnConnect);
            m_sock.BeginConnect(epServer, onconnect, m_sock);
        }
        catch (Exception ex)
        {
            MessageBox.Show(ex.Message);
        }
    }

    public void OnConnect(IAsyncResult ar)
    {
        Socket sock = (Socket)ar.AsyncState;

        try
        {
            if (sock.Connected)
                SetupRecieveCallback(sock);
            else
                MessageBox.Show("Unable to establish connection");
        }
        catch (Exception ex)
        {
            MessageBox.Show(ex.Message);
        }
    }

    public void OnRecievedData(IAsyncResult ar)
    {
        Socket sock = (Socket)ar.AsyncState;

        try
        {
            int nBytesRec = sock.EndReceive(ar);
            if (nBytesRec > 0)
            {
                string sRecieved = Encoding.ASCII.GetString(m_byBuff, 0, nBytesRec);
                MessageBox.Show(sRecieved); //TO SHOW IF SOMETHING HAPPENED. HERE IS MY PROBLEM
                SetupRecieveCallback(sock);
            }
            else
            {
                Console.WriteLine("Client {0}, disconnected", sock.RemoteEndPoint);
                sock.Shutdown(SocketShutdown.Both);
                sock.Close();
            }
        }
        catch (Exception ex)
        {
            MessageBox.Show(ex.Message);
        }
    }

    public void SetupRecieveCallback(Socket sock)
    {
        try
        {
            AsyncCallback recieveData = new AsyncCallback(OnRecievedData);
            sock.BeginReceive(m_byBuff, 0, m_byBuff.Length, SocketFlags.None, recieveData, sock);
        }
        catch (Exception ex)
        {
            MessageBox.Show(ex.Message);
        }
    }

    public void SendEnteredString(string EnteredData)
    {
        if (m_sock == null || !m_sock.Connected)
        {
            MessageBox.Show("You have to be connected to send something.");
            return;
        }

        try
        {
            Byte[] byteDateLine = Encoding.ASCII.GetBytes(EnteredData.ToCharArray());
            m_sock.Send(byteDateLine, byteDateLine.Length, 0);
        }
        catch (Exception ex)
        {
            MessageBox.Show(ex.Message);
        }
    }

    public void CloseConnection()
    {
        if (m_sock != null && m_sock.Connected)
        {
            m_sock.Shutdown(SocketShutdown.Both);
            m_sock.Close();
        }
    }
}
Was it helpful?

Solution

Create event DataReceived in your connector class and raise it when data received:

public class TCPServerConnector
{
    public event Action<string> DataReceived; // event

    public void OnRecievedData(IAsyncResult ar)
    {    
        try
        {
            Socket sock = (Socket)ar.AsyncState;
            int nBytesRec = sock.EndReceive(ar);
            if (nBytesRec > 0)
            {
                string sRecieved = Encoding.ASCII.GetString(m_byBuff, 0, nBytesRec);
                if (DataReceived != null) // check if subscribed
                    DataRecieved(sRecieved); // raise event with data

                SetupRecieveCallback(sock);
            }
            else
            {
                Console.WriteLine("Client {0}, disconnected", sock.RemoteEndPoint);
                sock.Shutdown(SocketShutdown.Both);
                sock.Close();
            }
        }
        catch (Exception ex)
        {
            MessageBox.Show(ex.Message);
        }
    }

    // rest of code is same
}

And subscribe to this event in main form:

public MainWindow()
{
    InitializeComponent();
    TestConnection.DataRecieved += TestConnection_DataRecieved;
}

private void TestConnection_DataRecieved(string data)
{ 
   if (InvokeRequired) // check if you are on main thread
   {
       // invoke same method on main thread
       Invoke((MethodInvoker)(() => TestConnection_DataRecieved(data)));
       return;
   }

   // show data in listbox
}

NOTE: You can use EventHandler<TEventArgs> type of delegate for event (like Microsoft does) and create protected method OnDataReceived(string data) for raising event. But I used Action<string> delegate for simplicity.

OTHER TIPS

Reason: You are getting cross thread exception because you are creating GUI on one thread and accessing it on another thread. This is not permissible in c#.

You need to change your line like this

 MethodInvoker method = delegate
    {
        MessageBox.Show(sRecieved);
    };

if (InvokeRequired)
    BeginInvoke(method);
else
    method.Invoke();
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top