【问题标题】:How to handle client disconnect in Socket Programming?如何在套接字编程中处理客户端断开连接?
【发布时间】:2013-01-20 20:11:29
【问题描述】:

我使用 Sockets 编写了一个服务器端代码,它工作正常,但有一个问题我不知道如何处理这种情况:如果客户端只是关闭应用程序而不发送断开连接请求,我的服务器端程序会崩溃。我需要做些什么来避免这种情况?请指导我,我是 Socket 编程的新手。

private void OnReceive(IAsyncResult result)
{
    try
    {
        Socket clientSocket = (Socket)result.AsyncState;
        clientSocket.EndReceive(result);

        command = responseMessage = string.Empty;
        command = ByteToString(receviedData);
        receviedData = new byte[30];

        if (command=="Connect")
        {
            ClientInfo clientInfo = new ClientInfo();
            clientInfo.socket = clientSocket;
            clientInfo.IP = clientSocket.RemoteEndPoint.ToString();
            connectedClients.Add(clientInfo);
            responseMessage = "Connection established...";
        }
        else if (command=="Disconnect")
        {
            for (int i = 0; i < connectedClients.Count; i++)
            {
                if (connectedClients[i].socket == clientSocket)
                {
                    connectedClients.RemoveAt(i);
                    break;
                }
            }
            clientSocket.Close();
        }
        else
        {
            responseMessage = "Error";
        }

        byte[] responseStatus = StringToByte(responseMessage);
        for (int i = 0; i < connectedClients.Count; i++)
        {
            if (connectedClients[i].socket==clientSocket)
            {
                connectedClients[i].socket.BeginSend(responseStatus, 0, responseStatus.Length,SocketFlags.None, new AsyncCallback(OnSend), connectedClients[i].socket);
                break;
            }
        }

    }
    catch(Exception ex)
    {
      throw new Exception(ex.Message);
    }
}

【问题讨论】:

  • 您将需要尝试/捕获相应的异常:msdn.microsoft.com/en-US/library/0yd65esw(v=vs.110).aspx 由于您似乎对堆栈溢出不熟悉,请务必阅读常见问题解答部分。 stackoverflow.com/faq
  • 请在您得到异常的地方发布代码。
  • 我正在使用这个,但是整个服务器端应用程序因此崩溃并且无法响应其他客户端
  • 我已经发布了代码@ErenErsönmez
  • @user1941098 好吧,您只是重新抛出异常。您只需忽略它(但可能会执行其他操作,例如从 connectedClients 中删除客户端对象)。

标签: c# sockets network-programming


【解决方案1】:

您的应用程序崩溃了,因为您在方法的 catch 块中抛出了异常。

如果您不希望您的应用程序崩溃,您需要从 catch 块中删除 throw new Exception(ex.Message); 行。 将其替换为处理错误并将您的应用程序优雅地恢复到安全状态的代码。通过阅读您的代码,这应该通过从 connectedClients 中删除 clientSocket 来完成

其次,最好只使用throw; 而不是throw new Exception(ex.Message);throw; 将重新抛出原始异常对象,从而保留堆栈跟踪和其他有助于调试软件的重要信息。 使用new Exception("Message") 将使用当前堆栈跟踪创建一个全新的异常对象。

private void OnReceive(IAsyncResult result)
{
    try
    {
        Socket clientSocket = (Socket)result.AsyncState;
        clientSocket.EndReceive(result);

        command = responseMessage = string.Empty;
        command = ByteToString(receviedData);
        receviedData = new byte[30];

        if (command=="Connect")
        {
            ClientInfo clientInfo = new ClientInfo() {
               socket = clientSocket,
               IP = clientSocket.RemoteEndPoint.ToString(),
               }; 
            connectedClients.Add(clientInfo);
            responseMessage = "Connection established...";
        }
        else if (command=="Disconnect")
        {
            removeClientInfo(clientSocket);
            clientSocket.Close();
        }
        else
        {
            responseMessage = "Error";
        }

        byte[] responseStatus = StringToByte(responseMessage);
        for (int i = 0; i < connectedClients.Count; i++)
        {
            if (connectedClients[i].socket==clientSocket)
            {
                connectedClients[i].socket.BeginSend(responseStatus, 0, responseStatus.Length,SocketFlags.None, new AsyncCallback(OnSend), connectedClients[i].socket);
                break;
            }
        }

    }
    catch(Exception ex)
    {
      // add error handling and gracefully recover
      // caution: The way done here, might work, but it smells :-/
      removeClientInfo((Socket)result.AsyncState);
      ((Socket)result.AsyncState).Close();
    }
}

/// removes the client info from the connectedClients enumerable
private void removeClientInfo(Socket socket)
{
    for (int i = 0; i < connectedClients.Count; i++)
    {
        if (connectedClients[i].socket == socket)
        {
            connectedClients.RemoveAt(i);
            break;
        }
    }
}

【讨论】:

  • 有没有更好的方法呢?有什么最佳实践来处理这个问题?
  • 您指的是我的回答的哪一部分?恕我直言,这两部分都是最佳实践
  • clientSocket不在catch块的范围内!如何处理 Catch 块中客户端的删除或关闭连接?
  • 哪种方法更适合您或 Eren Ersönmez?哪一个套件可扩展应用程序?
  • @user1941098 Eren 和我都使用相同的方法(在 catch 块中正确处理错误)。他还指出,您需要检查EndReceive 的返回值,我对代码进行了一些更改以增强可读性。 tl;dr:全部使用。 关于“可扩展应用程序”:目前这不是您的问题。你的问题是让它工作。一旦您的代码有效、正确且可读,就关心性能。不早了
【解决方案2】:

您在 catch 块内抛出了一个新异常,除非您正在执行一些日志记录或类似操作,否则这没有多大意义。将 catch 块更改为:

catch(SocketException)
{ 
}

此外,您应该检查从EndReceive 返回的读取字节数。如果您收到零字节,则表示客户端已关闭连接:

int numReceived = clientSocket.EndReceive(result);
if(numReceived == 0)
{
    //client has shutdown the connection
}

【讨论】:

  • EndReceive 和 Receive 方法有什么区别吗?
  • 当然它们是不同的。它们分别与异步套接字和同步有关。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-12-20
  • 1970-01-01
  • 2021-03-25
  • 2013-12-10
  • 1970-01-01
  • 2018-10-19
相关资源
最近更新 更多