0

我这里有很大的问题。我有一个 MVP 应用程序,其中有一个模型可以处理与外部设备通信的实现。在应用程序中,我创建了 4 个此类的实例。每个实例使用唯一的 IP 和端口通过 UDP 套接字与其外部设备进行通信。

现在,对于所有初始测试,一切似乎都很好,因为我有 4 个视图,每个视图都显示了每个唯一设备的计数。对于测试,我总是只连接到一个设备,而其他 3 个连接什么也不做,但是视图总是按预期显示计数为零。

当我开始以编程方式创建单个 log4net 记录器以根据计时器触发记录每个实例的计数时,问题就出现了。我发现每个日志文件都记录了相同的计数!我以为这是我的 log4net 实现,但是在调试时,我在调试器中为自己看到每个实例都返回相同的计数。

当模型的实例被创建时,它们都被传递给一个演示者,该演示者持有对每个模型的引用。每个模型都有一个唯一的字符串标识符,这就是我在调试时知道正在查看哪个实例的方式(除了 IP 和端口)。

另外,我应该注意,计数是在接收到一个未经请求的 UDP 数据包时设置的,该数据包从未出现在 3 个实例上。

我的继承如下。

HeadModel
-HeadModelAbstract
--SubModel
---SubModelAbstract

引用的计数在子模型中。这样做是因为我需要一个类来与具有一组有限功能的基本级设备对话。然而,更高级的设备将具有该功能以及附加功能。我最初想传递接口,但发现这样做会隐藏子模型中的属性和方法,我仍然想公开这些属性和方法,所以我选择了你看到的抽象实现。

感谢所有能够做到这一点并尝试提供帮助的人。

摘要 - 为什么一个类的 4 个唯一实例都为相同的非静态属性返回相同的值?

更新:这是杂项代码

SystemStatus 保存所有计数和状态。ParseSystemStatus 函数从每次接收到 UDP 消息时触发的事件处理程序中调用。

public class SuccintClass : SuccintAbstract{

    UdpServer udpServer = null;

    public SuccintClass(int port){
        udpServer = new UdpServer(port); //Start receiver on port 3000
        udpServer.PacketReceived += client_UnsolicitedMessageReceived;
    }

    private void client_UnsolicitedMessageReceived(object sender, PacketReceivedEventArgs e) {



        ushort msgID = (ushort)((e.Buffer.Data[10]<<8) + e.Buffer.Data[11]);

        byte[] data = new byte[e.Buffer.Length];
        Array.Copy(e.Buffer.Data, 0, data, 0, e.Buffer.Length);

        switch (msgID) {
            case 9001:  
                break;
            case 0x5000:    
                break;
            case 9005:  //System Status
                ParseSystemStatus(data);
                OnSystemStatusReceived(new SystemStatusReceivedEventArgs(this.sysStatus));
                break;
            case 9014:  
                break;
            default:
                break;
        }
    }
    private SystemStatus sysStatus = SystemStatus.NullStatus;

    public void ParseSystemStatus(byte[] buffer) {
        int CounterOffset = 14;
        int[] converted = new int[6];

        for (int i = 0; i < 6; i++) {
            converted[i] = BitConverter.ToInt32(buffer, i * 4 + CounterOffset);
        }

        this.sysStatus.State = (SystemStatus.SystemState)converted[0];
        this.sysStatus.Count1 = converted[1];
        this.sysStatus.Count2= converted[2];
        this.sysStatus.Count3= converted[3];
        this.sysStatus.Count4= converted[4];
        this.sysStatus.Count5= converted[5];
    }
}

然后 UdpServer 看起来像这样

sealed class UdpServer : IDisposable {
    public UdpServer( int serverPort ) {
        this.socket = new Socket( AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp );
        this.socket.Bind( new IPEndPoint( IPAddress.Any, serverPort ) );

        for ( int i = 0; i < 200; i++ ) {
            BeginAsyncReceive( );
        }
    }

    ~UdpServer( ) {
        Dispose( false );
    }

    public void Dispose( ) {
        Dispose( true );
        GC.SuppressFinalize( this );
    }

    private void Dispose( bool isDisposing ) {
        if ( !disposed ) {
            disposed = true;
            rwLock.AcquireWriterLock( Timeout.Infinite );
            socket.Close( );
            rwLock.ReleaseWriterLock( );

            while ( threads > 0 )
                Thread.Sleep( 1 );
        }
    }

