3.4 檢查與清理會話
本線程負責處理建立連接後的客戶端會話TSession或Socket對象的關閉與資源清理工作,其它方法中出現異常等情況,盡可能標記相關TSession對象的屬性NoReply=true,表示該會話已經無效、需要清理。
檢查會話隊列並清理資源分3步:第一步,Shutdown()客戶端Socket,此時可能立即觸發某些Socket的異步方法EndReceive();第二步,Close()客戶端Socket,釋放占用資源;第三步,從會話表中清除該會話對象。其中,第一步完成後,某個TSession也許不會立即到第二步,因為可能需要處理其異步結束方法。
需要指出, 由於涉及多線程處理,需要頻繁加解鎖操作,清理工作前先建立一個會話隊列列副本sessionTable2,檢查與清理該隊副本列列的TSession對象。
/// <summary>
/// 檢查客戶端狀態(掃描方式,若長時間無數據,則斷開)
/// </summary>
private void CheckClIEntState(object state)
{
while (!_stopReceiver)
{
DateTime thisTime = DateTime.Now;
// 建立一個副本 ,然後對副本進行操作
Hashtable sessionTable2 = new Hashtable();
lock (_sessionTable)
{
foreach (TSession session in _sessionTable.Values)
{
if (session != null)
{
sessionTable2.Add(session.ID, session);
}
}
}
foreach (TSession session in sessionTable2.Values) // 對副本進行操作
{
Monitor.Enter(session);
try
{
if (session.State == TSessionState.NoReply) // 分三步清除一個 Session
{
session.State = TSessionState.Closing;
if (session.ClIEntSocket != null)
{
try
{
// 第一步:shutdown
session.ClIEntSocket.Shutdown(SocketShutdown.Both);
}
catch { }
}
}
else if (session.State == TSessionState.Closing)
{
session.State = TSessionState.Closed;
if (session.ClIEntSocket != null)
{
try
{
// 第二步: Close
session.ClIEntSocket.Close();
}
catch { }
}
}
else if (session.State == TSessionState.Closed)
{
lock (_sessionTable)
{
// 第三步:remove from table
_sessionTable.Remove(session.ID);
Interlocked.Decrement(ref _clIEntCount);
}
this.OnClIEntRequest();
session.Clear(); // 清空緩沖區
}
else if (session.State == TSessionState.Normal) // 正常的會話
{
TimeSpan ts = thisTime.Subtract(session.LastDataReceivedTime);
if (Math.Abs(ts.TotalSeconds) > _maxSocketDataTimeout) // 超時,則准備斷開連接
{
session.DisconnectType = TDisconnectType.Timeout;
session.State = TSessionState.NoReply; // 標記為將關閉、准備斷開
}
}
}
finally
{
Monitor.Exit(session);
}
} // end foreach
sessionTable2.Clear();
} // end while
}
4 、結語
基於多線程處理的系統代價是比較大的,需要經常調用加/解鎖方法lock()或Monitor.Enter(),需要經常創建處理線程等。從實際運行效果看,筆者的實現方案有較好的穩定性:2005年4月到5月間,在一個普通PC機器上連續運行30多天不出一點故障。同時,筆者采用了時序區間判重等算法,有效地提高了系統處理與響應速度。測試表明,在普通的PC機器(P4 2.0)上,可以做到0.5秒處理一個數據包,如果優化代碼和服務器,還有較大的性能提升空間。
上面的代碼是筆者實現的省級公路交通流量數據服務中心(DSC)項目中的接收服務器框架部分,整個系統還包括:數據轉發交通部的轉發服務器、數據遠程查詢客戶端、綜合報表數據處理系統、數據在線發布系統、系統運行監控系統等。
實際的接收服務器類及其輔助類超過3K行,整個系統則超過了60K。因為是早期實現的程序,難免有代碼粗糙、方法欠妥的感覺,只有留待下個版本完善擴充了。由於與甲方有保密合同和版權保護等,不可能公開全部源代碼,刪減也有不當之處,讀者發現時請不吝指正。下面是帶詳細注釋的代碼下載URL。