2

I have been working on a client to server chat messenger and I have run into some issues regarding asynchronous calls. I have been trying, with no success, to fix them for a while.

The issue is with the client, there is no problem connecting to the server but once the first 'BeginSend' method is called the whole client stops responding.(It doesn't stop 100% it occasionally responds but after a ridiculous response time of 30 seconds+). The rest of the calls operate fine but the UI wont respond. The server responds fine as well.

Here is the code, I have commented HERE where the client first freezes.

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Windows.Forms.VisualStyles;
using System.Net.Sockets;
using System.Net;

namespace Client_GUI_Design_Test
{
    public partial class Login : Form
    {
        public Socket clientSocket;
        public string strName;
        public byte[] byteData = new byte[1024];
        public List<String> contacts = new List<String>();
        public Boolean needUpdate = true;
       // public Panel globalPanel;
        public Login()
        {
            InitializeComponent();
        }

        public struct Contact
        {
            public string strName;
        }


        public int num1 = 0;
        public void addContact(String contactName, String message, int status, int imageNum)
        {
            //MessageBox.Show(this.Size.Width.ToString());
            panel1.HorizontalScroll.Enabled = false;
            panel1.HorizontalScroll.Visible = false;
            if (num1 >= panel1.Size.Height - 47)
            {
                panel1.Size = new Size(257, 514);
                this.Size = new Size(277, this.Size.Height);
                panel1.AutoScroll = true;
                panel1.HorizontalScroll.Enabled = false;
                panel1.HorizontalScroll.Visible = false;
                panel1.VerticalScroll.Visible = true;
            }
            panel1.VerticalScroll.Value = 0;
            //Contact Panel
            Panel contactPanel = new Panel();
            contactPanel.Size = new Size(249, 47);
            contactPanel.Margin = new System.Windows.Forms.Padding(0, 0, 0, 0);

            //Name Label
            Label name = new Label();
            //Font
            float size = 12.00f;
            name.Font = new Font("Microsoft Sans Serif", size, FontStyle.Bold);
            //Location
            name.Location = new Point(29, 3);
            //Size
            name.Size = new Size(112, 20);
            name.Text = contactName;
            //Events
            #region
            name.MouseEnter += delegate
            {
                contactPanel.BackColor = Color.Gainsboro;
            };
            name.MouseLeave += delegate
            {
                contactPanel.BackColor = System.Drawing.SystemColors.Control;
            };
            #endregion
            contactPanel.Controls.Add(name);

            //Image Box
            PictureBox image = new PictureBox();
            //Image
            image.Image = imageList1.Images[imageNum];
            //Size
            image.Size = new Size(56, 42);
            //Location
            image.Location = new Point(187, 4);
            //Events
            #region
            image.MouseEnter += delegate
            {
                contactPanel.BackColor = Color.Gainsboro;
            };
            image.MouseLeave += delegate
            {
                contactPanel.BackColor = System.Drawing.SystemColors.Control;
            };
            #endregion
            contactPanel.Controls.Add(image);

            //Status
            PictureBox statusPic = new PictureBox();
            //Image
            statusPic.Image = imageList2.Images[status];
            //Size
            statusPic.Size = new Size(20, 20);
            //Location
            statusPic.Location = new Point(3, 3);
            //Events
            #region
            statusPic.MouseEnter += delegate
            {
                contactPanel.BackColor = Color.Gainsboro;
            };
            statusPic.MouseLeave += delegate
            {
                contactPanel.BackColor = System.Drawing.SystemColors.Control;
            };
            #endregion
            contactPanel.Controls.Add(statusPic);

            //Message Label
            Label messageL = new Label();
            //Font
            float sizem = 11.25f;
            messageL.Font = new Font("Microsoft Sans Serif", sizem, FontStyle.Bold);
            messageL.ForeColor = Color.Gray;
            //Location
            messageL.Location = new Point(29, 23);
            //Size
            messageL.Size = new Size(153, 18);
            //Text
            messageL.Text = message;
            //Events
            #region
            messageL.MouseEnter += delegate
            {
                contactPanel.BackColor = Color.Gainsboro;
            };
            messageL.MouseLeave += delegate
            {
                contactPanel.BackColor = System.Drawing.SystemColors.Control;
            };
            messageL.MouseHover += delegate
            {
                ToolTip tip = new ToolTip();
                tip.Tag = messageL.Text;
                tip.Show(messageL.Text, messageL, 1, 1, 750);
            };
            #endregion

            contactPanel.Controls.Add(messageL);

            contactPanel.Paint += new PaintEventHandler(contactPanel_Paint);
            {


            };
            contactPanel.MouseEnter += delegate
            {
                contactPanel.BackColor = Color.Gainsboro;
            };
            contactPanel.MouseLeave += delegate
            {
                contactPanel.BackColor = System.Drawing.SystemColors.Control;
            };
            contactPanel.Location = new Point(0, num1);
            //globalPanel = contactPanel;
            //panel1.Controls.Add(contactPanel);
            addPanel(contactPanel);
            num1 += 47;


        }

        public delegate void UpdatePanelCallBack(Panel panel);
        private void addPanel(Panel panel)
        {
            if (InvokeRequired)
            {
                object[] pList = { panel };
                panel1.BeginInvoke(new
                   UpdatePanelCallBack(OnUpdatePanel), pList);
            }

            else
            {
                OnUpdatePanel(panel);
            }
        }

        private void OnUpdatePanel(Panel panel)
        {
            panel1.Controls.Add(panel);
        }

        VisualStyleRenderer renderer = new VisualStyleRenderer(VisualStyleElement.Button.PushButton.Normal);
        void contactPanel_Paint(object sender, PaintEventArgs e)
        {
            renderer.DrawEdge(e.Graphics, panel1.ClientRectangle,
                Edges.Top,
                EdgeStyle.Bump, EdgeEffects.Flat);
        }

        private void button3_Click(object sender, EventArgs e)
        {
            try
            {
                clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

                //IPAddress ipAddress = IPAddress.Parse(txtServerIP.Text);
                //Server is listening on port 43594
                IPEndPoint ipEndPoint = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 43594);

                //Connect to the server
                clientSocket.BeginConnect(ipEndPoint, new AsyncCallback(OnConnect), null);
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message, "SGSclient", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }


        }

        private void OnSend(IAsyncResult ar)
        {
            try
            {
                Socket client = (Socket)ar.AsyncState;
                client.EndSend(ar);
                //clientSocket.EndSend(ar);
               //strName = textBox2.Text;
               // DialogResult = DialogResult.OK;
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message, "SGSclient", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }

        private void OnConnect(IAsyncResult ar)
        {
            try
            {
                clientSocket.EndConnect(ar);

                //We are connected so we login into the server
                Data msgToSend = new Data();
                msgToSend.cmdCommand = Command.Login;
                msgToSend.strName = textBox2.Text;
                msgToSend.strMessage = null;

                byte[] b = msgToSend.ToByte();

                //Send the message to the server
                //HERE - Starts freezing the UI thread, continues to do background operations
                clientSocket.BeginSend(b, 0, b.Length, SocketFlags.None, new AsyncCallback(OnSend), clientSocket);

                clientSocket.BeginReceive(byteData,
                                       0,
                                       byteData.Length,
                                       SocketFlags.None,
                                       new AsyncCallback(OnReceive),
                                       clientSocket);
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message, "SGSclient", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }

            panel4.Visible = false;
            panel1.Visible = true;
        }

        public void OnReceive(IAsyncResult ar)
        {
            try
            {
                clientSocket.EndReceive(ar);

                Data msgReceived = new Data(byteData);
                //Accordingly process the message received
                switch (msgReceived.cmdCommand)
                {
                    case Command.Login:
                        //lstChatters.Items.Add(msgReceived.strName);
                        break;

                    case Command.Logout:
                        //lstChatters.Items.Remove(msgReceived.strName);
                        break;

                    case Command.Message:
                        break;

                    case Command.List:
                        MessageBox.Show(msgReceived.strName);
                        //contacts.Add(msgReceived.strName);
                        needUpdate = true;
                        //lstChatters.Items.AddRange(msgReceived.strMessage.Split('*'));
                        //lstChatters.Items.RemoveAt(lstChatters.Items.Count - 1);
                        //txtChatBox.Text += "<<<" + strName + " has joined the room>>>\r\n";

                        break;
                }

                byteData = new byte[1024];

                clientSocket.BeginReceive(byteData,
                                          0,
                                          byteData.Length,
                                          SocketFlags.None,
                                          new AsyncCallback(OnReceive),
                                          clientSocket);

            }
            catch (ObjectDisposedException)
            { }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message, "SGSclientTCP: " + strName, MessageBoxButtons.OK, MessageBoxIcon.Error);
            }

        }

        private void Login_Load(object sender, EventArgs e)
        {
            CheckForIllegalCrossThreadCalls = false;
            //backgroundWorker1.RunWorkerAsync();

        }

        private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
        {
            while (true)
            {
                if(needUpdate){

                }
            }
        }


        private void Login_DoubleClick(object sender, EventArgs e)
        {
            MessageBox.Show("");
            foreach (string s in contacts)
            {
                addContact(s, "Random Message", 0, 1);
                needUpdate = false;
            }
        }
    }
}

