c# - 如何在 C# 中取消绑定(bind)套接字?

标签 c# .net sockets asynchronous

我在创建的测试应用程序中重用服务器套接字时遇到了一些问题。基本上,我有一个同时实现客户端和服务器端的程序。我出于测试目的运行该程序的两个实例,一个实例开始托管,另一个实例连接。这是监听代码:

private void Listen_Click(object sender, EventArgs e)
{
    try
    {
        server = new ConnectionWrapper();
        HideControls();
        alreadyReset = false;

        int port = int.Parse(PortHostEdit.Text);
        IPEndPoint iep = new IPEndPoint(IPAddress.Any, port);

        server.connection.Bind(iep); // bellow explanations refer to this line in particular
        server.connection.Listen(1);
        server.connection.BeginAccept(new AsyncCallback(OnClientConnected), null);
        GameStatus.Text = "Waiting for connections on port " + port.ToString();
    }
    catch (Exception ex)
    {
        DispatchError(ex);
    }
}
private void OnClientConnected(IAsyncResult iar)
{
    try
    {
        me = Player.XPlayer;
        myTurn = true;
        server.connection = server.connection.EndAccept(iar); // I will only have one client, so I don't care for the original listening socket.
        GameStatus.Text = server.connection.RemoteEndPoint.ToString() + " connected";
        StartServerReceive();
    }
    catch (Exception ex)
    {
        DispatchError(ex);
    }
}

这第一次工作正常。然而,过了一会儿(当我的小游戏结束时),我在 server 对象上调用了 Dispose(),实现如下:

public void Dispose()
{
    connection.Close(); // connection is the actual socket
    commandBuff.Clear(); // this is just a StringBuilder
}

我在对象构造函数中也有这个:

public ConnectionWrapper()
{
    commandBuff = new StringBuilder();
    connection = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

    connection.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
}

当我第二次点击 Listen 按钮时,我没有收到任何错误消息。客户端连接正常,但是我的服务器端没有第二次检测到客户端连接,这基本上使服务器无用。我猜它正在连接到旧的、挥之不去的套接字,但老实说,我不知道为什么会这样。这是客户端连接代码:

private void Connect_Click(object sender, EventArgs e)
{
    try
    {
        client = new ConnectionWrapper();
        HideControls();
        alreadyReset = false;

        IPAddress ip = IPAddress.Parse(IPEdit.Text);
        int port = int.Parse(PortConnEdit.Text);
        IPEndPoint ipe = new IPEndPoint(ip, port);
        client.connection.BeginConnect(ipe, new AsyncCallback(OnConnectedToServer), null); 
    }
    catch (Exception ex)
    {
        DispatchError(ex);
    }
}

如果我在 CMD 中执行 netstat -a,我会看到我使用的端口仍然绑定(bind)并且其状态为 LISTENING,即使在调用 Dispose( )。我读到这是正常的,并且该端口“未绑定(bind)”有超时。

有没有办法强制该端口解除绑定(bind)或设置一个非常短的超时时间,直到它自动解除绑定(bind)?现在,它只有在我退出程序时才会解除绑定(bind)。也许我在我的服务器上做错了什么?如果是这样,那会是什么?为什么客户端连接正常,服务端第二次检测不到?

我可以让套接字始终监听,而不是处理它,并使用单独的套接字来处理服务器连接,这可能会解决它,但我希望其他程序能够在连续的游戏 session 之间使用该端口。

我记得看到另一个问题问这个,但我的情况没有令人满意的答案。

最佳答案

端口保持打开状态可能有几个原因,但我认为您应该能够通过在套接字上使用显式 LingerOption 来解决您的问题:

LingerOption lo = new LingerOption(false, 0);
socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Linger, lo);

这基本上将套接字关闭变成了异常关闭,而不是正常关闭。如果您希望它优雅但不想等待太久,请在构造函数中使用 true 并为超时指定一个小但非零的值。

我刚刚注意到这一行,这几乎无疑是您问题的一部分:

server.connection = server.connection.EndAccept(iar); // I will only have one client, so I don't care for the original listening socket.

你在这里写的评论是错误的。您的包装类根本不应该允许写入 connection 。但是您不能简单地将监听套接字替换为客户端套接字 - 它们是两个不同的套接字!

这里将要发生的是 (a) 监听套接字超出范围,因此永远不会明确关闭/处置 - 这将在随机时间发生,可能是在一个令人讨厌的时间。 (b) 您关闭的套接字只是客户端套接字,它不会关闭监听套接字,因此难怪您在重新绑定(bind)另一个监听套接字时遇到问题。

您实际看到的不是套接字超时,而是垃圾收集器意识到监听套接字已死并释放/完成它所花费的时间。要解决这个问题,您需要停止覆盖监听套接字;您的包装类的 Dispose 方法应该处理原始监听套接字,并且应该单独跟踪客户端套接字,并在您实际使用它时处理它。

事实上,您根本不需要重新绑定(bind)另一个监听套接字。监听套接字始终保持事件状态。实际连接仅由客户端套接字表示。您应该只需要在最终关闭服务器时处理监听套接字。

关于c# - 如何在 C# 中取消绑定(bind)套接字?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2821520/

相关文章:

java - 点对点套接字

c# - 在 C# 的 Windows 窗体中的 PictureBox 中显示 System.Windows.Media.Imaging.BitmapSource

c# - 删除大数据集的关闭点 C#

c# - 使用单声道的 SocketCAN C#

c# - 如何获取服务器域名

c# - 使用 LINQ 查询获取索引值的集合

c# - 如何使 Visual Studio 扩展的构建失败

c++ - 如何使用 C++ 将服务器的响应接收到字符串变量中?

c# - 在 C# 中干燥对象实例化

python - 使用Python通过套接字(端口80)连接基本访问webdata(html)