C# Socket/TCPClient断线重连/不断重连的简单思路+代码,海量注释
前言
最近在写一个透传项目,需要实现一个TCPClient模式的透传。在没有连接上时会去不断发起连接直至连接成功, 还有断连后又会不断发起请求连接,直至再次连接成功。 作为小白,第一反应就是去百度,结果百度搜索出来的,全是CSDN,而且清一色都是上来贴一大堆代码,令人头晕,还一大堆重复的,越看越烦而且搜索无果。
既然没有路,那就由我自己来开辟!
连接成功前进行不断发起请求连接
其实这个功能思路非常简单,无非就是 尝试连接=>连接失败=>重连
(连接成功就跳出)。
用代码写出来:
//创建一个新的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地址), 端口号);//尝试连接}复制代码
而这上面仅是进行一次失败重连,如果再失败怎么办?所以我们要不断重复这个步骤。加一层while循环让它不断进行重连。 代码如下:
//创建一个新的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); }}复制代码
其实这么写,就已经实现了我们初步的功能,客户端会进行不断地连接,直至成功连上服务器。但是,这里有个很严重的问题,如果一直没连上,一直在执行这一步重连,程序会卡死在这里。 所以我们需要额外多开个子线程去执行这一步操作。
//创建一个新的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();//开始执行线程复制代码
即使一直连不上也不会使程序卡死,会一直进行重连直到连上服务器。但是我们的问题还没解决。那就是 循环结束问题。这部分尝试不断连接,while循环的条件是true,无限循环,就会导致即使连上后,虽然break,但是线程没有结束,还是会继续去进行无限循环。我们需要重新设置循环中止条件。
代码如下:
//创建一个新的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();//开始执行线程复制代码
这样就不会再有无限循环的问题了。至此,我们已经完成了功能的一半,这部分已经实现了连接前的不断请求连接,那么连接后又断开呢?很显然,如果在连接后再断开,我们无法进行重连。
另一半就要实现 断线重连。
断线重连
这个思路同样很简单,就是服务器断开->调用连接方法->不断连接
。
连接方法就是我们上一步写过的功能,我们已经实现不断连接了,我们要将上一步的功能封装成一个方法体去调用就可以了。
连接上服务器后,就是个不断接收的过程,所以也需要多开一个线程去不断接收消息。 代码如下:
//注意,这里的开始部分还是上一步的代码,只不过嵌进了方法体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再去重连 } } Thread thread2 = new Thread(new ParameterizedThreadStart(ClientReceiveData));//接收线程方法 thread2.IsBackground = true;//该值指示某个线程是否为后台线程。 thread2.Start(client);//参数是用我们自建的Socket对象,就是上面的Socket client=new…… }); thread.IsBackground = true;//设置为后台线程,在程序退出时自己会自动释放 thread.Start();//开始执行线程}复制代码
这样就是在连接后就会进入不断接收的子线程,接下来写接收程序的代码,与网络上的大同小异,只不过我们会稍作改动,在异常断开和正常退出时都去重新进行连接。Ip地址和端口号可以设置成全局变量,方便进行获取,代码如下:
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);//二进制数组转换成字符串…… }}复制代码
到这里就已经全部实现了!!接下来看看效果吧!!(以本人做的项目做例子)。
本次分享结束,有什么不足的地方,希望大家可以指出,或者不懂的可以留言问我,我们可以多交流!如果想实现图里动态刷新连接状态,可以看我上一篇文章C#LINQ实现动态刷新
2022.11/25 BUG反映
有些小伙伴会反映有以下BUG:
ProxSocet.Shutdown(SocketShutdown.Both);
---------------------
System.Net.Sockets.SocketException:“由于套接字没有连接并且(当使用一个 sendto 调用发送数据报套接字时)没有提供地址,发送或接收数据的请求没有被接受。”
按照这位朋友说的改正就好了!
之前写文章的时候,是直接根据自己已经成功实现的思路去白板徒手写了代码去复现,没有做测试,所以有了意想不到的BUG。感谢各位网友,你们都很强大!
来源地址:https://blog.csdn.net/aa989111337/article/details/127092479
免责声明:
① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。
② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341