Send and receive using async client socket in c# forms

3.3k views Asked by At

I am trying to connect to a remote server, log in, and then send data to it and receive data from it using a C# forms application. Originally I wrote a console application, which works fine, but I need to use forms. The problem is that my BeginSend and BeginReceive methods in the forms application are not working.

I receive a message from the remote server when my socket first connects to it (successfully), and my ConnectionStatusTest shows I am still connected. But it seems that the connection is then dropped before I can send data to it. The original message from the server displays again (rather than the new message I want to receive), then the SendDataException and ReceiveDataException message boxes display, and the ConnectionStatusTest tells me I'm not connected any more.

Here's my code:

using System;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace ServerForm
{
    public partial class LoginForm : Form
    {
        public LoginForm()
        {
            InitializeComponent();
        }

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

    public class StateObject
    {
        public Socket clientSocket = null;
        public const int recvBufferSize = 1024;
        public byte[] recvbuffer = new byte[recvBufferSize];
        public StringBuilder sb = new StringBuilder();
    }

    private static ManualResetEvent connectionCompleted = new ManualResetEvent(false);
    private static ManualResetEvent sendCompleted = new ManualResetEvent(false);
    private static ManualResetEvent recvCompleted = new ManualResetEvent(false);

    private static String response = String.Empty;

    private void LoginForm_Shown(object sender, EventArgs e)
    {
        try
        {
            IHostEntry ipHost = Dns.GetHostEntry("*server's web address*");
            IPAddress serverIP = ipHost.AddressList[0];
            int port = 7500;
            IPEndPoint remoteEP = new IPEndPoint(serverIP, port);

            tcpSocket.BeginConnect(remoteEP, new AsyncCallback(ConnectCallback), tcpSocket);
            connectionCompleted.WaitOne();
            tcpSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.KeepAlive, true);
            //tcpSocket.IOControl(IOControlCode.KeepAliveValues, inValue, outValue);

            AsyncSendReceiveData.ReceiveData(tcpSocket);
            AsyncSendReceiveData.recvCompleted.WaitOne();
        }
    }
        catch (Exception ex)
        {
            MessageBox.Show(ex.Message, "LoginForm_Shown Exception");
        }
    }

    private static void ConnectCallback(IAsyncResult ar)
    {
        try
        {
            Socket tcpSocket = (Socket)ar.AsyncState;
            tcpSocket.EndConnect(ar);
            if (tcpSocket.Connected == true)
            {
                MessageBox.Show(String.Format("Socket connected to server ({0})", 
                tcpSocket.RemoteEndPoint.ToString()));
            }

            connectionCompleted.Set();
        }
        catch (Exception e)
        {
            MessageBox.Show(e.Message, "ConnectCallback Exception");
        }
    }

    private void passText_KeyPress(object sender, KeyPressEventArgs e)
    {
        if (e.KeyChar == (char)13)
        {
            try
            {
                string usernameString = userText.Text.ToUpper();
                string passwordString = passText.Text.ToUpper();
                string loginDetails = String.Format("LOGIN {0}:{1}",
                usernameString, passwordString);

                string data = loginDetails;

                AsyncSendReceiveData.SendData(tcpSocket, data);
                AsyncSendReceiveData.sendCompleted.WaitOne();

                AsyncSendReceiveData.ReceiveData(tcpSocket);
                AsyncSendReceiveData.recvCompleted.WaitOne();

                ConnectionStatusTest();

                string versionString = "VERSION 3";
                data = versionString;

                AsyncSendReceiveData.SendData(tcpSocket, data);
                AsyncSendReceiveData.sendCompleted.WaitOne();

                AsyncSendReceiveData.ReceiveData(tcpSocket);
                AsyncSendReceiveData.recvCompleted.WaitOne();
            }
            catch (SocketException ex)
            {
                MessageBox.Show(ex.Message, "KeyPress Exception");
            }
        }
    }

    public class AsyncSendReceiveData
    {
        public static ManualResetEvent sendCompleted = new ManualResetEvent(false);
        public static ManualResetEvent recvCompleted = new ManualResetEvent(false);

        public static void ReceiveData(Socket tcpSocket)
        {
            try
            {
                StateObject stateobj = new StateObject();
                stateobj.clientSocket = tcpSocket;

                tcpSocket.BeginReceive(stateobj.recvbuffer, 0,
                StateObject.recvBufferSize, 0,
                new AsyncCallback(ReceiveCallback), stateobj);
            }
            catch (SocketException e)
            {
                MessageBox.Show(e.Message + "\n" + "Exception occurred" + 
                e.StackTrace.ToString(), "ReceiveData Exception");
            }
        }

        private static void ReceiveCallback(IAsyncResult ar)
        {
            try
            {
                StateObject stateobj = (StateObject)ar.AsyncState;
                Socket tcpSocket = stateobj.clientSocket;

                int bytesRead = tcpSocket.EndReceive(ar);
                if (bytesRead > 0)  //This if statement seems to cause slow connection.
                {
                    //Adds more data received to sb                  
                    stateobj.sb.Append(Encoding.ASCII.GetString(stateobj.recvbuffer,
                    0, bytesRead));

                    //Get the rest of the data, if there is any more.
                    tcpSocket.BeginReceive(stateobj.recvbuffer, 0,
                    StateObject.recvBufferSize, 0,
                    new AsyncCallback(ReceiveCallback), stateobj);
                }
                else
                {
                    if (stateobj.sb.Length > 1)
                    {
                        response = stateobj.sb.ToString();
                    }
                    recvCompleted.Set();
                    MessageBox.Show(response);
                }                    
            }
            catch (Exception e)
            {
                MessageBox.Show(e.Message, "ReceiveCallback Exception");
            }
        }

        public static void SendData(Socket tcpSocket, String data)
        {
            try
            {
                byte[] sendDataBytes = Encoding.ASCII.GetBytes(data);
                tcpSocket.BeginSend(sendDataBytes, 0, sendDataBytes.Length,
                SocketFlags.None, new AsyncCallback(SendCallback), tcpSocket);
            }
            catch (SocketException e)
            {
                MessageBox.Show(e.Message + "\n" + "Exception occurred" +
                e.StackTrace.ToString(), "SendData Exception");
            }
        }

        private static void SendCallback(IAsyncResult ar)
        {
            try
            {
                Socket tcpSocket = (Socket)ar.AsyncState;
                tcpSocket.EndSend(ar);
                sendCompleted.Set();
            }
            catch (Exception e)
            {
                MessageBox.Show(e.Message, "SendCallback Exception");
            }
        }
    }

    public void ConnectionStatusTest()
    {
        try
        {
            byte[] tmp = new byte[1];
            bool blockingState = tcpSocket.Blocking;
            tcpSocket.Blocking = false;
            tcpSocket.Send(tmp, 0, 0, SocketFlags.None);
        }
        catch (SocketException SockEx)
        {
            if (SockEx.NativeErrorCode.Equals(10035))
                MessageBox.Show("Still connected, but the send would block");
            else
            {
                MessageBox.Show(String.Format("Disconnected: error code {0}",
                SockEx.NativeErrorCode.ToString()));
            }
        }
        finally
        {
            bool blockingState = tcpSocket.Blocking;
        }
        MessageBox.Show(String.Format("Connected: {0}",
        tcpSocket.Connected.ToString()));

I've spent a lot of time trying to work out why the BeginSend and BeginReceive methods aren't working, and I think I've narrowed it down to one of two things:

  1. In the passText_KeyPress function, the data part of string data = loginDetails is not passed as a parameter to the SendData function. Therefore the SendData function doesn't work properly and the SendDataException is displayed. If this is the problem, how do I pass loginDetails (or some other string) to the SendData function?

  2. When connecting using a console application, I have no problem connecting, logging in, or sending or receiving data, and the connection doesn't get dropped. However, when I tried to connect using telnet, a message came back from the server after about 30 seconds of inactivity, saying "connection to host lost". The connection seems to be dropped after about 30 seconds in my forms application too, and for some reason the form application takes about 25 seconds to tell me I'm connected. I have tried using KeepAlive in the forms application, but apparently the default TCP KeepAlive period is 2 hours. If this is the problem, how could I change this to, say, 20 seconds? Everything I've read suggests either letting Visual Studio through my firewall (I tried this - no luck), or having to change registry keys, which seems a bit too involved as a solution.

The error message is "an established connection was aborted by the software in your host machine", and the error code is 10053, which makes me think I'm getting timed out somehow. I may of course be wrong, and the problem could be something else I haven't spotted. I would really appreciate your help! Thanks.


UPDATE - I've rewritten the code using synchronous methods (and added a timer, just so I can see if I'm still connected every 5 seconds). I now get two different errors.

