Stream-Socket Kommunikation
-
Hallo,
ich bin ein Novize in C und neu hier im Forum - entschuldigt, wenn ich das falsche Forum schreibe.
Ich habe eine Servre-Client-Verbinug zwischen zwei Rechnern hergestellt, nun sollen sich beide gegenseitig Benutzereingaben zuschicken (...ja, ist ein Chat - zur Uebung). Socketinitialisierung und Verbindung klappt, einzelne Nachrichten senden auch noch, aber wenn beide empfangen und senden sollen - funktioniert es nicht. Hier der Code, den der Klient und Server enthealt:
Ueber socket_get wird gesendet und empfangen. new_addr ist das struct vom verbundenen Rechner und my_addr ist struct des Lokalen. Der Code-Schnippsel ist in einer do-while-Schleife eingebunden.FD_ZERO(&readfds); //eventuell ueberfluessig (?) FD_SET(sock_get, &readfds); select(sock_get+1, &readfds, NULL, NULL, NULL); if (FD_ISSET(sock_get, &readfds)) { recv(sock_get, get, MAXDATA-1, 0); printf("%s> %s\n", inet_ntoa(new_addr.sin_addr), get); } if (FD_ISSET(sock_get, &writefds)) { printf("%s> ", inet_ntoa(my_addr.sin_addr)); fgets(put, sizeof(put), stdin); send(sock_get, put, sizeof(put), 0); }
Meine Ueberlegung ist, dass wenn sock_get nicht in readfds ist, es keine Nachricht beinhaltet. So kann ich recv() ueberspringen, und des lokalen Rechners Nachricht verschicken. Wenn ich nun von einem Rechner eine Nachricht schicken wuerde, sollte der andere Rechner bei select() feststellen, dass sich das Socket im readable-Zustand befindet - also die Nachricht annehmen.
In der Realiteat gehen beide Rechner eine Verbindung ein und ueberhaupt keine Nachricht kommt an. (Theorie und Praxis ...) Ich denke ich habe etwas mit select() falsch verstanden.Ich bin fuer jede Hilfe im Vorraus dankbar...
Gruss Caspar
-
zuerst: benutzt du bei select mit absicht kein timeout?
dann fällt mir auf: wie soll denn sein socket merken, ob er mit senden dran ist? die methode die du anwendest, passt vielmehr, wenn man auf mehreren sockets hört, ob sich wo was tut. ich meine damit, dass du dir den teil, wo du sendest sparen kannst. würde viel mehr einen normalen lese-loop machen, und bei bedarf senden
-
Korbinian schrieb:
...einen normalen lese-loop machen, und bei bedarf senden
while (put != "quit") { do { recvd = recv(sock_get, get, MAXDATA-1, 0); printf ("> %s", get); } while (recv(sock_get, get, MAXDATA-1, 0) != "0"); if (fgets(put, sizeof(put), stdin) != 0) { send(sock_get, p_put, sizeof(put), 0); } }
...das habe ich geschrieben. ich weiss nicht recht, wie ich deine, mir einleuchtenden worte, umsetzen soll. das problem ist, dass ich nicht weiss, woran ich erkenne, dass der andere rechner nicht sendet. daher ist die while-schleife mit recv() in der bedingung auch immer war [0 wird gesendet wenn es keine verbindung mehr gibt].
zum anderen weiss ich nicht, woran ich erkenne, wenn etwas eingetippt wurde. ich brauche dir sicherlich nicht erzaehlen, dass fgets immer wahr ist, denn um den ausdruck if (fgets... ) auszuwerten muss fgets beendet werden - das geschieht mit einer eingabebestaetigung, ueblicherweise enter-taste und somit habe ich etwas eingegeben - die bedingung ist immer wahr und waehrenddessen kann nicht empfangen werden.so kommt es, dass ich erst schreiben kann wenn der Klient die verbindung beendet. schreibe ich den Klienten so um ,dass der lokale rechner schreibt, komme ich nicht mehr in die recv()-whileschleife rein - if (fgets...) ist ja immer wahr.
nun endlich die konktrete frage:
1. woran erkenne ich, dass im moment nicht gesendet wird oder wie schreibe ich die while-schleife fuer recv() um, so dass nur recv()d wird, wenn was kommt?
2.woran erkenne ich, ob eine eingabe getaetigt wurde, ohne dabei fgets() immer auszufuehren?
3. (nebenbei) wieso kann ich keine neuen variablen deklarieren, wenn schon irgendwelche funktionen ausgefuehrt wurden -> ich muss alle variablen am anfang des programmes deklarieren?Ich danke fuer jede Hilfe...
Gruss caspar
-
- du kannst variablen deklarieren, wanns dir passt wenn du dynamisch mehr brauchst, nimmm einen vector oder eine einfache list.
- mit nonblocking sockets und FD_*+select kannst du überprüfen, ob auf einem socket was zu holen ist, dein erster code war da schon richtig. der unterschied ist folgender: hättest du blocking-sockets, würde das programm solang beim recv aufruf verharren, bis was reinkommt. bei nonblocking überprüfst du, ob was da ist, und holst es dann ab. du kannst auch ohne diese überprüfung ein recv machen, nur hast halt dann nichts im buffer.
- eingaben erkennen, musst dich an das entsprechende betriebssytem wenden. c/c++ hat eigentlich weder tastatur, maus noch nen monitor
ich hab solche sachen immer multithreaded gemacht, vielleicht hilft dir das
-
danke fuer die antwort.´benutze ich fcntl(sock_listen, S_SETFL, O_NONBLOCK), so gibt der Compiler (C++Builer 5) "Undefiniertes Sysmbol S_SETFL", dabei habe ich fcntl.h included. Gibt es da irgenwelche windowsspezifischen eigenschaften? Kennt jemand funktionen fuer windows, mit denen man sockets auf nonblocking setzen kann? unter google wurde ich nicht fuendig.
--> ich habe schon das codefragment, mit dem man nichtblockierende sockets schreiben kann:
u_long ul = 1; ioctlsocket(sock_get,FIONBIO, &ul);
caspar
-
ich habe den code wie folgt umgeeandert:
ul = 1; ioctlsocket(sock_get, FIONBIO, &ul); do { FD_SET(sock_get, &readfds); select(sock_get+1, &readfds, NULL, NULL, NULL); if (FD_ISSET(sock_get, &readfds)) { if ((recvd = recv(sock_get, get, MAXDATA-1, 0)) > 0) { //[get[recvd] = '\0'; oder mit ?] printf("192.168.0.2> %s\n", get); } } } while (1)
mit ul = 1; ioctlsocket(sock_get, FIONBIO, &ul); wird sock_get zu einem nichtblockierendem. den code fuer das senden habe ich aus dem programm vorerst ausgelassen; dazu habe ich einen klienten geschrieben, der nur schreibt. das resultat:
sobald der klient eine nachricht losschickt, werden ein haufen "192.168.0.1>" (also die recv()ausgabe ohne dynamischen inhalt) ausgegeben. danach folgt der verunstaltete inhalt der nachricht und es folgen weitere "192.168.0.2".wie kann ich ein "empfangen nach bedarf" schreiben? ich versichere euch, das ist nicht die einzige variante, die ich ausprobiert habe (die sinnvollste),... ich bin ratlos.
schreibe ich das von oben und heange eine benutzereingabe an, die an den clienten verschickt werden soll, so komme ich nicht mehr in die recv()-schleife, um nachrichten zu empfangen. ich renne mich im kreis - ich bin kein guter laeufer
ich freue mich ueber jede form von hilfe
Gruss caspar
-
mir fällt nur eine etwas baurige lösung ein: multithreading
ein thread, der dauernd schaut ob was da ist, ein thread, der das "gewöhnliche" programm weiter macht (senden bei bedarf)
-
ich gebe zu, ich weiss nicht, was multi-threads sind (mehrere laufende prozesse?). ich koennte aber auch mein programmgesign aendern. bis jetzt hatte ich vor, dass der server anfragen annimmt und selber nachrichten versendet; eine art peer-to-peer mit server - komisch, doch funktionell war es so. nun werde ich (versuchen) einen server zu schreiben, der alle einkommenden nachrichten annimmt und sie an alle angemeldeten clients versendet [wie z.b. IRC]. der server hat also keine moeglichkeit selber nachricht von der benutzereingabe zu versenden.
trotzdem denke ich, dass es prinzipiell moeglich waere auch mein erstes vorhaben zu loesen (...mit entsprechenden programmierfaehigkeiten ;).gruss caspar
[ich denke bei meinem neuen vorhaben werde ich euch auch noch zu genuege belaestigen]