C# Socket/TCPClient disconnection reconnection/continuous reconnection simple idea + code, massive comments

Preface


I am currently writing a transparent transmission project, which requires the implementation of a transparent transmission in TCPClient mode. When there is no connection, it will continue to initiate connections until the connection is successful. After the connection is disconnected, it will continue to initiate connection requests until the connection is successful again . As a novice, my first reaction was to go to Baidu. As a result, the search results on Baidu were all about CSDN, and they all posted a lot of code, which was dizzying. There were also a lot of duplicates. The more I looked, the more annoying it became, and the search was useless. fruit.

Since there is no road, I will open it myself!


Continuously initiate connection requests before the connection is successful.

In fact, the idea of ​​this function is very simple, nothing more than 尝试连接=>连接失败=>重连(jumping out when the connection is successful).

Write it out in code:

//创建一个新的Socket对象
Socket client=new Socket(AddressFamily.InterNetwork,SocketType.Stream, ProtocolType.Tcp)
try
{
    client.Connect(IPAddress.Parse(IP地址), 端口号);//尝试连接
}
catch
{
    client.Close();//先关闭
    /*使用新的客户端资源覆盖,上一个已经废弃。如果继续使用以前的资源进行连接,即使参数正确,
    服务器全部打开也会无法连接*/
    client=new Socket(AddressFamily.InterNetwork,SocketType.Stream, ProtocolType.Tcp);
    client.Connect(IPAddress.Parse(IP地址), 端口号);//尝试连接
}

复制代码

And this is just a failed reconnection. What if it fails again? So we have to keep repeating this step. Add a while loop to allow it to reconnect continuously. code show as below:

//创建一个新的Socket对象
Socket client=new Socket(AddressFamily.InterNetwork,SocketType.Stream, ProtocolType.Tcp)
while(true)//无限循环
{
    try
    {
        client.Connect(IPAddress.Parse(IP地址), 端口号);//尝试连接,失败则会跳去catch
        break;//在此处加上break,成功就跳出循环,避免死循环
    }
    catch
    {
        client.Close();//先关闭
        /*使用新的客户端资源覆盖,上一个已经废弃。如果继续使用以前的资源进行连接,即使参数正确,
        服务器全部打开也会无法连接*/
        client=new Socket(AddressFamily.InterNetwork,SocketType.Stream, ProtocolType.Tcp);
    }
}

复制代码

In fact, by writing like this, our preliminary function has been realized. The client will continue to connect until it successfully connects to the server. However, there is a very serious problem here. If you are not connected and continue to perform this step to reconnect, the program will get stuck here . So we need to open additional sub-threads to perform this step.

//创建一个新的Socket对象
Socket client=new Socket(AddressFamily.InterNetwork,SocketType.Stream, ProtocolType.Tcp)
//将方法写进线程中
Thread thread=new Thread(() =>
{
    while(true)//无限循环
    {
        try
        {
            client.Connect(IPAddress.Parse(IP地址), 端口号);//尝试连接,失败则会跳去catch
            break;//在此处加上break,成功就跳出循环,避免死循环
        }
        catch
        {
            client.Close();//先关闭
            /*使用新的客户端资源覆盖,上一个已经废弃。如果继续使用以前的资源进行连接,
            即使参数正确, 服务器全部打开也会无法连接*/
            client=new Socket(AddressFamily.InterNetwork,SocketType.Stream, ProtocolType.Tcp);
            Thread.Sleep(1000);//等待1s再去重连
        }
    }
});
thread.IsBackground = true;//设置为后台线程,在程序退出时自己会自动释放
thread.Start();//开始执行线程
复制代码

Even if it is unable to connect, the program will not freeze and it will continue to reconnect until it connects to the server. But our problem is not solved yet. That's the end-of-cycle problem. This part tries to connect continuously. The condition of the while loop is true and the infinite loop will cause even after the connection is connected, although it breaks, the thread does not end and the infinite loop will continue. We need to reset the loop termination condition.

code show as below:

//创建一个新的Socket对象
Socket client=new Socket(AddressFamily.InterNetwork,SocketType.Stream, ProtocolType.Tcp)
public static bool IsConnet=true;//判断是否成功连接,设置为全局变量,方便随时控制
//将方法写进线程中
Thread thread=new Thread(() =>
{
    while(IsConnet)//循环
    {
        try
        {
            client.Connect(IPAddress.Parse(IP地址), 端口号);//尝试连接,失败则会跳去catch
            IsConnet=false;//成功连接后修改bool值为false,这样下一步循环就不再执行。
            break;//在此处加上break,成功就跳出循环,避免死循环
        }
        catch
        {
            client.Close();//先关闭
            /*使用新的客户端资源覆盖,上一个已经废弃。如果继续使用以前的资源进行连接,
            即使参数正确, 服务器全部打开也会无法连接*/
            client=new Socket(AddressFamily.InterNetwork,SocketType.Stream, ProtocolType.Tcp);
            Thread.Sleep(1000);//等待1s再去重连
        }
    }
});
thread.IsBackground = true;//设置为后台线程,在程序退出时自己会自动释放
thread.Start();//开始执行线程
复制代码