Fixed the problem in the other classes, however still having issues in OnReceive (Might help to faster identify the issue)

public void OnReceive(IAsyncResult ar)
        {
            try
            {
                Socket client = (Socket)ar.AsyncState;
                client.EndReceive(ar);

                Data msgReceived = new Data(byteData);
                //Accordingly process the message received
                switch (msgReceived.cmdCommand)
                {
                    case Command.Login:
                        //lstChatters.Items.Add(msgReceived.strName);
                        break;

                    case Command.Logout:
                        //lstChatters.Items.Remove(msgReceived.strName);
                        break;

                    case Command.Message:
                        break;

                    case Command.List:
                        MessageBox.Show(msgReceived.strName);
                        //contacts.Add(msgReceived.strName);
                        needUpdate = true;
                        //lstChatters.Items.AddRange(msgReceived.strMessage.Split('*'));
                        //lstChatters.Items.RemoveAt(lstChatters.Items.Count - 1);
                        //txtChatBox.Text += "<<<" + strName + " has joined the room>>>\r\n";

                        break;
                }

                byteData = new byte[1024];

               /* client.BeginReceive(byteData,
                                          0,
                                          byteData.Length,
                                          SocketFlags.None,
                                          new AsyncCallback(OnReceive),
                                          client);*/

            }
            catch (ObjectDisposedException)
            { }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message, "SGSclientTCP: " + strName, MessageBoxButtons.OK, MessageBoxIcon.Error);
            }

        }

