fcntl O_NONBLOCK problem
-
Hallo Zusammen,
Ich habe eine Frage im Bezug auf das seltsame Verhalten von recvfrom:
Wenn ich einen file_deskriptor von meinem udp socket mit fcntl auf O_NONBLOCK
einstelle, liefert recvfrom immer -1, obwohl Daten ankommen..Code sieht so aus:
/*... */ int addr_size = sizeof(server) ; if( (client_fd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) return 1 ; /* .. */ flags = fcntl(client_fd, F_GETFL, 0) ; flags |= O_NONBLOCK ; fcntl(client_fd, F_SETFL, flags) ; /* .. */ while(repetition > 0){ sendto(client_fd, argv[3], strlen(argv[3]), 0, (struct sockaddr *)&server, sizeof(server)) ; /* server -might- have sent sth */ what = recvfrom(client_fd,client_buf, MAX_LEN-1, 0, (struct sockaddr *)&server, &addr_size) ; fprintf(stdout, "recvfrom delivers:%d\n",what); --repetition ; } /*.. */
Irgendwie verstehe ich nicht warum recvfrom immer -1 liefert.
Muesste es nicht genuegen die Datei-Deskriptor auf O_NONBLOCK umzustellen?
Wenn Data da ist, wuerde recvfrom den buffer einfuellen und wenn nicht
einfach die Schleife weiter ausfuehren lassen.
Oder muesste man hier "select" benutzen? Sowie ich von select verstanden habe, der
Aufruf ist nur dann noetig wenn man "multiplexing" zwischen mehreren Sockets
machen muss(auf mehrere Sockets lauschen). Meiner Meinung nach waere die
Verwendung von select hier ueberfluessig, weil die Anwendung sich nur fuer einen
Socket interessiert.Danke im voraus,
-
in deinem codebeispiel sieht man nicht, dass du deinen udp socket an einen port bindest. du sagst nur, dass du udp verwenden willst. um daten zu senden, ist das bind() nicht nötig, um daten zu empfangen aber schon. du solltest außerdem perror verwenden. recvfrom gibt -1 allgemein im fehlerfall zurück, sodass man errno bei verwendung von non-blocking sockets immer prüfen muss.
-
udp schrieb:
in deinem codebeispiel sieht man nicht, dass du deinen udp socket an einen port bindest. du sagst nur, dass du udp verwenden willst. um daten zu senden, ist das bind() nicht nötig, um daten zu empfangen aber schon.
In der Tat habe ich den socket an einem Port nicht gebunden. Ich habe auf
der Clientseite die "bind" dem Kernel ueberlassen. Nach deiner Empfehlung habe
ich bind noch zu meiner Code eingebunden, leider dasselbe Problem../* client seite */ /* noch zusaetzlich zu der Code in meiner ersten Posting */ client.sin_family = AF_INET ; client.sin_port = htons(10000) ; client.sin_addr.s_addr = inet_addr("192.168.2.10") ; memset(client.sin_zero, '\0', sizeof(client.sin_zero)) ; if(bind(client_fd, (struct sockaddr *)&client, sizeof(client)) ==-1){ perror("bind"); exit(0); } /* ... */ while(repetition > 0) { /* siehe meine erste Posting */ }
Was ich inzwischen gemerkt habe: Wenn ich bei recvfrom busy waiting mache (
in eine endlose whileschleife laufen solange Packet vom Server eingtroffen ist}
dann bekommt der nonblocking recvfrom tatsaechlich etwas vom Server -unabhaengig
von der Wiederholung "repetition".int c=0 ; while(repetitition > 0 ){ sendto(client_fd, argv[3], strlen(argv[3]), 0, (struct sockaddr *)&server, sizeof(server)); /* .. */ while(1){ what = recvfrom(client_fd,client_buf, MAX_LEN-1, 0, (struct sockaddr *)&server, &addr_size) ; c++ ; if(what > -1) break ; } fprintf(stdout,"had to be looped:%d times\n",c) ; c =0 ; /* reset */ --repetition; }
Dieser Ansatz ist selbstverstaendlich nicht Sinn einer nonblocking socket
Implementierung -es dient nur dafuer wie schnell der Client Prozess im Vergleich
zu Server Prozess ist. Client wird so ausgefuehrt:./client <target_ip> <repetition> <message>
Ohne busy-waiting:
Der Client arbeitet schneller (bis zur 50 Wiederholung) als die Server-Seite. Und er ist schon laengst fertig bevor was recvfrom sich mit einem
Datagram meldet. Ab ungefaehr 50 Repetition kriegt der Client von Server's
Nachricht.Zu Kern meiner Frage: Ich glaube select wird hier nicht unbedingt benoetigt
wenn man nur als empfaenger gucken moechte ob da was empfangen worden ist.
Den Socket auf O_NONBLOCK setzen, das reicht voellig aus.
Man kriegt die Daten irgendwann ruebergestellt. Wenn man allderings zwei
Seiten synchronisieren moechte, haette man mit nonblocking sockets
problem, denke ich.
-
das perror musst du auch bei recvfrom verwenden, damit dort die fehlermeldung ausgegeben wird. bitte poste diese meldung dann auch. das bind sollte so passen, wie du es hast.
es sieht so aus, als dass ich das bei deinem ersten posting missverstanden habe. ich dachte, du postest server code. das bind ist aber trotzdem nötig. wenn man pakete verschickt, ist es das nicht, da der kernel automatisch einen port auswählt. leider ist mir keine method bekannt, herauszufinden, was der kernel verwendet hat. wenn jemand anderer das weiß, bitte hier posten. weiter unten im beitrag steht noch eine andere methode.Was ich inzwischen gemerkt habe: Wenn ich bei recvfrom busy waiting mache ( in eine endlose whileschleife laufen solange Packet vom Server eingtroffen ist} dann bekommt der nonblocking recvfrom tatsaechlich etwas vom Server -unabhaengig von der Wiederholung "repetition".
leider verstehe ich nicht, was du damit sagen willst. welches socket ist bei dir non-blocking und warum? non-blocking ist das gleiche wie busy-waiting.
dein code beispiel enthält eine variable "repetitition" und eine namens "repetition". sind das zwei verschiedene, oder war das ein tippfehler?dein projekt besteht aus einem client und einem server, die per udp kommunizieren. der client schickt eine anfrage, die im server bearbeitet wird, indem ein neuer server-prozess geforkt wird. hab ich das so richtig verstanden?
man kann auch für udp connect verwenden, sodass der client eine art pseudo-verbindung im kernel erstellt. dadurch kann man dann per read und write auf den socket zugreifen. probier das für den udp client aus.
für den server brauchst du nur einen socket und hast auch immer nur einen file descriptor. dort werden immer nur einzelne udp pakete landen. die müssen dann einzeln bearbeitet werden.
-
Danke fuer die ausfuehrliche Antwort erst mal.
udp schrieb:
das perror musst du auch bei recvfrom verwenden, damit dort die fehlermeldung ausgegeben wird. bitte poste diese meldung dann auch. das bind sollte so passen, wie du es hast.
if( recvfrom(client_fd,client_buf, MAX_LEN-1, 0, (struct sockaddr *)&server, &addr_size) == -1 ){ perror("recvfrom"); return ; }
udp schrieb:
das bind ist aber trotzdem nötig. wenn man pakete verschickt, ist es das nicht, da der kernel automatisch einen port auswählt. leider ist mir keine method bekannt, herauszufinden, was der kernel verwendet hat. wenn jemand anderer das weiß, bitte hier posten. weiter unten im beitrag steht noch eine andere methode.
Was bind angeht, habe ich bei Stevens nachgelesen dass es nicht unbedingt
noetig ist, wenn man erstmal beim Process "sendto" ausfuehrt:Stevens Buch schrieb:
With a UDP socket the first time the process calls sendto, if the socket
has not yet had a local port bound to it, that is when an ephemeral port is
chosen by the kernel for the socket. As with TCP, the client can call
bind explicitly, but this is rarely done.(Seite 217)udp schrieb:
non-blocking ist das gleiche wie busy-waiting.
dein code beispiel enthält eine variable "repetitition" und eine namens "repetition". sind das zwei verschiedene, oder war das ein tippfehler?ja, es war tippfehler, repetition sollte alle heissen.
udp schrieb:
dein projekt besteht aus einem client und einem server, die per udp kommunizieren. der client schickt eine anfrage, die im server bearbeitet wird, indem ein neuer server-prozess geforkt wird. hab ich das so richtig verstanden?
Ja, ganz genau. einen "concurrent" Diener. Mittlerweile werden die einzelne
Clients per Server-Threads bedient.udp schrieb:
man kann auch für udp connect verwenden..
habe ich noch nicht ausprobiert.. ich weiss nicht, ist das direkte
read/write paar effizienter als sendto/recvfrom?Eine Frage habe ich noch:
Was kann man bei Maschinen manipulieren damit die udp verbindung zuverlaessiger
wird? Die Argumente, die mir einfallen, sind:
- Kernel UDP-Buffer auf client (sysctl -w net.core.rmem_max=8388608)
- Kernel UDP-Buffer auf server
- Socket Receive Buffer ( setsockopt(...,..,SO_RCVBUF,..) )
- und natuerlich buffer von meiner applikationGruss,
-
funktioniert jetzt alles?
udp ist als unzuverlässiges protokoll entwickelt worden. du kannst nichts tun, um es zuverlässliger zu machen. genauer noch, gibt es für zuverlässigkeit keine steigerung. es gibt ja oder nein in allen situationen. wenn du zuverlässligkeit haben willst, musst du tcp verwenden.
-
udp schrieb:
funktioniert jetzt alles?
ja, das tut es.. danke fuer die zusaetzliche Infos.
Gruss.