This way there will no longer be the problem of infinite loops. At this point, we have completed half of the function. This part has implemented the continuous request for connection before connection, but what about disconnection after connection? Obviously, if we connect and then disconnect, we cannot reconnect.
The other half needs to be disconnected and reconnected.


Disconnect and reconnect

This idea is also very simple, that is 服务器断开->调用连接方法->不断连接.
The connection method is the function we wrote in the previous step. We have implemented continuous connection. We just need to encapsulate the function in the previous step into a method body and call it.
After connecting to the server, it is a continuous receiving process, so it is also necessary to open an additional thread to continuously receive messages. code show as below:

//注意,这里的开始部分还是上一步的代码,只不过嵌进了方法体
public void Connet(string Iptxt,int Port)//接收参数是目标ip地址和目标端口号。客户端无须关心本地端口号
{
    //创建一个新的Socket对象
    Socket client=new Socket(AddressFamily.InterNetwork,SocketType.Stream, ProtocolType.Tcp)
    IsConnet=true;//注意,此处是全局变量,将其设置为true
    //将方法写进线程中
    Thread thread=new Thread(() =>
    {
        while(IsConnet)//循环
        {
            try
            {
                client.Connect(IPAddress.Parse(Iptxt), Port);//尝试连接,失败则会跳去catch
                IsConnet=false;//成功连接后修改bool值为false,这样下一步循环就不再执行。
                break;//在此处加上break,成功就跳出循环,避免死循环
            }
            catch
            {
                client.Close();//先关闭
                /*使用新的客户端资源覆盖,上一个已经废弃。如果继续使用以前的资源进行连接,
                即使参数正确, 服务器全部打开也会无法连接*/
                client=new Socket(AddressFamily.InterNetwork,SocketType.Stream, ProtocolType.Tcp);
                Thread.Sleep(1000);//等待1s再去重连
            }
        }
        /*这里不一样就是放接收线程,在连接上后break出来,执行。
        因为需要带参数,所以要用到特别的ParameterizedThreadStart,
        然后开始线程。↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓*/
        Thread thread2 = new Thread(new ParameterizedThreadStart(ClientReceiveData));//接收线程方法
        thread2.IsBackground = true;//该值指示某个线程是否为后台线程。
        thread2.Start(client);//参数是用我们自建的Socket对象,就是上面的Socket client=new……
        
    });
    thread.IsBackground = true;//设置为后台线程,在程序退出时自己会自动释放
    thread.Start();//开始执行线程
}
复制代码

In this way, after connecting, it will enter the sub-thread that continues to receive. Next, write the code of the receiving program, which is similar to that on the network, except that we will make slight changes to reconnect when abnormal disconnection and normal exit . The IP address and port number can be set as global variables for easy access. The code is as follows:

 public void ClientReceiveData(object socket)//TCPClient消息的方法
{
    var ProxSocket = socket as Socket;//处理上一步传过来的Socket函数
    byte[] data = new byte[1024 * 1024];//接收消息的缓冲区
    while (!IsConnet)//同样循环中止的条件
    {
        int len = 0;//记录消息长度,以及判断是否连接
        try
        {
            //连接函数Receive会将数据放入data,从0开始放,之后返回数据长度。
            len = ProxSocket.Receive(data, 0, data.Length, SocketFlags.None);
        }
        catch (Exception)
        {
            //异常退出
            ProxSocet.ShutDown(SocketShutdown.Both);//中止传输
            ProxSocet.Close();//关闭
            Connet(ip地址,端口号);//重新尝试去连接
            IsConnet=false;//注意,此处是全局变量,将其设置为false,防止循环
            return;//让方法结束,终结当前接收服务端数据的异步线程
        }
        if (len <= 0)
        {
            //如果小于0,证明无连接,服务端正常退出
            ProxSocet.ShutDown(SocketShutdown.Both);//中止传输
            ProxSocet.Close();//关闭
            Connet(ip地址,端口号);//重新尝试去连接
            IsConnet=false;//注意,此处是全局变量,将其设置为false,防止循环
            return;//让方法结束,终结当前接收服务端数据的异步线程
        }
        //这里做你想要对消息做的处理
        //string str = Encoding.Default.GetString(data, 0, len);//二进制数组转换成字符串……
    }
}
复制代码

Everything has been implemented here!! Let’s take a look at the effect!! (Take the project I did as an example).

At the end of this sharing, if there are any shortcomings, I hope you can point them out. If you don’t understand, you can leave a message and ask me. We can communicate more! If you want to dynamically refresh the connection status in the picture, you can read my previous article C#LINQ to achieve dynamic refresh

2022.11/25 BUG report


Some friends will report the following BUG:

ProxSocet.Shutdown(SocketShutdown.Both);
---------------------
System.Net.Sockets.SocketException: "Because the socket is not connected and (when using a When calling sendto to send a datagram socket) no address was provided and the request to send or receive data was not accepted."

Just correct it as this friend said!

 

When I wrote the article before, I directly wrote the code on the whiteboard to reproduce it based on the idea that I had successfully implemented. I did not do any testing, so there were unexpected bugs. Thank you netizens, you are all powerful!

Guess you like

Origin blog.csdn.net/aa989111337/article/details/127092479