I know it is a lot of code, I am sorry but this is my last option I am completely stuck. Any help would be greatly appreciated :)

Simon Taylor
  • 503
  • 2
  • 7
  • 17
  • 1
    Try this clientSocket.BeginConnect(ipEndPoint, new AsyncCallback(OnConnect), clientSocket); and inside OnConnect use the socket from ar.AsyncState same as in OnSend. You are accessing clientSocket from different thread. – Mihai Hantea Mar 26 '14 at 04:59
  • @MihaiHantea After following you advice I was able to fix most of the methods apart from onReceive. I am still running into the same problem there. – Simon Taylor Mar 26 '14 at 05:33
  • 1
    You mean is still freezing when you call client.EndReceive(ar) ? – Mihai Hantea Mar 26 '14 at 05:41
  • @MihaiHantea yes, and by trial and error I have determined it freezes as soon as it calls: "client.BeginReceive(byteData, 0, byteData.Length, SocketFlags.None, new AsyncCallback(OnReceive), client); From the OnConnect Method – Simon Taylor Mar 26 '14 at 05:44
  • @SimonTaylor, are you limited to C# 4.0 / VS2010? Otherwise, you should be using `Task`-based socket API and `async`/`await`. It would reduce your code to a fraction of its size, and will make it a lot more simple and less error-prone. – noseratio Mar 26 '14 at 07:25
  • @Noseratio I am able to use C# 4.5 / VS2012 could you perhaps send me a link so I can have a look at how they work, perhaps and example or another question? – Simon Taylor Mar 26 '14 at 08:17
  • 1
    @SimonTaylor, some links: [1](http://bsmadhu.wordpress.com/2012/09/29/simplify-asynchronous-programming-with-c-5-asyncawait/), [2](http://stackoverflow.com/a/22033127/1768303), [3](http://stackoverflow.com/q/22222531/1768303). – noseratio Mar 26 '14 at 08:31
  • @Noseratio Thanks, I will look into it but does it require .net 5.0 because I am not sure I can target that :( – Simon Taylor Mar 26 '14 at 08:49
  • @SimonTaylor, it requires .NET 4.5 and VS2012+ (i.e .C# 5.0). – noseratio Mar 26 '14 at 08:50
  • 1
    @Noseratio oh sorry misread it, I should be able to use it. I will take a look, see what I can do. Thanks :) – Simon Taylor Mar 26 '14 at 08:51

1 Answers1

1

You are accessing clientSocket from different thread.

Inside button3_Click call BeginConnect passing your socket like this:

//Connect to the server
clientSocket.BeginConnect(ipEndPoint, new AsyncCallback(OnConnect), clientSocket);

Inside OnReceive get the socket from AsyncState (passed here when called BeginConnect):

Socket client = (Socket)ar.AsyncState;
client.EndReceive(ar);

From msdn EndConnect

EndConnect is a blocking method that completes the asynchronous remote host connection request started in the BeginConnect method

Before calling BeginConnect, you need to create a callback method that implements the AsyncCallback delegate. This callback method executes in a separate thread and is called by the system after BeginConnect returns. The callback method must accept the IAsyncResult returned by the BeginConnect method as a parameter.

EDIT:

As i can see in your code, your are using byteData buffer which is declared as class field and accessed from another thread when BeginReceive is trying to access it. Check the Asynchronous Server Socket Example to see how you can handle the reading part.

You can look at this example on msdn Socket Code Examples

Asynchronous Server Socket Example

Asynchronous Client Socket Example

Mihai Hantea
  • 1,741
  • 13
  • 16
  • Sorry for being so ignorant but I can't see anything in my code that deviates from your solutions. My code runs, without freezing, fine as long as I dont call any BeginReceive methods, hence I feel the issue may reside in the OnReceive methods. Also are the manual event handlers, which are present in the msdn examples, necessary they feel as I feel they defeat the purpose of A-Synchronicity. Thank you so much for your help so far :) – Simon Taylor Mar 26 '14 at 06:07
  • 1
    I updated the answer. When you use that byteData buffer you are doing same thing as before, accessing resources owned by the UI thread from another thread. Try using a similar class as in their example StateObject. – Mihai Hantea Mar 26 '14 at 06:10
  • Thanks your solution works, however I am having trouble reading data the same way as that shown in the example. I create a new instance of StateObject and pass it as the state to OnReceive I then create a new instance of StateObject from ar.AsyncState however it still freezes as if I had accessed resources from the main thread, if you could help it would be so Amazing :) – Simon Taylor Mar 26 '14 at 07:09
  • 1
    Can you post another question for the BeginReceive, OnReceive with the relevant code to see how it looks now? I guess you are missing something. – Mihai Hantea Mar 26 '14 at 07:23
  • Sorry I was away from my computer temporarily, here is the new question: http://stackoverflow.com/questions/22655111/asynchronous-sockets-blocking-ui-thread Thank you so much :) – Simon Taylor Mar 26 '14 at 08:22
  • 1
    Ok, I will have a look. – Mihai Hantea Mar 26 '14 at 08:47