基於TCP通信的客戶端斷線重連
轉載:http://www.cnblogs.com/networkcomms/p/4304362.html
在CS程序中,斷線重連應該是一個常見的功能。
此處的斷線重連主要指的是服務器端因為某種故障,服務器端程序或者系統進行了重新啟動,客戶端能夠自動探測到服務器端掉線,並嘗試重新進行連接
本程序基於來自英國的開源c#通信框架的networkcomms(2.3.1版本)
先看一下效果
初始狀態:

當服務器端程序關閉后,客戶端會自動探測到,並在客戶端顯示相關信息

然后,我們設定為每隔5秒重連一次,可以自定義設置重連的次數,比如說重連50次,如果還沒有重連成功,則放棄重連

然后我們重新啟動服務器端,客戶端會顯示重連成功.

具體步驟如下:
需要修改幾處NetworkComms2.3.1通信框架中的代碼
第一步:修改ConnectionInfo類的NoteConnectionShutdown方法
該方法原來是:
internal void NoteConnectionShutdown()
{
lock (internalLocker)
ConnectionState = ConnectionState.Shutdown;
}
修改后為:
private bool reconnectFlag = false;
/// <summary>
/// 是否為重連接模式
/// </summary>
public bool ReconnectFlag
{
get { return reconnectFlag; }
set { reconnectFlag = value; }
}
/// <summary>
/// Note this connection as shutdown
/// </summary>
internal void NoteConnectionShutdown()
{
lock (internalLocker)
ConnectionState = ConnectionState.Shutdown;
//添加以下代碼 初始狀態為False 觸發連接狀態改變事件
if (reconnectFlag == false)
{
StateChanged.Raise(this, new StringEventArgs("連接出現異常"));
}
}
//添加狀態改變事件
public event EventHandler<StringEventArgs> StateChanged;
第二步:在NetworkComms庫類中添加相關的代碼如下:
using System;
using System.Collections.Generic;
using System.Text;
using NetworkCommsDotNet.Tools;
namespace NetworkCommsDotNet
{
public static class Extensions
{
public static void Raise<T>(this EventHandler<T> handler, object sender, T args) where T : EventArgs
{
if (handler != null)
handler(sender, args);
}
}
public class StringEventArgs : EventArgs
{
public StringEventArgs(string text)
{
Text = text;
}
public string Text { get; set; }
}
}
namespace System.Runtime.CompilerServices
{
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class | AttributeTargets.Assembly)]
public sealed class ExtensionAttribute : Attribute { }
}
第三步:在NetworkComms靜態類中添加如下方法:
public static void ClearDic()
{
lock (globalDictAndDelegateLocker)
{
allConnectionsById.Clear();
allConnectionsByEndPoint.Clear();
oldNetworkIdentifierToConnectionInfo.Clear();
}
}
如果您使用的是V3版本,代碼稍微變化:
public static void ClearDic()
{
lock (globalDictAndDelegateLocker)
{
allConnectionsByIdentifier.Clear();
allConnectionsByEndPoint.Clear();
oldNetworkIdentifierToConnectionInfo.Clear();
}
}
客戶端代碼:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using NetworkCommsDotNet;
using DPSBase;
using System.Net;
using System.Threading;
namespace AppClient
{
public partial class Form1 : Form
{
//連接信息類
public ConnectionInfo connnectionInfo = null;
//連接類
Connection connection;
public Form1()
{
InitializeComponent();
}
//在窗體上顯示新信息
void Form_ConnectionStatusNotify(object sender, StringEventArgs e)
{
if (this.InvokeRequired)
{
this.Invoke(new EventHandler<StringEventArgs>(this.Form_ConnectionStatusNotify), sender, e);
}
else
{
lblLink.Text = e.Text;
lblLink.ForeColor = Color.Blue;
}
}
private bool ServerNotifyClose = false;
public event EventHandler<StringEventArgs> ConnectionStatusNotify;
void connnectionInfo_StateChanged(object sender, StringEventArgs e)
{
//如果不是服務器通知關閉,則自動重連,如果是服務器通知關閉,則不作處理
//本Demo中沒有使用ServerNotifyClose
if (ServerNotifyClose == false)
{
//更新連接信息類 設置為重連模式
connnectionInfo.ReconnectFlag = true;
ConnectionStatusNotify.Raise(this, new StringEventArgs("可能由於服務器的故障,與服務器端的連接已斷開"));
int num = 0;
int retryCount = 30;
int retrySpanInMSecs = 5000;
do
{
try
{
NetworkComms.ClearDic();
connection = TCPConnection.GetConnection(connnectionInfo);
ConnectionStatusNotify.Raise(this, new StringEventArgs("重連成功"));
connnectionInfo.ReconnectFlag = false;
break;
}
catch (Exception ex)
{
num++;
if (num < retryCount)
{
ConnectionStatusNotify.Raise(this, new StringEventArgs("正在進行第" + num + "次重連"));
Thread.Sleep(retrySpanInMSecs);
}
}
}
while (num < retryCount);
}
}
private void button1_Click(object sender, EventArgs e)
{
connnectionInfo = new ConnectionInfo(txtIP.Text, int.Parse(txtPort.Text));
//如果不成功,會彈出異常信息
connection = TCPConnection.GetConnection(connnectionInfo);
button1.Enabled = false;
button1.Text = "連接成功";
//訂閱連接信息類中的連接狀態改變事件
connnectionInfo.StateChanged += new EventHandler<StringEventArgs>(connnectionInfo_StateChanged);
this.ConnectionStatusNotify += new EventHandler<StringEventArgs>(Form_ConnectionStatusNotify);
}
//獲取水果相關信息
private void button2_Click(object sender, EventArgs e)
{
if (listBox1.SelectedIndex > -1)
{
string resMsg = connection.SendReceiveObject<string>("ReqFruitEngName", "ResFruitEngName", 5000, listBox1.Text);
MessageBox.Show("您選擇的水果的英文名稱是:" + resMsg);
}
else
{
MessageBox.Show("請選擇一項");
}
}
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
this.Dispose();
}
}
}
服務器端無需額外的設置。

