C#.NET Server轮询多个连接 - 有更好的方法吗?

时间:2010-11-20 21:22:27

标签: c# asynchronous tcp polling

我正在使用.NET C#开发TCP服务器。它使用异步I / O(I / O完成)来同时处理大量客户端。现在我在列表中有所有TCP连接,我不断寻找任何特定连接的状态机的变化。一旦I / O完成设置了某些标志,就会更新给定连接的状态机。

我确信有更好的方法可以做到这一点 - 当前的实现非常耗费处理器,因为我没有阻止等待更新,而是在没有限制的情况下进行轮询。我真的不在乎我的服务器是否在浪费周期,但我猜它的设计很糟糕。我试图找到一种方法来处理特定的连接,只有当I / O完成信号有待处理的东西,并等待(即睡眠)时。任何人都可以建议一个好方法吗?

我认为某些线程同步事情可能会在循环的主线程等待任何I / O完成释放它的地方起作用。但是,有时使用调用线程执行I / O完成(当数据立即可用时等),因此这会导致此解决方案出现问题。

你能提出的任何建议都会非常感激!

这是主线程执行的(简化)循环(rgClient是客户端列表):

//Do communications on each client we currently have connected.
//This loops runs backwards so we can delete elements on the fly
//without have to iterate through more than once.
lock (rgClient)
{
    for (i = rgClient.Count - 1; i >= 0; i--)
    {
        if (!rgClient[i].DoComm())
        {
            rgClient[i].DoClose();
            rgClient.RemoveAt(i);
        }
    }
}

DoComm()在连接的状态机上执行更新,包括执行当前状态的活动,然后在必要时转换到新状态。这是发送简单“ack”数据包的状态类:

class StateAck : State
{
    public StateAck(TextBox txtOutputExt, Form fmOwner)
        : base(txtOutputExt, fmOwner)
    {
        fWriting = false;
    }

    public override bool DoExecute(out Type tpNextState)
    {
        PktAck pkt;

        if (!base.DoExecute(out tpNextState))
        {
            return false;
        }

        //Start a write if we haven't yet
        if (!fWriting)
        {
            pkt = new PktAck();
            fWriting = true;

            return FPutPkt(pkt.rgbSerialize());
        }

        //Is the read finished / has an error occurred?
        if (fDataErrorWrite)
        {
            return false;
        }

        //Process the data
        if (fDataWritten)
        {
            tpNextState = typeof(StateIdle);
        }

        return true;
    }

    private bool fWriting;
}

每次从主线程调用DoComm()时,执行都会通过DoExecute()。 99%的时间,实际上没有发生任何事情。当写入(通过调用FPutPkt()启动)完成时,标志将发出信号,然后将下一个状态设置为“空闲”。我想要做的是,主线程只检查已完成网络活动并且需要更新的客户端,以避免通过DoExecute()进行常量和冗余传递。

1 个答案:

答案 0 :(得分:0)

我找到了一个似乎运作良好的解决方案。使用EventWaitHandler(System.Threading)自动重置到每次循环底部的WaitOne()。然后任何回调或辅助线程都可以通过调用EWH.Set()来通知EventWaitHandler,这将允许循环进行另一次传递。在不对程序流进行重大修改的情况下消除轮询循环的CPU使用率的好方法。希望它可以帮到某人。