【问题标题】:C# BeginSend and BeginReceive issues in socket connection套接字连接中的 C# BeginSend 和 BeginReceive 问题
【发布时间】:2016-09-30 13:45:13
【问题描述】:

我在我的 C# 应用程序中同时使用 BeginSend 和 BeginReceive。它是一个客户端监听器,只会接收来自服务器的数据。我正在使用 BeginReceive 来获取数据。

但我发现仅接收并不足以保持连接的活跃和稳定。因此,我决定让我的应用程序始终使用 BeginSend ping 服务器以保持连接处于活动状态。

下面是我的代码:

private void timer1_Tick(object sender, EventArgs e)
{
    DataProcess();
}

public Socket _serverSocket = null;
private byte[] _recieveBuffer = new byte[128];
byte[] AllBytes;
void DataProcess()
{
    //TimeSpan ts;
    try
    {
        //Thread.Sleep(100);
        if (_serverSocket == null || sockState == MySocketState.Disconnected)
        {
            Console.WriteLine("Trying to connect...");
            SetupServer();
        }
        else
        {
            AllBytes = ASCIIEncoding.ASCII.GetBytes(COM_DeviceIsOnline);
            _serverSocket.BeginSend(AllBytes, 0, AllBytes.Length, SocketFlags.None, new AsyncCallback(SendCallback), null);
        }
    }
    catch (Exception ex)
    {
        Console.WriteLine(ex.ToString());
    }
}

private void SetupServer()
{
    try
    {
        _serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
        _serverSocket.Connect(_sIP, Int32.Parse(_sPort));
    }
    catch (SocketException ex)
    {
        Console.WriteLine(ex.Message);
    }

    if (_serverSocket.Connected)
    {
        sockState = MySocketState.Connected;
        browser.ExecuteScriptAsync("svrConnect();");
        Console.WriteLine("Server connected...");
        _serverSocket.BeginReceive(_recieveBuffer, 0, _recieveBuffer.Length, SocketFlags.None, new AsyncCallback(ReceiveCallback), null);
    }
}

void SendCallback(IAsyncResult ar)
{
    try
    {
        int bytesSent = _serverSocket.EndSend(ar);
    }
    catch (Exception ex)
    {
        Console.WriteLine(ex.ToString());
    }
}

