.net异步套接字超时检查线程安全

发布于 2024-07-30 02:54:58 字数 2279 浏览 1 评论 0原文

http://msdn.microsoft.com/en-us /library/system.net.sockets.socketasynceventargs.aspx

从上面的 msdn 示例开始,我尝试编写一个超时检查,它将关闭不活动的客户端套接字并释放资源。

这就是我想出的办法。 但我不确定它是否完全线程安全以及是否有更好的方法来做到这一点。 我希望有人能给一些建议。

void IO_Completed(object sender, SocketAsyncEventArgs e)
{
    if (e.SocketError != SocketError.Success)
    {
        CloseClientSocket(e);
        return;
    }

    if (1 < Interlocked.CompareExchange(ref token.Status, 1, 0))
        return;

    switch (e.LastOperation)
    {
        case SocketAsyncOperation.Receive:
            ProcessReceive(e);
            break;
        ...
    }

    token.LastActive = Environment.TickCount;
    Interlocked.CompareExchange(ref token.Status, 0, 1);
}

void ProcessReceive(SocketAsyncEventArgs e)
{
    AsyncUserToken token = (AsyncUserToken)e.UserToken;
    if (e.BytesTransferred > 0)
        if (!token.Socket.SendAsync(e))
            ProcessSend(e);
    else
        CloseClientSocket(e);
}

void ProcessSend(SocketAsyncEventArgs e)
{
    AsyncUserToken token = (AsyncUserToken)e.UserToken;
    if (!token.Socket.ReceiveAsync(e))
        ProcessReceive(e);
}

TimeoutCheck 将每 20 秒执行一次。 allReadWriteArgs 是一个包含所有 SocketAsyncEventArgs 的数组。 关闭套接字后,将通过 SocketError.OperationAborted 调用 IO_Completed。

void TimeoutCheck(object state)
{
    AsyncUserToken token;
    int timeout = Environment.TickCount - 20000;
    for (int i = 0; i < allReadWriteArgs.Length; i++)
    {
        token = (AsyncUserToken)allReadWriteArgs[i].UserToken;
        if (token.LastActive < timeout)
            if (0 == Interlocked.CompareExchange(ref token.Status, 2, 0))
                Interlocked.Exchange(ref token.Socket, null).Close();
    }
}


void CloseClientSocket(SocketAsyncEventArgs e)
{
    AsyncUserToken token = e.UserToken as AsyncUserToken;

    if (token.Socket != null)
    {
        try
        {
            token.Socket.Shutdown(SocketShutdown.Both);
        }
        catch (SocketException) { }
        token.Socket.Close();
    }

    token.Status = 2;
    bufferManager.FreeBuffer(e);
    readWritePool.Push(e);
    ...
}

http://msdn.microsoft.com/en-us/library/system.net.sockets.socketasynceventargs.aspx

Starting with the above msdn example I'm trying to write a timeout check which will close inactive client sockets and free up resources.

This is what I have come up with. But I'm not sure if its completely thread-safe and if there is a better way to do this. I hope someone can give some advices.

void IO_Completed(object sender, SocketAsyncEventArgs e)
{
    if (e.SocketError != SocketError.Success)
    {
        CloseClientSocket(e);
        return;
    }

    if (1 < Interlocked.CompareExchange(ref token.Status, 1, 0))
        return;

    switch (e.LastOperation)
    {
        case SocketAsyncOperation.Receive:
            ProcessReceive(e);
            break;
        ...
    }

    token.LastActive = Environment.TickCount;
    Interlocked.CompareExchange(ref token.Status, 0, 1);
}

void ProcessReceive(SocketAsyncEventArgs e)
{
    AsyncUserToken token = (AsyncUserToken)e.UserToken;
    if (e.BytesTransferred > 0)
        if (!token.Socket.SendAsync(e))
            ProcessSend(e);
    else
        CloseClientSocket(e);
}

void ProcessSend(SocketAsyncEventArgs e)
{
    AsyncUserToken token = (AsyncUserToken)e.UserToken;
    if (!token.Socket.ReceiveAsync(e))
        ProcessReceive(e);
}

TimeoutCheck will execute once each 20 seconds. allReadWriteArgs is an array with all SocketAsyncEventArgs. After closing the socket IO_Completed will be invoked with SocketError.OperationAborted.

