我在使用C#編寫的託管Windows服務中工作。它不斷接收來自通過TCP/IP連接的多個客戶端的消息。客戶端基本上是一個接收並重新發送從溫度計到服務器的消息的路由器。服務器解析消息並將它們存儲在SQL Server數據庫中。C#TCP服務器停止接收客戶端消息,當服務重新啓動時恢復
我面臨的問題是,有些客戶端突然停止發送消息。但是,只要服務重新啓動,它們就會再次連接並恢復發送。我沒有客戶端的代碼,因爲它是第三方設備,我很確定問題出在服務器上。
我設法通過實現一個定時器來持續檢查每個客戶端是否仍然連接(見下面的代碼)以減少問題。此外,我使用socket.IOControl(IOControlCode.KeepAliveValues, ...)
方法向套接字添加了Keep Alive模式,但問題仍在發生。
我發佈了一些我認爲相關的特定部分的代碼。但是,如果需要更多片段來了解問題,請詢問我並編輯帖子。所有的try/catch塊都被刪除,以減少代碼的數量。
我不想要一個完美的解決方案,任何指導將不勝感激。
private Socket _listener;
private ConcurrentDictionary<int, ConnectionState> _connections;
public TcpServer(TcpServiceProvider provider, int port)
{
this._provider = provider;
this._port = port;
this._listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
this._connections = new ConcurrentDictionary<int, ConnectionState>();
ConnectionReady = new AsyncCallback(ConnectionReady_Handler);
AcceptConnection = new WaitCallback(AcceptConnection_Handler);
ReceivedDataReady = new AsyncCallback(ReceivedDataReady_Handler);
}
public bool Start()
{
this._listener.Bind(new IPEndPoint(IPAddress.Any, this._port));
this._listener.Listen(10000);
this._listener.BeginAccept(ConnectionReady, null);
}
// Check every 5 minutes for clients that have not send any message in the past 30 minutes
// MSG_RESTART is a command that the devices accepts to restart
private void CheckForBrokenConnections()
{
foreach (var entry in this._connections)
{
ConnectionState conn = entry.Value;
if (conn.ReconnectAttemptCount > 3)
{
DropConnection(conn);
continue;
}
if (!conn.Connected || (DateTime.Now - conn.LastResponse).TotalMinutes > 30)
{
byte[] message = HexStringToByteArray(MSG_RESTART);
if (!conn.WaitingToRestart && conn.Write(message, 0, message.Length))
{
conn.WaitingToRestart = true;
}
else
{
DropConnection(conn);
}
}
}
}
private void ConnectionReady_Handler(IAsyncResult ar)
{
lock (thisLock)
{
if (this._listener == null)
return;
ConnectionState connectionState = new ConnectionState();
connectionState.Connection = this._listener.EndAccept(ar);
connectionState.Server = this;
connectionState.Provider = (TcpServiceProvider)this._provider.Clone();
connectionState.Buffer = new byte[4];
Util.SetKeepAlive(connectionState.Connection, KEEP_ALIVE_TIME, KEEP_ALIVE_TIME);
int newID = (this._connections.Count == 0 ? 0 : this._connections.Max(x => x.Key)) + 1;
connectionState.ID = newID;
this._connections.TryAdd(newID, connectionState);
ThreadPool.QueueUserWorkItem(AcceptConnection, connectionState);
this._listener.BeginAccept(ConnectionReady, null);
}
}
private void AcceptConnection_Handler(object state)
{
ConnectionState st = state as ConnectionState;
st.Provider.OnAcceptConnection(st);
if (st.Connection.Connected)
st.Connection.BeginReceive(st.Buffer, 0, 0, SocketFlags.None, ReceivedDataReady, st);
}
private void ReceivedDataReady_Handler(IAsyncResult result)
{
ConnectionState connectionState = null;
lock (thisLock)
{
connectionState = result.AsyncState as ConnectionState;
connectionState.Connection.EndReceive(result);
if (connectionState.Connection.Available == 0)
return;
// Here the message is parsed
connectionState.Provider.OnReceiveData(connectionState);
if (connectionState.Connection.Connected)
connectionState.Connection.BeginReceive(connectionState.Buffer, 0, 0, SocketFlags.None, ReceivedDataReady, connectionState);
}
}
internal void DropConnection(ConnectionState connectionState)
{
lock (thisLock)
{
if (this._connections.Values.Contains(connectionState))
{
ConnectionState conn;
this._connections.TryRemove(connectionState.ID, out conn);
}
if (connectionState.Connection != null && connectionState.Connection.Connected)
{
connectionState.Connection.Shutdown(SocketShutdown.Both);
connectionState.Connection.Close();
}
}
}
CheckForBrokenConnections如何觸發? – MarcF 2013-02-15 21:59:26
這是一個'System.Timers。定時器回調,我還沒有發佈啓動它的代碼。我稍後會發布代碼。 – MarcusVinicius 2013-02-16 13:17:50
您的代碼幾乎沒有潛在的錯誤。對於示例,您試圖修改您在迭代時使用的'ConcurrentDictionary'。這行代碼是什麼:ThreadPool.QueueUserWorkItem(AcceptConnection,connectionState)'?另外,你是如何定義'thisLock'?一個錯誤的鎖定對象也會導致併發錯誤。 – YavgenyP 2013-02-27 12:07:13