private void ReceiveCallback(IAsyncResult AR)
{
    try
    {
        if (_serverSocket == null)
            _serverSocket = (Socket)AR.AsyncState;

        //Check how much bytes are recieved and call EndRecieve to finalize handshake
        int recieved = _serverSocket.EndReceive(AR);

        if (recieved <= 0)
        {
            CloseSocket();
            return;
        }

        //Copy the recieved data into new buffer , to avoid null bytes
        byte[] recData = new byte[recieved];
        Buffer.BlockCopy(_recieveBuffer, 0, recData, 0, recieved);
        string strData = ASCIIEncoding.ASCII.GetString(recData);
        Console.WriteLine(strData);

        //Process data here the way you want , all your bytes will be stored in recData

        int nSOHPos = -1;
        int nSTXPos = -1;
        int nETXPos = -1;
        int nEOTPos = -1;

        nSOHPos = Array.IndexOf(recData, SOH);
        nSTXPos = Array.IndexOf(recData, STX);
        nETXPos = Array.IndexOf(recData, ETX);
        nEOTPos = Array.IndexOf(recData, EOT);

        if ((nSOHPos == -1) || (nSTXPos == -1) || (nETXPos == -1) || (nEOTPos == -1))
        {
            CloseSocket();
            return;
        }

        if (nSOHPos > nSTXPos ||
                nSTXPos > nETXPos ||
                nETXPos > nEOTPos)
        {
            CloseSocket();
            return;
        }

        if ((nETXPos - nSTXPos) < 6)
        {
            CloseSocket();
            return;
        }

        string _sCommand = ASCIIEncoding.ASCII.GetString(recData, nSTXPos + 6, 2);

        if ((_sCommand == "CN") || (_sCommand == "CL") || (_sCommand == "CR"))
        {
            nDept = int.Parse(ASCIIEncoding.ASCII.GetString(recData, nSTXPos + 12, 2));
            if (nDept != dept_id) return;
            nCntr = int.Parse(ASCIIEncoding.ASCII.GetString(recData, nSTXPos + 14, 2));
            nServ = int.Parse(ASCIIEncoding.ASCII.GetString(recData, nSTXPos + 16, 2));
            sQnum = ASCIIEncoding.ASCII.GetString(recData, nSTXPos + 8, 4);
            string[] sSplit = strData.Split('^');
            string sCusName = sSplit[1];
            //if (_sCommand == "CR") sCusName = " ";
            Console.WriteLine("Dept ID = " + nDept);
            Console.WriteLine("Cntr ID = " + nCntr);
            Console.WriteLine("Serv ID = " + nServ);
            Console.WriteLine("Queue No = " + sQnum);
            Console.WriteLine("Cus Name = " + sCusName);
            var script = string.Format("nextCall(\'{0}\', {1}, {2});", sQnum, nCntr, nServ);
            browser.ExecuteScriptAsync(script);
        }
    }
    catch (Exception ex)
    {
        Console.WriteLine(ex.ToString());
        CloseSocket();
    }
    finally
    {
        try
        {
            //Start receiving again
            if (_serverSocket != null)
                _serverSocket.BeginReceive(_recieveBuffer, 0, _recieveBuffer.Length, SocketFlags.None, new AsyncCallback(ReceiveCallback), null);
            //theDevSock.BeginReceive(_recieveBuffer, 0, _recieveBuffer.Length, SocketFlags.None, ReceiveCallback, theDevSock);
        }
        catch (Exception ex2)
        { }
    }
}

public void CloseSocket()
{
    try
    {
        if (_serverSocket != null)
        {
            if (_serverSocket.Connected)
                _serverSocket.Shutdown(SocketShutdown.Both);
            _serverSocket.Close();
        }
    }
    catch (Exception ex)
    {
        Console.WriteLine(ex.ToString());
    }
    _serverSocket = null;
    sockState = MySocketState.Disconnected;
    browser.ExecuteScriptAsync("svrDisconnect();");
}

我的应用程序开始连接到服务器。连接到服务器后,它将开始 ping 服务器。但我发现每次使用 BeginSend ping 服务器后,都会出现错误“线程 0x1ff0 已退出,代码为 259 (0x103)”。并且与服务器的连接将被断开,我的应用程序将重新尝试再次连接服务器。然后在连接成功后,再次ping服务器时,它再次断开连接。

我可以知道我的编码是否正确编写?我可以像这样使用 BeginSend 来 ping 服务器吗?

【问题讨论】:

  • 要保持连接活跃,您可以使用SetSocketOption 方法,例如:socket.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.KeepAlive, true);
  • 我应该在哪里添加这行代码?可以推荐吗?
  • 这真的取决于你的应用逻辑是怎样的,但一般的方法是在你连接之前设置套接字选项。
  • 你是什么意思,“保持连接活跃”? TCP 可以永远保持没有数据的开放连接,除非线路上有其他东西破坏了它。一个更大的问题是您没有使用任何消息框架,这根本不适用于 TCP - TCP 是基于流的协议,而不是基于消息的协议;如果需要消息,则需要添加自己的消息协议。
  • @raidensan,我试着把它放进去,它有以下错误:在getsockopt或setsockopt调用中指定了未知、无效或不受支持的选项或级别第一次机会异常类型为'System.Net System.dll 中发生 .Sockets.SocketException'

标签: c# winforms visual-studio sockets server


【解决方案1】:

