2

我正在尝试连接到远程服务器,登录,然后使用 C# 表单应用程序向它发送数据并从它接收数据。最初我写了一个控制台应用程序,效果很好,但是我需要使用表单。问题是表单应用程序中的我BeginSendBeginReceive方法不起作用。

当我的套接字第一次连接到远程服务器(成功)时,我收到一条来自远程服务器的消息,并且我的ConnectionStatusTest节目我仍然连接。但似乎在我向它发送数据之前连接就被断开了。来自服务器的原始消息再次显示(而不是我想要接收的新消息),然后显示SendDataExceptionReceiveDataException消息框,并ConnectionStatusTest告诉我我不再连接。

这是我的代码:

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()));

我花了很多时间试图弄清楚为什么 BeginSend 和 BeginReceive 方法不起作用,我认为我已经将其缩小到以下两件事之一:

  1. passText_KeyPress函数中, 的data部分string data = loginDetails不作为参数传递给SendData函数。因此该SendData功能无法正常工作并SendDataException显示 。如果这是问题所在,我如何将loginDetails(或其他字符串)传递给SendData函数?

  2. 使用控制台应用程序连接时,连接、登录或发送或接收数据都没有问题,并且连接不会断开。但是,当我尝试使用 telnet 进行连接时,在大约 30 秒不活动后从服务器返回一条消息,说“与主机的连接丢失”。在我的表单应用程序中,连接似乎也在大约 30 秒后断开,并且由于某种原因,表单应用程序需要大约 25 秒来告诉我我已连接。我曾尝试在表单应用程序中使用 KeepAlive,但显然默认的 TCP KeepAlive 期限为 2 小时。如果这是问题所在,我该如何将其更改为 20 秒?我读过的所有内容都建议让 Visual Studio 通过我的防火墙(我试过这个 - 没有运气),或者必须更改注册表项,

错误消息是“已建立的连接被主机中的软件中止”,错误代码是 10053,这让我觉得我不知何故超时了。我当然可能是错的,问题可能是我没有发现的其他问题。我将衷心感谢您的帮助!谢谢。


更新- 我已经使用同步方法重写了代码(并添加了一个计时器,这样我就可以每 5 秒查看一次是否仍然连接)。我现在得到两个不同的错误。

第一个说“无法立即完成非阻塞套接字操作”并产生错误代码 10035Receive当我仍然连接到远程服务器时,该方法会发生此错误。我认为清除缓冲区可能会有所帮助,但这只是意味着我下次尝试阅读它时会收到一条空白消息。所以我只能认为我实际上并没有收到来自服务器的新消息,即使我知道我应该这样做。

第二个是原始错误 - “已建立的连接被主机中的软件中止”,错误代码 10053 -Send当我在 30 秒后断开连接时发生这种情况。我已经在不同的地方读到更改Socket.Blocking属性可能会有所帮助,但我已经尝试在调用该方法之前进行它true,但都没有帮助。falseReceive

关于如何让它发挥作用的任何想法?

修改后的代码:

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();
    }
}
4

0 回答 0