The first says "A non-blocking socket operation could not be completed immediately" and produces error code 10035. This error occurs at the Receive method while I am still connected to the remote server. I thought clearing the buffer might help, but it just means I get a blank message the next time I try to read it. So I can only think I'm not actually receiving a new message from the server, even though I know I should be.

The second is the original error - "An established connection was aborted by the software in your host machine", error code 10053 - and this occurs at the Send method when I get disconnected after 30 seconds. I've read in various places that changing the Socket.Blocking property might help, but I've tried making it true and false just before the calls to the Receive method, and neither helps.

Any ideas on how I can get this to work?

Revised code:

namespace ServerForm
{
    public partial class LoginForm : Form
    {
        public LoginForm()
        {
            InitializeComponent();
        }

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

    byte[] rcvdBytes = new byte [1024];
    System.Timers.Timer timer1 = new System.Timers.Timer(5000);

    private void LoginForm_Shown(object sender, EventArgs e)
    {
        try
        {
            timer1.Enabled = true;
            timer1.Elapsed += new ElapsedEventHandler(OnTimedEvent);
            timer1.AutoReset = true;

            IPHostEntry ipHost = Dns.GetHostEntry("*server's web address*");
            IPAddress serverIP = ipHost.AddressList[0];
            int port = 7500;
            IPEndPoint remoteEP = new IPEndPoint(serverIP, port);

            tcpSocket.Connect(remoteEP);
            tcpSocket.SetSocketOption(SocketOptionLevel.Socket,
            SocketOptionName.KeepAlive, true); //Has no effect, as I get
                                               //disconnected after 30 seconds.
            if (tcpSocket.Connected == true)
            {
                MessageBox.Show(String.Format("Socket connected to primary
                server {0}", tcpSocket.RemoteEndPoint.ToString()));
            }

            int bytesRcvd = tcpSocket.Receive(rcvdBytes);
            MessageBox.Show(String.Format("Response received: {0}",
            Encoding.ASCII.GetString(rcvdBytes, 0, rcvdBytes.Length),
            "Connected to server"));
            Array.Clear(rcvdBytes, 0, rcvdBytes.Length);
        }
        catch (Exception ex)
        {
            MessageBox.Show(String.Format(ex.Message, "LoginForm_Shown Exception"));
        }
    }

    private void passText_KeyPress(object sender, KeyPressEventArgs e)
    {            
        if (e.KeyChar == (char)13)
        {
            try
            {                    
                string usernameString = userText.Text.ToUpper();
                string passwordString = passText.Text.ToUpper();
                string loginDetails = String.Format("LOGIN {0}:{1}",
                usernameString, passwordString);

                byte[] loginBytes = Encoding.ASCII.GetBytes(loginDetails);

                tcpSocket.Send(loginBytes);  //SocketException (0x80004005):
                //"An established connection was aborted
                //by the software in your host machine"
                //occurs here once disconnected.  Error code 10053

                int bytesRcvd = tcpSocket.Receive(rcvdBytes); //SocketException
                //(0x80004005): "A non-blocking socket operation could not be 
                //completed immediately" occurs here while connected.
                //Error code 10035

                MessageBox.Show(String.Format("Response received: {0}",
                Encoding.ASCII.GetString(rcvdBytes, 0, rcvdBytes.Length)));
                //Displays same welcome message from server, or nothing if
                //I clear the rcvdBytes buffer first, and not a new message.

                string versionString = "VERSION 3";
                byte[] versionBytes = Encoding.ASCII.GetBytes(versionString);
                tcpSocket.Send(versionBytes);

                bytesRcvd = tcpSocket.Receive(rcvdBytes);
                MessageBox.Show(String.Format("Response received: {0}",
                Encoding.ASCII.GetString(rcvdBytes, 0, rcvdBytes.Length)));

                ConnectionStatusTest();
            }
            catch (SocketException sockEx)
            {
                MessageBox.Show(String.Format("SocketException: {0}",
                sockEx.ToString()));
            }
            catch (ArgumentNullException argNullEx)
            {
                MessageBox.Show(String.Format("ArgumentNullException: {0}",
                argNullEx.ToString()));
            }
            catch (Exception ex)
            {
                MessageBox.Show(String.Format("Exception: {0}", ex.ToString()));
            }
        }
    }

    public void ConnectionStatusTest()
    {
        try
        {
            byte[] tmp = new byte[1];
            bool blockingState = tcpSocket.Blocking;
            tcpSocket.Blocking = false;
            tcpSocket.Send(tmp, 0, 0, SocketFlags.None);
        }
        catch (SocketException SockEx)
        {
            if (SockEx.NativeErrorCode.Equals(10035))
                MessageBox.Show("Still connected, but the send would block");
            else
            {
                MessageBox.Show(String.Format("Disconnected: error code {0}",
                SockEx.NativeErrorCode.ToString()));
            }
        }
        finally
        {
            bool blockingState = tcpSocket.Blocking;
        }
        MessageBox.Show(String.Format("Connected: {0}",
        tcpSocket.Connected.ToString()));
    }

    private void LoginForm_FormClosed(object sender, FormClosedEventArgs e)
    {
        tcpSocket.Shutdown(SocketShutdown.Both);
        tcpSocket.Close();

        Form1 Childform1 = new Form1();
        Childform1.MdiParent = ParentForm;
        Childform1.Show();
    }

    private void OnTimedEvent(object sender, EventArgs e)
    {
        ConnectionStatusTest();
    }
}
0

There are 0 answers