我添加了以下代码来发送 ping:

        private void timer2_Tick(object sender, EventArgs e)
    {
        pingServer();
    }

    void pingServer()
    {
        if (_serverSocket != null || sockState == MySocketState.Connected)
        {
            Console.WriteLine("Ping Server");
            AllBytes = ASCIIEncoding.ASCII.GetBytes(COM_DeviceIsOnline);
            //_serverSocket.BeginSend(AllBytes, 0, AllBytes.Length, SocketFlags.None, new AsyncCallback(SendCallback), null);
            SendTCPData(0x01, 1, 1, 1, "AA", "");
        }
    }

    public bool SendTCPData(byte bySequenceNo, int nSlaveID, int nDeviceID,
                            int nStatus, String strCommand, String strData)
    {
        return SendTCPData(ACK, bySequenceNo, nSlaveID, nDeviceID, nStatus, strCommand, strData);
    }

    public bool SendTCPData(byte byTypeCode, byte bySequenceNo,
                            int nSlaveID, int nDeviceID,
                            int nStatus, String strCommand,
                            String strData)
    {
        return SendTCPData(byTypeCode, bySequenceNo, nSlaveID, nDeviceID,
                            nStatus, strCommand, strData, 0);
    }

    public bool SendTCPData(byte byTypeCode, byte bySequenceNo,
                            int nSlaveID, int nDeviceID,
                            int nStatus, String strCommand, String strData, int nProtocolType)
    {
        byte[] AllBytes;
        String strTemp;
        try
        {
            strTemp = nSlaveID.ToString("0#") +
              nDeviceID.ToString("0#") +
              nStatus.ToString() +
              strCommand + strData;

            if (nProtocolType == 0)
            {

                AllBytes = new byte[strTemp.Length + 7];
                AllBytes[0] = SOH;
                AllBytes[1] = byTypeCode;
                AllBytes[2] = (byte)((int)bySequenceNo | 0x80);
                AllBytes[3] = STX;
                ASCIIEncoding.ASCII.GetBytes(strTemp).CopyTo(AllBytes, 4);
                AllBytes[AllBytes.Length - 3] = ETX;
                AllBytes[AllBytes.Length - 2] = 128;
                AllBytes[AllBytes.Length - 1] = EOT;
            }
            else
            {
                AllBytes = new byte[strTemp.Length + 6];
                AllBytes[0] = SOH;
                AllBytes[1] = STX;
                ASCIIEncoding.ASCII.GetBytes(strTemp).CopyTo(AllBytes, 2);
                AllBytes[AllBytes.Length - 4] = ETX;
                AllBytes[AllBytes.Length - 3] = ETX;
                AllBytes[AllBytes.Length - 2] = 128;
                AllBytes[AllBytes.Length - 1] = EOT;
            }
            //Debug.WriteLine("Sending data to " + _sIPAddr + " -> " + ASCIIEncoding.ASCII.GetString(AllBytes));
            return SendByte(AllBytes, AllBytes.Length);
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.ToString());
        }
        return false;

    }

    public bool SendByte(byte[] pByteData, int nLength)
    {
        try
        {
            if  (_serverSocket == null) return false;
            if (!_serverSocket.Connected) return false;
            Console.WriteLine("SendByte = " + ASCIIEncoding.ASCII.GetString(pByteData, 0, nLength));
            //Debug.WriteLine("SendByte Length= " + nLength.ToString() );
            _serverSocket.BeginSend(pByteData, 0, nLength, SocketFlags.None,
                SendCallback, _serverSocket);
            //dtLastActivity = DateTime.Now;
            return true;
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.ToString());
            CloseSocket();

        }
        return false;
    }

我使用 30 秒计时器来发送 ping,所以只要它与服务器有连接,它就会发送 ping。现在,它已经可以检测到拔下的电缆了。

我想知道你们对这个答案的看法吗?

【讨论】:

  • 只是我的应用程序在我添加了 ping 代码后,在调试模式下,它偶尔会显示“线程 0x??? has exit with code 259”。在我添加 ping 代码之前不会发生这种情况。这是一个问题吗?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-06-28
  • 2017-04-15
  • 2016-04-22
  • 1970-01-01
相关资源
最近更新 更多