下面是創建偵聽Socket對象的方法代碼。
/// <summary>
/// 創建接收服務器的 Socket, 並偵聽客戶端連接請求
/// </summary>
private bool CreateReceiverSocket()
{
try
{
_receiverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
_receiverSocket.Bind(new IPEndPoint(IPAddress.Any, _tcpSocketPort)); // 綁定端口
_receiverSocket.Listen(_maxAllowListenQueueLength); // 開始監聽
return true;
}
catch
{
this.OnReceiverException();
return false;
}
}
3.2 偵聽客戶端連接請求
服務器端循環等待客戶端連接請求。一旦有請求,先判斷客戶端連接數是否超限,接著檢測該客戶端IP地址,一切正常後建立TSession對象,並調用異步方法接收客戶端Socket數據包。
代碼中,Socket讀到數據時的回調AsyncCallback委托方法EndReceiveData()完成數據接收工作,正常情況下啟動另一個異步BeginReceive()調用。
.Net中,每個異步方法都有自己的獨立線程,異步處理其實也基於多線程機制的。下面代碼中的異步套異步調用,既占用較大的系統資源,也給處理帶來意想不到的結果,更是出現異常時難以控制和處理的關鍵所在。
/// <summary>
/// 循環偵聽客戶端請求,由於要用線程池,故帶一個參數
/// </summary>
private void ListenClIEntRequest(object state)
{
Socket clIEnt = null;
while (!_stopReceiver)
{
if (_stopConnectRequest) // 停止客戶端連接請求
{
if (_receiverSocket != null)
{
try
{
_receiverSocket.Close(); // 強制關閉接收器
}
catch
{
this.OnReceiverException();
}
finally
{
// 必須為 null,否則 disposed 對象仍然存在,將引發下面的錯誤
_receiverSocket = null;
}
}
continue;
}
else
{
if (_receiverSocket == null)
{
if (!this.CreateReceiverSocket())
{
continue;
}
}
}
try
{
if (_receiverSocket.Poll(_loopWaitTime, SelectMode.SelectRead))
{
// 頻繁關閉、啟動時,這裡容易產生錯誤(提示套接字只能有一個)
clIEnt = _receiverSocket.Accept();
if (client != null && clIEnt.Connected)
{
if (this._clientCount >= this._maxAllowClIEntCount)
{
this.OnReceiverException();
try
{
clIEnt.Shutdown(SocketShutdown.Both);
clIEnt.Close();
}
catch { }
}
else if (CheckSameClientIP(clIEnt)) // 已存在該 IP 地址
{
try
{
clIEnt.Shutdown(SocketShutdown.Both);
clIEnt.Close();
}
catch { }
}
else
{
TSession session = new TSession(clIEnt);
session.LoginTime = DateTime.Now;
lock (_sessionTable)
{
int preSessionID = session.ID;
while (true)
{
if (_sessionTable.ContainsKey(session.ID)) // 有可能重復該編號
{
session.ID = 100000 + preSessionID;
}
else
{
break;
}
}
_sessionTable.Add(session.ID, session); // 登記該會話客戶端
Interlocked.Increment(ref _clIEntCount);
}
this.OnClIEntRequest();
try // 客戶端連續連接或連接後立即斷開,易在該處產生錯誤,系統忽略之
{
// 開始接受來自該客戶端的數據
session.ClIEntSocket.BeginReceive(session.ReceiveBuffer, 0,
session.ReceiveBufferLength, SocketFlags.None, EndReceiveData, session);
}
catch
{
session.DisconnectType = TDisconnectType.Exception;
session.State = TSessionState.NoReply;
}
}
}
else if (clIEnt != null) // 非空,但沒有連接(connected is false)
{
try
{
clIEnt.Shutdown(SocketShutdown.Both);
clIEnt.Close();
}
catch { }
}
}
}
catch
{
this.OnReceiverException();
if (clIEnt != null)
{
try
{
clIEnt.Shutdown(SocketShutdown.Both);
clIEnt.Close();
}
catch { }
}
}
// 該處可以適當暫停若干毫秒
}
// 該處可以適當暫停若干毫秒
}