2

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();
    }
}
FelixEvauro10
  • 21
  • 1
  • 3
  • You are using the async functions and immediately waiting for completion. Why? – usr May 30 '14 at 13:17
  • I've done that based on the async client socket example provided here on MSDN: http://msdn.microsoft.com/en-us/library/bew39x2a%28v=vs.110%29.aspx Then the MessageBox will (I hope) show me the response from the server. – FelixEvauro10 May 30 '14 at 13:33
  • Sad to see such examples on MSDN. You should ask more often "why am I doing this?". The point of async is to *not* wait. Use the synchronous methods and remove all callbacks from your code. Much simpler. This will allow you to debug the code more easily. I didn't see any problem that might cause the issue you describe. It is quite a lot of code, though. Not easy to find anything. Make a backup and delete everything not strictly required. Then update the post. – usr May 30 '14 at 13:50
  • I've added my revised code above. Any suggestions? – FelixEvauro10 Jun 02 '14 at 14:20
  • First of all you don't understand what Blocking does. Don't use it without understanding it. Even if the code seems to work using it (which it doesn't) you don't know whether it is actually correct in all cases.; Blocking is not the right solution here. Remove all of it. In fact it causes the first error you see. This is a usage error. You have misused the API.; To test the connection status you must send and receive something. To debug this, disable the connection testing code. Simplify the program.; I do not spot any error that is obvious to me. Apply these suggestions to make progress. – usr Jun 02 '14 at 14:37
  • That definitely helps resolve the "non-blocking socket operation" error, but I'm still stuck with the "connection aborted by the software in your host machine" error, which seems like a nightmare to solve. The UI thread seems to be blocked while it's trying to run the `Send` and `Receive` methods, but that's because they're running synchronously isn't it? – FelixEvauro10 Jun 02 '14 at 15:08
  • Can you create a 10 line repro for that? Just hard-code all the sends and receives. – usr Jun 02 '14 at 15:10
  • I've just tried creating a function to send and receive data, and then calling that function in `PassText_KeyPress`. But it doesn't make any difference (at least, not the way I've done it.) The problem now just seems to be actually getting the response from the server. I only get the "connection aborted by software in your host machine" error if I press the enter key more than once. So what am I missing? Why are all responses from the server (after the first one that shows a welcome message) empty? – FelixEvauro10 Jun 03 '14 at 09:25

0 Answers0