Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/303.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
如何在C#中解除套接字绑定?_C#_.net_Sockets_Asynchronous - Fatal编程技术网

如何在C#中解除套接字绑定?

如何在C#中解除套接字绑定?,c#,.net,sockets,asynchronous,C#,.net,Sockets,Asynchronous,我在测试应用程序中重用服务器套接字时遇到一些问题。基本上,我有一个同时实现客户端和服务器端的程序。出于测试目的,我运行了该程序的两个实例,一个实例开始托管,另一个实例连接。这是监听代码: private void Listen_Click(object sender, EventArgs e) { try { server = new ConnectionWrapper(); HideControls(); alreadyReset

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

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()
对象,实现如下:

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
,我会看到我使用的端口仍然处于绑定状态,其状态是
正在侦听
,即使在调用
Dispose()
之后也是如此。我读到这是正常的,并且该端口有一个“解除绑定”的超时

有没有办法强制该端口解除绑定或设置很短的超时,直到它自动解除绑定?现在,它只有在我退出程序时才会解除绑定。也许我的服务器有问题?如果是,那会是什么?为什么客户端连接良好,而服务器端第二次没有检测到它

我可以让套接字始终侦听,而不是处理它,并使用单独的套接字来处理服务器连接,这可能会修复它,但我希望其他程序能够在连续播放会话之间使用该端口


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

端口保持打开可能有几个原因,但我认为您应该能够通过在套接字上使用显式的
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.
你在这里写的评论是错误的。您的包装器类实际上根本不应该允许写入
连接。但是您不能简单地用客户机套接字替换侦听套接字-它们是两个不同的套接字

这里将要发生的是:(a)侦听套接字超出范围,因此永远不会显式关闭/释放-这将在随机时间发生,可能是在不愉快的时间。(b)您关闭的套接字只是客户端套接字,它不会关闭侦听套接字,因此您在重新绑定另一个侦听套接字时遇到问题也就不足为奇了

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


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

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

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

当您运行
netstat
时,您是否看到它处于
TIME\u WAIT
状态?@Aaronaught:不,它说的是
listing
这不起作用。如果我使用此选项而不是“重用地址”选项,则会出现“仅一个连接…”错误,如果我同时使用这两个选项,则会发生与我描述的相同的情况。@IVlad:我更新了此答案,花了一段时间才意识到发生了什么,直到我读到其中一条评论时我才注意到……太好了,这就解决了问题,谢谢。这将教会我再次过早地优化。。。如果您还想回答一个后续问题,我将非常感谢:在我将
EndAccept
返回的客户端套接字分配给另一个套接字之后,是否可以处理侦听套接字,还是应该仅在游戏实际结束时处理它?这两种方法现在似乎都有效,但正确的方法是什么?@IVlad:老实说,我从未尝试过,可能是因为我没有编写只接受一个客户端的套接字服务器。我认为,一旦有了客户端套接字,就可以关闭侦听套接字,但实际上没有理由这样做,因为如果将其设置为积压1,侦听套接字无论如何都不会接受新连接。至少按照惯例,“正确”的方法是一直监听,直到服务器关闭。您不需要使用LingerOption(true,0)吗?LingerOption.Enabled的文档表示,如果为false,则使用默认超时,这与立即中止close.interest不同。这会阻塞服务器。这里不是讽刺,但我不知道为什么会被阻止,因为我只在点击按钮时发送小字符串。我在想有什么东西阻塞了连接,从而使它保持打开状态,但我不知道是什么。