C# 解决Socket/TCPServer监听端口释放失败的问题

现象

使用Socket建立了TCPServer开启监听后,关闭连接无法成功。

亦或是关闭连接后,二次创建仍显示端口占用问题。


原因以及方法:

①可能是数据流还在传输,导致TCPServer无法关闭。不能直接调用Close()方法。

proxsocket.Close(100);//参数内是允许数据再传输100ms

解决方法:在关闭前,结束全部数据流的传输,再Close,即可成功关闭监听,释放监听端口。

public void StopServerConnet(Socket proxsocket)//关闭连接
{
    try
    {
        proxsocket.Shutdown(SocketShutdown.Both);//禁用某 Socket 上的发送和接收。
    }
    catch (Exception)
    {

    }
    finally
    {
        proxsocket.Close(100);
        proxsocket.Dispose();
    }

}

注意:假如在ShutDonw的时候Catch到了错误,证明禁用失败,已经是其他的问题,此时Close也无济于事。请看原因2。

②在有Client断开连接的时候,Server没有主动去再去断开一次Client。

假如你Server存在一个或多个Client连接,在接收消息方法体内应该监听Client连接情况并且做出相应处理。假如在Client断开时没有做主动断开的处理,就只是结束此方法线程,端口也将无法释放。

错误示范

    var ProxSocket = socket as Socket;
    byte[] data = new byte[1024 * 1024];//接收消息的缓冲区
    while (true)
    {
        int len = 0;//记录消息长度
        try
        {
            len = ProxSocket.Receive(data, 0, data.Length, SocketFlags.None);//此处接收Client的连接

        }
        catch (Exception)
        {
            //假如Client异常退出
            return;//让方法结束,终结当前接收服务端数据的异步线程
        }
        if (len <= 0)
        {
            //如果小于0,证明无连接,CLient正常退出
            return;//让方法结束,终结当前接收服务端数据的异步线程
        }

        //接收到的消息处理
        //…………

    }
    return;

解决方法:在Client异常退出和正常退出时,都要主动去断开Client的连接,后续监听端口就才能正确释放。

正确示范:

public void ReceiveData(object socket)//TCPServer接收消息的方法
{
    var ProxSocket = socket as Socket;
    byte[] data = new byte[1024 * 1024];//接收消息的缓冲区
    while (true)
    {
        int len = 0;//记录消息长度
        try
        {
            len = ProxSocket.Receive(data, 0, data.Length, SocketFlags.None);//此处接收Client的连接

        }
        catch (Exception)
        {
            //假如Client异常退出

            //应该关闭此Client连接
            ps.StopServerConnet(ProxSocket);

            return;//让方法结束,终结当前接收服务端数据的异步线程
        }
        if (len <= 0)
        {
            //如果小于0,证明无连接,CLient正常退出

            //应该关闭此Client连接
            ps.StopServerConnet(ProxSocket);

            return;//让方法结束,终结当前接收服务端数据的异步线程
        }

        //接收到的消息处理
        //…………

    }
    return;
}

关闭Client连接方法与原因①的解决方法一样。

③监听方法开启子线程时没有设置为背景线程,子线程在应用结束时没有结束。

假如在开启监听线程时,只是start,没有设置为背景线程,不仅会阻塞主线程,而且有可能会使应用退出后仍会占用端口。

错误示范:

//1实例化socket类,寻址方式,类型,协议。
Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream,ProtocolType.Tcp);
//2实例化端口号
IPEndPoint ip = new IPEndPoint(IPAddress.Any,int.Parse(PortTxt));
//3绑定端口号
try
{
    socket.Bind(ip);
    //4开启监听
    socket.Listen(10);
    //5开始接受客户端连接
    Thread thread=new Thread(new ParameterizedThreadStart(AcceptClientConnet));//持续监听客户端连接方法
    thread.Start(socket);

}
catch (Exception)
{

}

正确示范:

//1实例化socket类,寻址方式,类型,协议。
Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream,ProtocolType.Tcp);
//2实例化端口号
IPEndPoint ip = new IPEndPoint(IPAddress.Any,int.Parse(PortTxt));
//3绑定端口号
try
{
    socket.Bind(ip);
    //4开启监听
    socket.Listen(10);
    //5开始接受客户端连接
    Thread thread=new Thread(new ParameterizedThreadStart(AcceptClientConnet));//持续监听客户端连接方法

    thread.IsBackground=true;//设置为背景线程

    thread.Start(socket);

}
catch (Exception)
{

}

总结

无非就是记得关闭数据流,关闭Client连接,设置背景线程。都是细节。细节往往决定成败啊,我被第二个原因卡了几个星期了才发现。希望能帮到大家。

你可能感兴趣的:(C#,c#,tcp/ip,网络)