Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/sockets/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C#TCP服务器停止接收客户端消息,在服务重新启动时恢复_C#_Sockets_Tcp_Windows Services - Fatal编程技术网

C#TCP服务器停止接收客户端消息,在服务重新启动时恢复

C#TCP服务器停止接收客户端消息,在服务重新启动时恢复,c#,sockets,tcp,windows-services,C#,Sockets,Tcp,Windows Services,我在用C#编写的托管Windows服务中工作。它不断从通过TCP/IP连接的多个客户端接收消息。客户端基本上是一个路由器,它从温度计接收信息并将其重新发送到服务器。服务器解析消息并将其存储在SQL Server数据库中 我面临的问题是,一些客户突然停止发送消息。但是,一旦服务重新启动,它们就会再次连接并恢复发送。我没有客户端的代码,因为它是第三方设备,我很确定问题出在服务器上 我通过实现一个计时器来减少问题,该计时器不断检查每个客户端是否仍然连接(请参阅下面的代码)。另外,我使用Socket.I

我在用C#编写的托管Windows服务中工作。它不断从通过TCP/IP连接的多个客户端接收消息。客户端基本上是一个路由器,它从温度计接收信息并将其重新发送到服务器。服务器解析消息并将其存储在SQL Server数据库中

我面临的问题是,一些客户突然停止发送消息。但是,一旦服务重新启动,它们就会再次连接并恢复发送。我没有客户端的代码,因为它是第三方设备,我很确定问题出在服务器上

我通过实现一个计时器来减少问题,该计时器不断检查每个客户端是否仍然连接(请参阅下面的代码)。另外,我使用
Socket.IOControl(IOControlCode.KeepAliveValues,…)
方法向套接字添加了保持活动模式,但问题仍然存在

我从我认为相关的特定部分发布了一些代码。但是,如果需要更多的片段来理解这个问题,请问我,我会编辑这篇文章。删除了所有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();
        }
    }
}
private Socket\u监听器;
私有ConcurrentDictionary\u连接;
公共TcpServer(TcpServiceProvider提供程序,int端口)
{
此._provider=provider;
这个。_port=port;
此._listener=新套接字(AddressFamily.InterNetwork、SocketType.Stream、ProtocolType.Tcp);
这是._connections=new ConcurrentDictionary();
ConnectionReady=新的异步回调(ConnectionReady\u处理程序);
AcceptConnection=新的WaitCallback(AcceptConnection\u处理程序);
ReceivedDataReady=新的异步回调(ReceivedDataReady\u处理程序);
}                
公共bool Start()
{    
this.\u listener.Bind(新的IPEndPoint(IPAddress.Any,this.\u端口));
这个._listener.Listen(10000);
此._listener.beginacept(ConnectionReady,null);
}
//每5分钟检查一次在过去30分钟内未发送任何消息的客户端
//MSG_RESTART是设备接受以重新启动的命令
私有void CheckForBrokenConnections()
{
foreach(此连接中的var条目)
{
ConnectionState conn=条目.Value;
如果(连接重新连接计数>3)
{
断开连接(conn);
继续;
}
如果(!conn.Connected | |(DateTime.Now-conn.LastResponse).TotalMinutes>30)
{
字节[]消息=HexStringToByteArray(消息重新启动);
if(!conn.WaitingToRestart&&conn.Write(message,0,message.Length))
{
conn.WaitingToRestart=true;
}
其他的
{
断开连接(conn);
}
}
}        
}
私有无效连接就绪\u处理程序(IAsyncResult ar)
{    
锁(这个锁)
{
if(this.\u listener==null)
返回;
ConnectionState ConnectionState=新的ConnectionState();
connectionState.Connection=this.\u listener.EndAccept(ar);
connectionState.Server=this;
connectionState.Provider=(TcpServiceProvider)this.\u Provider.Clone();
connectionState.Buffer=新字节[4];
Util.SetKeepAlive(connectionState.Connection,KEEP_ALIVE_TIME,KEEP_ALIVE_TIME);
int newID=(this.\u connections.Count==0?0:this.\u connections.Max(x=>x.Key))+1;
connectionState.ID=newID;
此.u connections.TryAdd(newID,connectionState);
QueueUserWorkItem(AcceptConnection,connectionState);
此._listener.beginacept(ConnectionReady,null);
}
}
私有无效连接\u处理程序(对象状态)
{    
ConnectionState st=作为ConnectionState的状态;
st.Provider.OnAcceptConnection(st);
如果(圣连接已连接)
st.Connection.BeginReceive(st.Buffer,0,0,SocketFlags.None,ReceivedDataReady,st);
}
私有void ReceivedDataReady\u处理程序(IAsyncResult结果)
{
ConnectionState ConnectionState=null;
锁(这个锁)
{
connectionState=result.AsyncState作为connectionState;
connectionState.Connection.EndReceive(结果);
if(connectionState.Connection.Available==0)
返回;
//这里解析消息
connectionState.Provider.OnReceiveData(connectionState);
if(connectionState.Connection.Connected)
connectionState.Connection.BeginReceive(connectionState.Buffer,0,0,SocketFlags.None,ReceivedDataReady,connectionState);
}
}
内部空心连接(连接状态连接状态)
{
锁(这个锁)
{
if(this._connections.Values.Contains(connectionState))
{
康涅狄格州;
此._connections.TryRemove(connectionState.ID,out conn);
}
if(connectionState.Connection!=null&&connectionState.Connection.Connected)
{
connectionState.Connection.Shutdown(SocketShutdown.Both);
connectionState.Connection.Close();
}
}
}
我想我看到的两件事

  • 如果这是您为多条消息保留的连接,则当
    connectionState.connection.Available==0
    IIRC可以接收到0长度的数据包时,您可能不应该从
    ReceivedDataReady\u处理程序返回。因此,如果连接仍然打开,在离开处理程序之前,应该调用
    connectionState.connection.BeginReceive(…)

  • (我不太愿意把它放在这里,因为我不记得具体细节)有一个事件你可以处理,告诉你什么时候你的基础连接发生了事情,包括连接或关闭连接的错误和失败。我一辈子都记不起这个名字。。。这可能比每隔几秒钟设置一次计时器更有效。它也