.NET Framework Ricevitore di messaggi asincroni


Esempio

using System;
using System.Collections.Generic;
using System.IO.Ports;
using System.Text;
using System.Threading;

namespace AsyncReceiver
{
    class Program
    {
        const byte STX = 0x02;
        const byte ETX = 0x03;
        const byte ACK = 0x06;
        const byte NAK = 0x15;
        static ManualResetEvent terminateService = new ManualResetEvent(false);
        static readonly object eventLock = new object();
        static List<byte> unprocessedBuffer = null;

        static void Main(string[] args)
        {
            try
            {
                var serialPort = new SerialPort("COM11", 9600, Parity.Even, 8, StopBits.One);
                serialPort.DataReceived += DataReceivedHandler;
                serialPort.ErrorReceived += ErrorReceivedHandler;
                serialPort.Open();
                terminateService.WaitOne();
                serialPort.Close();
            }
            catch (Exception e)
            {
                Console.WriteLine("Exception occurred: {0}", e.Message);
            }
            Console.ReadKey();
        }

        static void DataReceivedHandler(object sender, SerialDataReceivedEventArgs e)
        {
            lock (eventLock)
            {
                byte[] buffer = new byte[4096];
                switch (e.EventType)
                {
                    case SerialData.Chars:
                        var port = (SerialPort)sender;
                        int bytesToRead = port.BytesToRead;
                        if (bytesToRead > buffer.Length)
                            Array.Resize(ref buffer, bytesToRead);
                        int bytesRead = port.Read(buffer, 0, bytesToRead);
                        ProcessBuffer(buffer, bytesRead);
                        break;
                    case SerialData.Eof:
                        terminateService.Set();
                        break;
                }
            }
        }
        static void ErrorReceivedHandler(object sender, SerialErrorReceivedEventArgs e)
        {
            lock (eventLock)
                if (e.EventType == SerialError.TXFull)
                {
                    Console.WriteLine("Error: TXFull. Can't handle this!");
                    terminateService.Set();
                }
                else
                {
                    Console.WriteLine("Error: {0}. Resetting everything", e.EventType);
                    var port = (SerialPort)sender;
                    port.DiscardInBuffer();
                    port.DiscardOutBuffer();
                    unprocessedBuffer = null;
                    port.Write(new byte[] { NAK }, 0, 1);
                }
        }

        static void ProcessBuffer(byte[] buffer, int length)
        {
            List<byte> message = unprocessedBuffer;
            for (int i = 0; i < length; i++)
                if (buffer[i] == ETX)
                {
                    if (message != null)
                    {
                        Console.WriteLine("MessageReceived: {0}", 
                            Encoding.ASCII.GetString(message.ToArray()));
                        message = null;
                    }
                }
                else if (buffer[i] == STX)
                    message = null;
                else if (message != null)
                    message.Add(buffer[i]);
            unprocessedBuffer = message;
        }
    }
}

Questo programma attende i messaggi racchiusi tra byte STX ed ETX e restituisce il testo che si frappone tra loro. Tutto il resto viene scartato. In caso di overflow del buffer di scrittura, si interrompe. Su altri errori resetta i buffer di input e output e attende ulteriori messaggi.

Il codice illustra:

  • Lettura della porta seriale asincrona (vedi Uso SerialPort.DataReceived ).
  • Elaborazione dell'errore della porta seriale (vedere Uso SerialPort.ErrorReceived ).
  • Implementazione del protocollo basato su messaggi non testuali.
  • Lettura parziale dei messaggi
    • L'evento SerialPort.DataReceived potrebbe verificarsi prima dell'intero messaggio (fino a ETX ). L'intero messaggio potrebbe anche non essere disponibile nel buffer di input (SerialPort.Read (..., ..., port.BytesToRead) legge solo una parte del messaggio). In questo caso inseriamo la parte ricevuta ( unprocessedBuffer ) e continuiamo ad aspettare ulteriori dati.
  • Trattare con diversi messaggi in arrivo.
    • L'evento SerialPort.DataReceived può verificarsi solo dopo che diversi messaggi sono stati inviati dall'altra parte.