void TimeoutCheck(object state)
{
    AsyncUserToken token;
    int timeout = Environment.TickCount - 20000;
    for (int i = 0; i < allReadWriteArgs.Length; i++)
    {
        token = (AsyncUserToken)allReadWriteArgs[i].UserToken;
        if (token.LastActive < timeout)
            if (0 == Interlocked.CompareExchange(ref token.Status, 2, 0))
                Interlocked.Exchange(ref token.Socket, null).Close();
    }
}


void CloseClientSocket(SocketAsyncEventArgs e)
{
    AsyncUserToken token = e.UserToken as AsyncUserToken;

    if (token.Socket != null)
    {
        try
        {
            token.Socket.Shutdown(SocketShutdown.Both);
        }
        catch (SocketException) { }
        token.Socket.Close();
    }

    token.Status = 2;
    bufferManager.FreeBuffer(e);
    readWritePool.Push(e);
    ...
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

爱格式化 2024-08-06 02:54:58

你的代码看起来不错。 你也可以做这样的事情:

void _connectionActivityCheck_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
    _connectionActivityCheck.Stop();

    try
    {
        List<Guid> connectionsToRemove = new List<Guid>();

        lock (_connections.SyncRoot)
        {
            IncomingConnection conn;

            foreach (DictionaryEntry item in _connections)
            {
                conn = (IncomingConnection)item.Value;

                if (conn.LastIncomingActivity.HasValue && 
                    DateTime.Now.Subtract(conn.LastIncomingActivity.Value).TotalSeconds > MaximumInactivitySeconds)
                        connectionsToRemove.Add(conn.ConnectionId);
            }
        }

        if (connectionsToRemove.Count > 0)
        {
            int itemsToRemove = connectionsToRemove.Count;

            foreach (Guid item in connectionsToRemove)
            {
                RemoveConnection(item);
            }

            Context.Current.Logger.LogInfo(_loggerName, 
                string.Format("{0} connections were closed due to incoming traffic inactivity", itemsToRemove));
        }
    }
    catch (Exception ex)
    {
        Context.Current.Logger.LogFatal(_loggerName, "An error ocurred while checking incoming traffic.", ex);
    }
    finally
    {
        _connectionActivityCheck.Start();
    }
}



private void RemoveConnection(Guid connectionId)
{
    lock (_connections.SyncRoot)
    {
        try
        {
            IncomingConnection conn = _connections[connectionId] as IncomingConnection;

            if (conn != null)
            {
                try
                {
                    conn.Dispose();
                }
                catch { }

                _connections.Remove(connectionId);
            }
        }
        catch { }
    }
}

Your code looks good. You can do something like this, as well:

void _connectionActivityCheck_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
    _connectionActivityCheck.Stop();

    try
    {
        List<Guid> connectionsToRemove = new List<Guid>();

        lock (_connections.SyncRoot)
        {
            IncomingConnection conn;

            foreach (DictionaryEntry item in _connections)
            {
                conn = (IncomingConnection)item.Value;

                if (conn.LastIncomingActivity.HasValue && 
                    DateTime.Now.Subtract(conn.LastIncomingActivity.Value).TotalSeconds > MaximumInactivitySeconds)
                        connectionsToRemove.Add(conn.ConnectionId);
            }
        }

        if (connectionsToRemove.Count > 0)
        {
            int itemsToRemove = connectionsToRemove.Count;

            foreach (Guid item in connectionsToRemove)
            {
                RemoveConnection(item);
            }

            Context.Current.Logger.LogInfo(_loggerName, 
                string.Format("{0} connections were closed due to incoming traffic inactivity", itemsToRemove));
        }
    }
    catch (Exception ex)
    {
        Context.Current.Logger.LogFatal(_loggerName, "An error ocurred while checking incoming traffic.", ex);
    }
    finally
    {
        _connectionActivityCheck.Start();
    }
}



private void RemoveConnection(Guid connectionId)
{
    lock (_connections.SyncRoot)
    {
        try
        {
            IncomingConnection conn = _connections[connectionId] as IncomingConnection;

            if (conn != null)
            {
                try
                {
                    conn.Dispose();
                }
                catch { }

                _connections.Remove(connectionId);
            }
        }
        catch { }
    }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文