Sockets+Threads
-
Hallo,
wie muss ich die Socketfunktionen schützen, dass beim Einsatz von Threads keine Probleme auftauchen?
Situation: Ein Thread ruft ständig die Accept() auf um neue Clients anzunehmen. Wenn ein Client verbunden wurde, läuft für ihn (Serverseitig) ein weiterer Thread, der ständig das Recv() auf dem neuen Socket abfragt. Schließt der Client die Verbindung, erhält das Recv() eine "0" und ich rufe CloseSocket() auf.
Problem: Nach dem CloseSocket() ruft der zweite Thread ebenfalls die Accept() auf.
Das kommt mir sehr ungewöhnlich vor. Ich nutze auch Select() um ein Timeout zu machen. SO_RCVTIMEO funktioniert bei Accept() nicht... oder gibt es ein SO_ACCEPT_TIMEOUT?
Hier nochmal Thread T1
(WHILE) x = Select(Socket1) (x > 0) Socket2 = accept(Socket1) run new thread(Socket2) (else) continue
Thread T2
(while) x = select(Socket2) (x > 0) r = recv(Socket2) (r == 0) CloseSocket(Socket2) !!! ab hier befindet dieser Thread sich im oberen Loop bei Thread T1 !!!
Kann es sein, dass die CodePointer in den SystemFunktionen verwechselt werden?
-
Als Ergänzung:
Wenn ich das Select() vor dem Accept() weg lasse und Accept() damit vollständig blockiert, dann funktioniert es. Leider kann ich ein blockierendes Accapt() nicht gebrauchen.
-
WAS für ein Problem hast Du denn?
Es gibt doch auch Millionen von Beispielen fürt einfache TCP-Server...
-
Du hast meine Schilderung des Problems nicht verstanden?
Ich hab mir auch schon gefühlte millionen von einfachen Beispielen angeschaut. Leider kümmern die sich immer nur um den Verbindungsaufbau, nicht aber um den Verbindungsabbau.
Das Forum hier habe ich auch schon durchstöbert... leider ohne Erfolg.
Also habe ich mich entschieden einen weiteren Thread auf zu machen.Was genau hast du nicht verstanden?
-
Den Satz verstehe ich gar nicht:
!!! ab hier befindet dieser Thread sich im oberen Loop bei Thread T1 !!!
Wie schaffts Du es denn vom Thread x in den Thread 1 zu springen?
Du kannst doch einfach nur das Beispiel hier nehmen und um "accept" eine while-Schrleife bauen... und wo "Client connected" steht Deinen Thread starten....
Also ich habe sozusagen gar nichts verstanden, was Du nicht verstanden hast...
Siehe auch hier:
http://www.codeproject.com/Articles/1891/Beginning-Winsock-Programming-Simple-TCP-server
-
Genau so ist mein Programm ja auch aufgebaut - und da wo ClientConnect steht, starte ich den neuen Thread. Also nach dem accept.
Wenn ich das accept so stehen lasse, funktioniert es ja auch. Weil das accept blockiert. ich will den Server aber auch wieder beenden können. Dazu muss das accept aufhören zu accepten und der socket muss geschlossen werden.
damit ich dem accept sagen kann, "hör auf", starte ich vor dem accept mit select ein timeout. Sobald das Timeout drin ist, habe ich das Problem, dass mein Thread2 nach dem CloseSocket irgendwo im Thread1 weiter macht.
Das ist ja auch das eigentliche Problem: Thread2 befindet sich nach dem close an einer Position im Code an der eigentlich Thread1 ist. Thread2 versucht dann genau wie Thread1 ein accept auf Socket1 zu machen.
Meine Vermutung:
1. Thread2 betritt CloseSocket: und übergibt seine RückSprungAdresse A500 2. Thread1 betritt accept: und übergibt seine RücksprungAdresse A200 !!! damit wird die RückSprungAdresse von Thread2 überschrieben !!! 3. Thread2 ist mit CloseSocket fertig und will weiter machen. Das System gibt ihm die RückSprungAdresse A200, ander eigentlich Thread1 arbeitet. 4. Thread1 ist fertig und macht auch an der Adresse A200 weiter
-
Das ist mir zu hoch....
-
Das Threadsystem von Windows funktioniert bestens, genauso wie WinSock. Dein 2. Thread müsste sich doch beenden, wenn der Client disconnectet ist. Wo ist dann das Problem? Socket freigegeben - Thread beendet. Du kannst theoretisch ein blockierendes Select laufen lassen und den Socket in einem anderen Thread schließen, wenn du den Server beenden willst. Dann kehrt accept() mit einem Fehler zurück und du kannst dir die ganze Timeout-Geschichte sparen. Wenn du Sachen wie AcceptEx() verwendest, kann man die Overlapped-Operationen sogar ganz sauber mit CancelIo() stoppen.
-
matti83 schrieb:
Thread2 befindet sich nach dem close an einer Position im Code an der eigentlich Thread1 ist.
Geh hier bitte mal einen Schritt zurück, und beschreibe, welche konkrete Beobachtung dich zu dieser Schlussfolgerung führt.
-
Hier ein Auszug der Console des Servers
Der AcceptThread hat die ID 800 auf dem Socket 1896
Dann liefert das Accept den Socket 1784 und startet
den Thread 2044.In Zeile 21 und 22 ist Thread 2044 dabei den Socket 1896 zu benutzen
01 Accept: 1896 vvvvvvvvvvvvvvvvvvvv tid: 800 02 ** Hier wurde eine Verbindung aufgebaut – neuer Socket+Thread ** 03 Read: Socket: 1784 END select tid: 2044 04 Read: Socket: 1784 Socket.cpp:407 tid: 2044 05 Accept: 1896 -------------------- tid: 800 06 Accept: 1896 ^^^^^^^^^^^^^^^^^^^^ tid: 800 07 Accept: Socket: 1896 Socket.cpp:218 tid: 800 08 Accept: 1896 vvvvvvvvvvvvvvvvvvvv tid: 800 09 Read: Socket: 1784 END select tid: 2044 10 Read: Socket: 1784 Socket.cpp:407 tid: 2044 11 Accept: 1896 -------------------- tid: 800 12 Accept: 1896 ^^^^^^^^^^^^^^^^^^^^ tid: 800 13 Accept: Socket: 1896 Socket.cpp:218 tid: 800 14 Accept: 1896 vvvvvvvvvvvvvvvvvvvv tid: 800 15 Read: Socket: 1784 END select tid: 2044 16 Read: Socket: 1784 BEG read tid: 2044 17 Read: Socket: 1784 END read tid: 2044 18 ** Hier wurde der Socket geschlossen und Recv gibt 0 zurück ** 19 Read: Socket: 1784 BEG release tid: 2044 20 release Socket: 1784 Socket.cpp:543 tid: 2044 21 Accept: Socket: 1896 Socket.cpp:218 tid: 2044 22 Accept: 1896 vvvvvvvvvvvvvvvvvvvv tid: 2044 23 Accept: 1896 -------------------- tid: 800 24 Accept: 1896 ^^^^^^^^^^^^^^^^^^^^ tid: 800 25 Accept: Socket: 1896 Socket.cpp:218 tid: 800 26 Accept: 1896 vvvvvvvvvvvvvvvvvvvv tid: 800
-
vvvvvvvvvvvvvvvvvv Kommt vor dem Select()
------------------ Kommt vor dem Accept() / Read()
^^^^^^^^^^^^^^^^^^ Kommt nach dem Accept() / Read()
-
Wie gibst Du denn diese Zeile aus? Woher hast Du die ThreadId?
-
cout << "Text" << socket << " tid: " << GetCurrentThreadId() << endl;
-
Dann verwendest Du in Deinem Worker-Thread einen falschen Socket...
-
Okay.. was sagt dir das?
-
wie meinst du denn das?
-
Du hast ein Fehler in Deinem Code!?
-
Da geh ich auch mal von aus, dass ich ein Fehler in meinem Code habe.
Aber ich komm nicht drauf welchen. Dieses Verhalten macht für mich kein Sinn.An der Thread-Sicherheit liegt es also nicht - meinst du.
Der Socket kann auch nicht falsch sein. Ist ja nur ein Integer/Long/FileDescriptor und es geht ja auch alles, bis ich den Client schließe.
Was mich verwundert, ist dass alles prima funktioniert, wenn ich das select() vor dem accept() weg lasse und accept() voll blockiert.
-
struct timeval m_timeout; struct sockaddr_in new_socket_m_addr; memset( &new_socket_m_addr, 0, sizeof( new_socket_m_addr ) ); socklen_t addrlen = sizeof( new_socket_m_addr ); m_timeout.tv_sec = 2; m_timeout.tv_usec = 0; while( true ){ fd_set readfds; FD_ZERO(&readfds); FD_SET(m_sock, &readfds); int ret = 0; ret = select( 0, &readfds, 0, 0, &m_timeout ); if( ret > 0 ){ newSock_sock = accept( m_sock, (struct sockaddr*)&new_socket_m_addr, &addrlen ); return newSock_sock; } }
-
Fang noch mal bei 0 an. Und dann ganz langsam, Schritt für Schritt, voranarbeiten.