如何在C#中取消绑定套接字?

时间:2010-05-12 18:08:32

标签: 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);
    }
}

第一次这很好用。但是,过了一会儿(当我的小游戏结束时),我在Dispose()对象上调用server,实现如下:

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,我看到我使用的端口仍然是绑定的,其状态为LISTENING,即使在调用Dispose()之后也是如此。我读到这是正常的,并且该端口超时是“未绑定”的。

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

我可以让套接字始终监听,不处理它,并使用单独的套接字来处理服务器连接,这可能会解决它,但我希望其他程序能够在连续的播放会话之间使用端口。 / p>

我记得有另外一个问题要问这个,但我的案子没有令人满意的答案。

2 个答案:

答案 0 :(得分:7)

端口保持打开可能有几个原因,但我认为您应该能够通过在套接字上使用显式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)你关闭的套接字只是客户端套接字,它不会关闭监听套接字,所以毫无疑问你在重新绑定另一个监听套接字时遇到了麻烦。

您实际目击的不是套接字超时,而是垃圾收集器意识到侦听套接字已死并且自由/完成它所需的时间。要解决此问题,您需要停止覆盖侦听套接字;包装器类的Dispose方法应该配置原始监听套接字,并且应该单独跟踪客户端套接字并在实际完成时处理它。

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

答案 1 :(得分:3)

我同意之前的回答,您还应该“关闭”以允许任何现有活动完成,然后关闭套接字标记它以便重复使用...

socket.Shutdown(SocketShutdown.Both);
socket.Disconnect(true);