    private bool disposed;

    private void BeginAsyncReceive( ) {
        rwLock.AcquireReaderLock( Timeout.Infinite );

        UdpPacketBuffer buffer = UdpPacketBufferPool.GetInstance( ).GetFromPool( );

        try {
            socket.BeginReceiveFrom( buffer.Data, 0, buffer.Data.Length, SocketFlags.None, ref buffer.RemoteEndPoint, EndAsyncReceive, buffer );
            Interlocked.Increment( ref threads );
        }
        catch ( SocketException exc ) {
            if ( logger.IsWarnEnabled ) {
                logger.Warn( "Error happened at the start of packet acquisition." );
                logger.Warn( exc.ToString( ) );
            }
        }
        catch ( ObjectDisposedException exc ) {
        }

        rwLock.ReleaseReaderLock( );
    }

    private void EndAsyncReceive( IAsyncResult asyncResult ) {
        BeginAsyncReceive( );

        rwLock.AcquireReaderLock( Timeout.Infinite );

        UdpPacketBuffer buffer = (UdpPacketBuffer)asyncResult.AsyncState;

        try {
            buffer.Length = socket.EndReceiveFrom( asyncResult, ref buffer.RemoteEndPoint );

            OnPacketReceived( new PacketReceivedEventArgs( buffer ) );
        }
        catch ( SocketException exc ) {
            logger.Warn( "Error happened during completion of packet acquisition." );
            logger.Warn( exc.Message );
            logger.Warn( exc.StackTrace );
        }
        catch ( ObjectDisposedException ) {
        }

        Interlocked.Decrement( ref threads );

        rwLock.ReleaseReaderLock( );
    }

    private void OnPacketReceived( PacketReceivedEventArgs args ) {
        EventHandler<PacketReceivedEventArgs> handler = PacketReceived;

        if ( handler != null ) {
            handler( this, args );
        }
    }

    public event EventHandler<PacketReceivedEventArgs> PacketReceived;

    private int threads;
    private ReaderWriterLock rwLock = new ReaderWriterLock( );
    private Socket socket;

    private ILog logger = LogManager.GetLogger( typeof( UdpServer ) );
}

最后,使用访问 SystemStatus 对象属性的 getter 和 setter 返回计数

override public int Count1 {
        get { return sysStatus.Count1; }
    }

    override public int Count2 {
        get { return sysStatus.Count2 ; }
    }

    override public int Count3 {
        get { return sysStatus.Count3 ; }
    }

    override public int Count4 {
        get { return sysStatus.Count4 ; }
    }

    override public int Count5 {
        get { return sysStatus.Count5 ; }
    }

每个实例都在其自己的唯一端口上启动。我已经在回调函数上设置了断点,用于接收未经请求的消息,但对于除实际用于接收数据的实例之外的任何实例都不会中断,但所有实例都返回相同的计数。

4

1 回答 1

0

初始化 sysStatus 时,将其设置为 SystemStatus.NullStatus,我假设它是一个静态 SystemStatus 实例。此分配更新 sysStatus 变量以指向内存中的 NullStatus 对象 - 它不会复制该对象。当您通过 sysStatus 变量设置值时,它将更新 SystemStatus.NullSTatus 指向的同一对象,该对象是所有实例共享的静态对象。您可能希望将其更改为结构,或使用新的 SystemStatus() 而不是将 sysStatus 指向该静态实例。

编辑:以下是我的原始答案...

我怀疑你的问题的关键在这里:

ParseSystemStatus 函数从每次收到 UDP 消息时触发的事件处理程序中调用

All 3 of your instances are wired up to listen for the PacketReceived event. When the OnPacketReceived method is called and it triggers the event stored in PacketReceived, that will fire off all of the event handlers which are wired up to listen to it. That means the client_UnsolicitedMessageReceived method will be called individually for each instance of your class.

In other words, the way your system is written now, you have 1 UDP server. Every time it receives a packet, it will notify every instance of SuccinctClass that the packet was received. If you want different behavior, you may have to come up with a way to specify which instance you really want to notify, or to detect from within an instance of SuccinctClass whether you really want to act on a particular packet or leave it up to another instance of the class.

于 2012-07-24T13:41:43.787 回答