Search code examples
c#multithreadingasyncsocketudpclient

Asyncronous Data coming from udp socket into TextBox Simultaneously


I am dealing with socket programming, but i have some issues about threading mechanism. Well, actually this is a big question mark on my head.

Firstly, i have a UDPListener class that i can receive the data coming from some clients on network. Additionally, i put an event handler in that class, i want to invoke a textbox when data is received simultaneously.

        class udpListener
        {     
           private static int portNumber = 9081;
           private UdpClient udp = new UdpClient(portNumber);
           private bool isend = false;
           public volatile int CountOfReceived = 0;
           public List<string> messageList = new List<string>();
           public List<string> ipList = new List<string>();
           public bool isClientClosed = false;
           messageReceivedEventArgs mre=new messageReceivedEventArgs();

        public void StartListening()
        {
            udp.BeginReceive(Receive, new object());   //BeginReceive must be completed by calling EndReceive method
        }

        private void Receive(IAsyncResult ar)
        {
            if (udp.Client == null)
                return;
            Interlocked.Increment(ref CountOfReceived);

            IPEndPoint ip = new IPEndPoint(IPAddress.Any, portNumber);
            byte[] bytes = udp.EndReceive(ar, ref ip);  //Ends a pending asynchronous receive

            if (bytes.Length > 0)
            {
                mre.isMessageReceived = true;
                mre.message = Encoding.ASCII.GetString(bytes); 
            }
            else
                mre.isMessageReceived = false;

            controlMessageReceived();
            string message = Encoding.ASCII.GetString(bytes);
            string[] seperator = {"|"};
            string[] array = message.Split(seperator, StringSplitOptions.RemoveEmptyEntries);
            messageList.Add(array[0]);
            ipList.Add(ip.Address.ToString());
            Console.WriteLine(message);
            StartListening();       
        }

        public void closeSocket()
        {
            if (udp.Client != null)
                udp.Close();
        }

        private void controlMessageReceived()
        {
            if(mre.isMessageReceived)
                handler(this,mre);
        }

        public event EventHandler<messageReceivedEventArgs> handler;


    }

    public class messageReceivedEventArgs:EventArgs
    {
        public bool isMessageReceived { get; set; }
        public string message {get;set;}
    }

As you can see, when bytes.length bigger than zero, i suppose yes, data is received and now i need to invoke textbox to transfer that data into textbox.

I simply test this scenario with a buttoneventhandler in main thread

   public partial class Form1 : Form
   {
    public Form1()
    {
        InitializeComponent();
    }


    private void button1_Click(object sender, EventArgs e)
    {

        networkInterface ni = new networkInterface();

        udpSender UDPSender = new udpSender();
        udpListener UDPListener = new udpListener();

        IPAddress braddress = ni.GetBroadcastAddress(ni.GetDefaultGateway(), ni.GetSubnetMask());
        UDPSender.Send(braddress, "somedatahere");
        Thread.Sleep(2000);
        UDPListener.StartListening();
        UDPListener.handler += appendTextBox;


    }      

    private void appendTextBox(object sender, messageReceivedEventArgs e)
    {
        textBox1.Text += e.message;

    }
}

I would store those messages into a string list and after socket is closed i would read that data from that listbox, but it is so tricky and unprofessional way to do that, i want to have those data in textbox while they are still received by server. I definetely aware of that my textbox is in main thread and appendtext method belongs to Asyncronous thread, and it is not permitted to access that textbox from another thread, but i really cannot figure out how to do that, what am i supposed to do to obtain what i desire.

Here is the solution how i overcome this problem, thanks to @Troy Mac1ure

   public partial class Form1 : Form
   {

    public delegate void UpdateTextCallback(string s);
    public Form1()
    {
        InitializeComponent();
    }


    private void button1_Click(object sender, EventArgs e)
    {
        networkInterface ni = new networkInterface();

        udpSender UDPSender = new udpSender();
        udpListener UDPListener = new udpListener();

        IPAddress braddress = ni.GetBroadcastAddress(ni.GetDefaultGateway(), ni.GetSubnetMask());
        UDPSender.Send(braddress, "gr|smartrc");
        Thread.Sleep(2000);
        UDPListener.StartListening();
        UDPListener.handler += appendTextBox;     

    }

    private void appendTextBox(object sender, messageReceivedEventArgs e)
    {
        if (textBox1.InvokeRequired)
        {
            textBox1.Invoke(new UpdateTextCallback(addTextBox), new object[] { e.message });  // invoking itself
        }
        else
        {
            textBox1.Text += e.message;
        }      
    }

    private void addTextBox(string text)
    {
        textBox1.Text += text;
    }
}

Solution

  • You can force the update to happen on the main thread by invoking itself. There are a few ways to declare and perform this task. Here is one:

    public delegate void UpdateTextCallback(object sender, messageReceivedEventArgs e);
    
    private void appendTextBox(object sender, messageReceivedEventArgs e)
    {
            if (textBox1.InvokeRequired)
            {
                listBox1.Invoke(new UpdateTextCallback(appendTextBox), new object[] { sender, e });  // invoking itself
            }
            else
            {
                textBox1.Text += e.message;
            }
    }