Poll in Windows



  • Hallo zusammen,

    ich versuche eine Serveranwendung umzuschreiben. Sie hat zum einen mehrere TCP Verbindungen und zum anderen mehrere Serielle Schnittstellen.

    Bisher läuft das Programm unter Linux. Jede Verbindung egal ob seriell oder TCP wurde als file descriptor in einem Array gespeichert und mit Poll auf eingehende Nachrichten überwacht. Eine schöne und saubere Sache.

    Jetzt möchte ich das ganze in Windows nachbilden. Ich bin erst mal auf Select umgestiegen. Für TCP kann ich dort auch die file descriptor in einem Array ablegen und mit select auf eingehende Verbindungen abfragen. Klappt wunderbar. Aber dann wollte ich auch die seriellen Schnittstellen mit abfragen. Ich habe die COM Ports aber mit CreateFile geöffnet und nun kein file descriptor sondern ein HANDLE. Mein erster Ansatz war mit _open_osfhandle aus einem HANDLE ein file descriptor zu erzeugen. Der liefert mir auch einen Wert (z.B. 3) zurück. Füge ich diesen jedoch dem Select hinzu, bekomme ich immer:

    WSAENOTSOCK
    10038 Socket operation on nonsocket.

    Das funktioniert also anscheinend nicht so einfach wie ich es mir gedacht habe.

    Mein nächster Ansatz war mir für die Überwachung der Seriellen Ports parallele Thread zu erstellen und die mir ReadFile auf eingehende Daten warten. Leider kann ich nun nicht mehr parallel WriteFile aufrufen, da dieser dann auf das beenden von ReadFile wartet. Auch ein vorheriges CancelIO hat nicht geholfen. Selbst ein CloseHandle blockiert und wartet wohl auf ReadFile.

    Was kann ich da machen? Muss ich da mit diesen Overlapped Funktionen ReadFileEx arbeiten? Die scheinen mir doch eine ganze Ecke komplizierter.

    Hoffe ihr habt damit mehr Erfahrung in ein paar Tips, wie es am einfachsten geht.

    Michael





  • Der Link ist ja ganz interessant aber hilft mir jetzt auch nicht weiter. Mein Server hat 3 TCP und 2 Serielle Schnittstellen, der Traffic ist absolut gering.

    Mir ist also vollkommen egal welche Strategie, hauptsache es funktioniert. Und da helfen mir die Heuristiken auch nicht weiter. Dort steht vermeide select(). Okay ich habe bereits select eingebaut, tut mir auch nicht weh. Und Low Traffic Server können Threads benutzen. Okay, habe ich gemacht, funktioniert aber ja wie gesagt nicht. Was nun?



  • Ich finde aber auch im Netz nichts wo mal jemand in C mehrere serielle Ports überwacht. Sockets verstehe ich ja noch irgendwie aber diese Handles ... 😞



  • Wenn du overlapped machst, kannst du mit WaitCommEvent auf Daten warten, soweit ich mich erinnere. Such dir mal ein Beispiel zu WaitCommEvent raus, das ist nicht unkompliziert und sollte auf jeden Fall korrekt implementiert werden.



  • nicht unkompliziert

    Also kompliziert.



  • knivil schrieb:

    Also kompliziert.

    Richtig, aber ich habe es cooler ausgedrückt. 🕶

    Oder komplizierter ausgedrückt. :p



  • Also das ist tatsächlich kompliziert, was in Linux ein einfaches read(...) ist, ist nun ein riesges Konstrukt.

    Ich habe also OVERLAPPED aktiviert.

    serial_port[aPort] = CreateFile(ser_devices[aPort].devicename, GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, 0);
    

    Nun habe ich

    ReadFile(serial_port[aPort], rx_message, BUFSIZE, &len, &ov))
    

    aufgerufen. Die kehrt natürlich sofort zurück. Liefert ReadFile TRUE zurück, lagen Daten bereit die direkt ausgelesen wurden. Liegen keine Daten zurück, wird FALSE zurück geliefert. Im Normalfall sollte nun GetLastError() auf ERROR_IO_PENDING stehen. Er wartet also auf eingehende Daten.

    Also warte ich nun auf eingehende Daten:

    GetOverlappedResult(serial_port[aPort], &ov, &len, TRUE))
    

    Ich habe hier das wait Flag auf TRUE gesetzt. Damit wartet er an der Stelle. Was okay ist da ich einen eigenen Thread fürs lesen habe.

    Nun hatte ich das Problem, dass auch dann nicht zurück gekehrt ist, wenn er bereits einen Teil eingelesen hat, der Buffer aber noch nicht voll ist. Also weniger als BUFSIZE Daten empfangen wurden. Daher habe ich Timeouts gesetzt:

    COMMTIMEOUTS tTimeout;
        tTimeout.ReadIntervalTimeout = MAXWORD;
        tTimeout.ReadTotalTimeoutMultiplier = 0;
        tTimeout.ReadTotalTimeoutConstant = 500; 
        tTimeout.WriteTotalTimeoutMultiplier = 0;
        tTimeout.WriteTotalTimeoutConstant = 0;
    
        // config the timeout
        if( !SetCommTimeouts(serial_port[aPort],&tTimeout) )
        {
            //error setting serial port state
    		fprintf(stderr, "Error: Setting serial port timeouts of serial device '%s'\n", ser_devices[aPort].devicename);
    		return INVALID_HANDLE_VALUE;
        }
    

    Jetzt hat er aber andauernd abgebrochen und erneut ReadFile aufgerufen. Daher habe ich noch ein Event definiert:

    SetCommMask(serial_port[aPort], EV_RXCHAR);
        WaitCommEvent(serial_port[aPort], &evt, &ov);
    

    Leider geht durch Overlapped auch WaitComm sofort mit FALSE zurück wenn keine Daten anliegen mit getLastError auf ERROR_IO_PENDING. Daher habe ich noch ein

    WaitForSingleObject(ov.hEvent,INFINITE);
    

    aufgerufen. Das kehrt erst zurück wenn wirklich Daten anliegen.

    Kommt mir ja alles sehr sehr sehr umständlich vor, aber zumindestens läuft das. Habt ihr noch Anmerkungen? Ansonsten hilft dieser Thread vielleicht mal jemanden.

    Hier noch der gesamte Code:

    DWORD len = 0;
        DWORD evt;
        HANDLE hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
    
        OVERLAPPED ov = {0};
    
        ov.hEvent = hEvent;
    
        // Wait for incomming data on serial port
        SetCommMask(serial_port[aPort], EV_RXCHAR);
        if (!WaitCommEvent(serial_port[aPort], &evt, &ov))
        {
            if (GetLastError() != ERROR_IO_PENDING)
            {
                system_log (0, "serial_read", "Error: WaitCommEvent failed on serial-port (%d)", aPort);
                receivedBytes = -1;
            }
            else
            {
                // Wait for EV_RXCHAR event
                WaitForSingleObject(ov.hEvent,INFINITE);
            }
        }
    
        if(!ReadFile(serial_port[aPort], rx_message, BUFSIZE, &len, &ov))
        {
            if (GetLastError() != ERROR_IO_PENDING)
            {
                system_log (0, "serial_read", "Error: Reading serial-port (%d)", aPort);
                receivedBytes = -1;
            }
            else
            {
                // Wait for the operation to complete before continuing.
                if (GetOverlappedResult(serial_port[aPort], &ov, &len, TRUE))
                    receivedBytes = (int) len;
                else
                    receivedBytes  = -1;
            }
        }
        else
        {
            // Operation has completed immediately.
            receivedBytes = (int) len;
        }
    

    Und ein Link der ganz hilfreich ist:

    http://www.codeproject.com/Articles/2682/Serial-Communication-in-Windows

    Danke an euch!


Anmelden zum Antworten