Merkwürdiges Socket Problem, bei einem nichtveränderbaren Endgerät (Client)



  • Schönen guten Abend 🙂
    Ich arbeite zur Zeit an einem für mich wichtigen Netzwerk Projekt, hierbei geht es um einen Client, der sich via GSM ins Internet einwählt und mit einem Server kommuniziert (TCP). Der Client ist ein fertiges Produkt und ich habe keinerlei Möglichkeit etwas zu verändern.
    Das Projekt soll mit Hilfe von Threads realisiert werden, hierzu soll nach jeder neuen Verbindung ein Thread abgesetzt werden, der eigenständig mit dem jeweiligen Client kommuniziert.

    So weit so gut ^^
    Meine Header Datei:

    // main.h
    #include <iostream>
    #include <conio.h>
    #include <windows.h>
    #include <windowsx.h>//für multithreading beötigt
    #include <stdio.h>//auch für multithreading benötigt
    #include <string>
    #include <winsock2.h>
    #include <winver.h>
    
    #define HOSTNAME "localhost"
    #define PORT_TCP 9999
    
    // PROTOTYPES
    int startWinsock(void);
    DWORD WINAPI handleconn(LPVOID IpParameter);
    
    SOCKET s;
    SOCKET ls;	// listening socket
    SOCKET news;
    SOCKADDR_IN addr;
    SOCKADDR_IN remoteAddr;
    FD_SET fdSet;
    
    int addrlen;
    
    long bytes;
    char buf[256];
    
    // main.cpp
    #include "main.h"
    
    using namespace std;
    
    int main(int argc, char *argv[]) {
    	startWinsock();
    
    	// Socket erstellen
    	ls = socket(AF_INET,SOCK_STREAM,0);
    	if(s==INVALID_SOCKET) {
    		printf("Fehler: Der Socket konnte nicht erstellt werden, fehler code: %d\n",WSAGetLastError());
    		return 1;
    	}
    	else {
        printf("Socket erstellt!\n");
    	}
    
    	// addr vorbereiten
    	memset(&addr,0,sizeof(SOCKADDR_IN));
    	addr.sin_family=AF_INET;
    	addr.sin_port=htons(PORT_TCP);
    	addr.sin_addr.s_addr=INADDR_ANY;
    
    	// bind Socket
    	bytes=bind(ls,(SOCKADDR*)&addr,sizeof(SOCKADDR_IN));
    	if(bytes==SOCKET_ERROR) {
    		printf("Fehler: bind, fehler code: %d\n",WSAGetLastError());
        return 1;
    	}
    	else {
    		printf("Socket an port 9999 gebunden\n");
    	}
    
    	int addrlen = sizeof(addr);
    	bytes=listen(ls,10);
    	while (1) {	// nach verarbeitung aller sockets, auf neue Verbindung warten
    		listen(ls, 2);
    		s = accept(ls, (sockaddr*)&addr, &addrlen);
    		// socket an thread uebergeben
    		DWORD nThreadID;
    		CreateThread(0, 0, handleconn, (void*)s, 0, &nThreadID);	
    	}
    	closesocket(s);
    	closesocket(ls);
    	WSACleanup();
    
    	cout<<"\nend";
    	getch();
    }
    
    DWORD WINAPI handleconn(void *s_) {
    	// Uebergabe
    	SOCKET mys = INVALID_SOCKET;
    	mys = (SOCKET)s_;
    	// Threaddeklarationen
    	long mybytes;
    	char mybuf[256];
    
    	while(1) {
    	// prüfen ob die verbindung geschlossen wurde oder ein fehler auftrat
    		mybytes = recv(mys, mybuf, 256, 0);
    		if(mybytes == 0 || mybytes == SOCKET_ERROR) { cout<<"\nconn&thread has killed!"; closesocket(mys); mys=INVALID_SOCKET; return 0; }else {	// pruefen ob socket noch intakt
    			char tmpbuf[300];
    			mybuf[mybytes]='\0'; // letztes byte terminieren
    			cout<<"\n(at)thread: "<<mybuf;
    
    			// vorbereiten der Antwort
    			sprintf(tmpbuf, "(019891989000AP05)");
    			// reply
    			send(mys,tmpbuf,(int)strlen(tmpbuf),0);
    		}
    
    	}
    
    }
    
    int startWinsock(void) {
      WSADATA wsa;
      return WSAStartup(MAKEWORD(2,0),&wsa);
    }
    

    Wenn ich nun mit dem Gerät kommuniziere funktioniert der Verbindungsaufbau, dann empfängt der Server (alles korrekt bis hierhin), dann aber Antwortet der Server und bekommt das gesendete zurück, anstelle der richtigen Antwort. Eine Möglichkeit ist, das der Client den Befehl nicht kennt und zurück sendet. Das erstaunliche ist, wenn ich den Server mit fdset realisiere funktioniert alles einwandfrei, wie bei diesem Tutorial:
    http://www.c-worker.ch/tuts/select.php

    Ich weiss nicht weiter. Ich hänge an diesem Problem schon etwas länger und bin fuer jede Hilfe mehr als dankbar.

    Ich wünsche noch einen schoenen Abend. 🙂



  • Der Code ist alles andere als sauber. Ein Fehler zu finden wird dadurch nicht einfacher.

    Es kann sein, dass send nicht alles gesendet hat => Rückgabewert überprüfen. Am besten eine SendAll-Funktion schreiben.

    Bei recv liest du übrigens 256 Zeichen ein, aber dein Buffer hat auch nur 256 Zeichen. Sollten also tatsächlich alle 256 Zeichen eingelesen werden, so ist gar kein Platz mehr für das Terminierungszeichen.

    Ansonsten ist der Code nicht sehr umfangreich, probiers also nochmal neu und ordentlich zu machen. Was hast du eigentlich gegen select?



  • Ich sehe, bis auf grausige Formatierung und schlechten Stil, nur ein paar kleinere Fehler, die IMO aber nicht für das von dir beschriebene Verhalten verantwortlich sein können.

    Ich will sie dir aber trotzdem nicht vorenthalten:

    * "if(s==INVALID_SOCKET)" nach "ls = socket(...)" ist falsch, da muss "ls==INVALID_SOCKET" hin

    * bind() liefert keine Byte-Anzahl zurück, der Name "bytes" für die Variable ist also Humbug

    * listen() muss man IIRC nicht wiederholen, 1x listen() reicht

    * Wenn du mit CreateThread() einen Thread erzeugst, solltest du das HANDLE auf diesen auch irgendwo wieder freigeben

    * "closesocket(s)" vor "WSACleanup()" ist falsch, darum kümmert sich ja bereits der Thread. Mal abgesehen davon dass die Accept-Schleife sowieso nie terminiert

    * Wenn du einen Thread mit CreateThread anlegst, und diesem die "Ownership" an einem Socket übergibst, dann solltest du den Fall dass der Thread nicht erzeugt wurde irgendwie handlen. Also in dem Fall dann zumindest den Socket wieder schliessen

    * Klassischer SOCKETS-Anfänger-Fehler: du prüfst bei recv() nicht ob die empfangene Nachricht schon vollständig ist. Ein Socket ist ein Stream, sowas wie Message-Grenzen gibt es da nicht. Anders gesagt: recv() kann auch bloss 1 Byte empfangen, obwohl mehr als 1 Byte auf der anderen Seite weggeschickt wurden. Garantiert ist nur dass recv(), wenn es keinen Fehler bzw. 0 für "Verbindung geschlossen" meldet, mindestens 1 Byte empfängt, um maximal so viele wie du angibst.

    * Sollte recv() tatsächlich 256 Byte empfangen, dann schreibst du bei "mybuf[mybytes]='\0'" ausserhalb des für "mybuf" reservierten Speichers. Entweder du musst 255 an recv() übergeben, oder du musst das Array 257 Bytes gross machen.

    OK, der letzte Punkt könnte evtl. seltsame Dinge geschehen lassen. Schert mich aber grad garnicht mir das genauer durchzudenken WAS da genau passieren könnte. Fix es einfach und guck was passiert 😉

    p.S.: z.T. schlechten Stil: weg mit den globalen Variablen, die braucht kein Mensch. Kann vermutlich alles lokal in main() definiert werden. Variablen immer grundsätzlich so lokal wie möglich und so global wie nötig.
    Und bessere Variablen-Namen könnte das Ding auch vertragen. Dann wäre der 1. von mir genannte Fehler z.B. viel besser sichtbar.
    Tip: Variablen-Namen dürfen auch mal länger als 8 Buchstaben sein



  • Danke fuer eure beiden Antworten. Ich habe die meisten Dinge von deiner Liste bereits geändert. Bin momentan nicht zu Hause würde den überarbeiteten Code sonst hochladen. Ich werde mir nachher mal anschauen, ob bei einem fehlerhaften "Befehl" dieser zurück kommt. Dann weiss ich/ wir, wie er einen nicht bekannten Befehl behandelt.
    Um die Frage zu klären ^^ ich habe ganz und gar nichts gegen die selected(fdset) Umsetzung, jedoch ist die Umsetzung als Thread eine Vorgabe.
    Was mich so erstaunt ist das es mit dem fdset einfach funktioniert...wird der Socket hierbei evtl. anders behandelt.
    Viele Grüße,
    essteeu



  • s schrieb:

    Was mich so erstaunt ist das es mit dem fdset einfach funktioniert...wird der Socket hierbei evtl. anders behandelt.


    du hast vermutlich einfach nen fehler nicht reingebaut den du jetzt drin hast.



  • Mhh, ich habe alle Kriterien umgesetzt (auf Anfrage schicke ich noch den Code, aber sollte eig klar sein). Desweiteren habe ich herausgefunden das bei einem falschen Befehl genau das gleiche Fehlerhalten auftritt, wie es jetzt der Fall ist. Ich weiss nicht mehr weiter, ich gehe an diesem Problem zu Grunde xD
    Wie gesagt per select funktioniert alles, vlt. noch eine Zusatzinformation, dass Gerät ist aus dem asiatischen Raum. Bin für jeden Ratschlag dankbar ^^

    Viele Grüße,
    essteeu

    Edit: Ich habe per Rückgabewert von send geprüft, ob alles übermittelt wird. Ist der Fall 😞 😉


Anmelden zum Antworten