Forum: PC-Programmierung Problem mit Websever, Port unklar und ändert sich


von Joachim D. (Firma: JDCC) (scheppertreiber)


Lesenswert?

Servus,

es zickt schon wieder ;)

Ubuntu 21, Gnu C-Compiler.
1
static  int     s;
2
static  struct  sockaddr_in a, a1;
3
static  int     port = 8001;
4
5
s = socket( AF_INET, SOCK_STREAM, 0);
6
memset( &a, 0, sizeof( a));
7
a.sin_family      = AF_INET;
8
a.sin_port        = htons( port);
9
a.sin_addr.s_addr = htonl( INADDR_ANY);
10
11
bind( s, (struct sockaddr*) &a, sizeof( a));
12
listen( s, 100);
13
printf( "\nlistening on port %d, PID %d", port, getpid());
14
fflush( stdout);

Symptome:

Meist lauscht es an Port 8001, manchmal nicht. Lt. lsof -i
lauscht es an einem anderen, beliebigen Port zB 37041 oÄ.
Nach mehrmaligem Abbruch mit ^C geht es dann wieder eine Weile.

Was kann denn das sein ?

von Hmmm (Gast)


Lesenswert?

Was sagt strace zum bind()?

Ich vermute, dass das schiefgeht, weil bereits etwas auf dem Port 
lauscht.

Immer Fehlerbehandlung einbauen!

von PittyJ (Gast)


Lesenswert?

bind() und listen() geben Fehler zurück, wenn irgendwas nicht geht.
Warum werden die in deinem Programm nicht beachtet?

von Joachim D. (Firma: JDCC) (scheppertreiber)


Lesenswert?

Ich schreibe mir die rets von bind und listen mal raus. Bisher
20 Versuche, immer 0 und der Port stimmt. 8001 ist frei.

von (prx) A. K. (prx)


Lesenswert?

Also Problem beim eingrenzen verschwunden?

von Joachim D. (Firma: JDCC) (scheppertreiber)


Lesenswert?

Soweit trau ich der Sache nicht ... ;)

von Hmmm (Gast)


Lesenswert?

Joachim D. schrieb:
> Bisher
> 20 Versuche, immer 0 und der Port stimmt. 8001 ist frei.

Starte Dein Tool mal testweise doppelt, damit der Port schon belegt ist.

von (prx) A. K. (prx)


Lesenswert?

Es dürfte davon abhängen, wie eine Verbindung beendet wird. Wird sie 
nicht vollständig beendet, beispielsweise bei Abbruch/Crash des Servers, 
bleibt der Socket im System auch nach Ende des Servers noch eine Weile 
im Status TIME_WAIT hängen und der Port ist währenddessen nicht 
benutzbar. Fehlt dir die Geduld, das abzuwarten, dann probier es mit 
SO_REUSEADDR/SO_REUSEPORT.

: Bearbeitet durch User
von Joachim D. (Firma: JDCC) (scheppertreiber)


Lesenswert?

Hmmm schrieb:
> Starte Dein Tool mal testweise doppelt, damit der Port schon belegt ist.

http-Server jdcc uQuery Linux, build Apr 11 2022 / 13:29:19 bind -1
*** Fehler Address already in use listen 0
listening on port 8001, PID 6471

von Hmmm (Gast)


Lesenswert?

(prx) A. K. schrieb:
> Fehlt dir die Geduld, das abzuwarten, dann probier es mit
> SO_REUSEADDR/SO_REUSEPORT.

Ersteres reicht und ist POSIX-konform.

Joachim D. schrieb:
> Address already in use

Genau das dürfte vorher auch passiert sein, da bloss noch unbemerkt.

von Joachim D. (Firma: JDCC) (scheppertreiber)


Lesenswert?

Denkbar bis wahrscheinlich.

Wieso bleibt der socket offen wenn das Programm beendet
wird ? Normal schließt das OS ja alles Ressourcen ...

von (prx) A. K. (prx)


Lesenswert?

Joachim D. schrieb:
> Wieso bleibt der socket offen wenn das Programm beendet
> wird ? Normal schließt das OS ja alles Ressourcen ...

Wenn bei der Beendigung noch eine Verbindung zu einem Client besteht, 
muss die nach allen Regeln des TCP korrekt beendet werden. Beendet der 
Client die Verbindung, gehts schnell. Tut es der Server, dann nicht:
https://hea-www.harvard.edu/~fine/Tech/addrinuse.html

: Bearbeitet durch User
von Kölner (Gast)


Lesenswert?

SOCK_STREAM wird nicht sofort mit Beendigung des Programms geschlossen. 
Versuchs mal mit SOCK_DGRAM.

von Hmmm (Gast)


Lesenswert?

Joachim D. schrieb:
> Wieso bleibt der socket offen wenn das Programm beendet
> wird ? Normal schließt das OS ja alles Ressourcen ...

Der Socket wird aus Userland-Sicht geschlossen, aber der TCP/IP-Stack 
hat ggf. noch eine Weile mit den Verbindungen zu tun.

Stell Dir z.B. einen Mobilfunk-Client vor, der durch einen Netzabriss 
nicht auf das FIN reagiert.

Kölner schrieb:
> SOCK_STREAM wird nicht sofort mit Beendigung des Programms geschlossen.
> Versuchs mal mit SOCK_DGRAM.

SOCK_DGRAM ist in diesem Fall UDP, das will man nur selten wirklich.

Aber SO_REUSEADDR ist genau für solche Fälle da: Solange kein listen() 
läuft, bekommt man den Port zurück.

von Joachim D. (Firma: JDCC) (scheppertreiber)


Lesenswert?

Jetzt hängt es wieder, es will den Port 8001 nicht rausrücken. Es dauert
ca 2 min dann ist der Port wieder frei.

COMMAND  PID USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
rygel   1849   jd   11u  IPv4  31495      0t0  TCP localhost:40297 
(LISTEN)
rygel   1849   jd   12u  IPv4  31475      0t0  UDP localhost:1900
rygel   1849   jd   13u  IPv4  31476      0t0  UDP 239.255.255.250:1900
rygel   1849   jd   14u  IPv4  31478      0t0  UDP localhost:51239
rygel   1849   jd   15u  IPv4  31480      0t0  UDP 
jd-linux.fritz.box:1900
rygel   1849   jd   16u  IPv4  31481      0t0  UDP 239.255.255.250:1900
rygel   1849   jd   17u  IPv4  31483      0t0  UDP 
jd-linux.fritz.box:47753
rygel   1849   jd   18u  IPv6  31486      0t0  UDP ip6-localhost:1900
rygel   1849   jd   19u  IPv6  31487      0t0  UDP [ff05::c]:1900
rygel   1849   jd   20u  IPv6  31489      0t0  UDP ip6-localhost:60218
rygel   1849   jd   22u  IPv4  31496      0t0  TCP 
jd-linux.fritz.box:34477 (LISTEN)
rygel   1849   jd   23u  IPv6  31497      0t0  TCP ip6-localhost:34991 
(LISTEN)
rygel   1849   jd   37u  IPv6  31512      0t0  UDP jd-linux:1900
rygel   1849   jd   38u  IPv6  31513      0t0  UDP [ff02::c]:1900
rygel   1849   jd   39u  IPv6  31515      0t0  UDP jd-linux:56591
rygel   1849   jd   40u  IPv6  31516      0t0  TCP jd-linux:42233 
(LISTEN)

Was könnte das sein ? Port 8001 taucht da nicht auf.

von Εrnst B. (ernst)


Lesenswert?

Joachim D. schrieb:
> Was könnte das sein ? Port 8001 taucht da nicht auf.

Mit was hast du nachgeschaut?

Mit "netstat" siehst du da z.B.
1
tcp        0      0 localhost:8001          localhost:57438         TIME_WAIT


und solange da noch ein oder mehrere "TIME_WAIT"-TCP-Verbindungen 
hängen, kriegst du den Serverport nicht zurück. Außer mit SO_REUSEADDR, 
das ist exakt dafür da.

von Εrnst B. (ernst)


Lesenswert?

Joachim D. schrieb:
> Was könnte das sein ? Port 8001 taucht da nicht auf.

Mit was hast du nachgeschaut?

Mit "netstat" siehst du da z.B.
1
tcp        0      0 localhost:8001          localhost:57438         TIME_WAIT


und solange da noch ein oder mehrere "TIME_WAIT"-TCP-Verbindungen 
hängen, kriegst du den Serverport nicht zurück. Außer mit SO_REUSEADDR, 
das ist exakt dafür da.

Joachim D. schrieb:
> Es dauert
> ca 2 min dann ist der Port wieder frei.

# cat /proc/sys/net/ipv4/tcp_fin_timeout

Aber: diesen Wert runterzustellen lindert nur die Symptome, "repariert" 
kriegst du es mit
1
int flagvalue = 1;
2
setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &flagvalue, sizeof(int));
vor dem "bind".

von Joachim D. (Firma: JDCC) (scheppertreiber)


Lesenswert?

Εrnst B. schrieb:
> Mit was hast du nachgeschaut?

lsof -i

von Joachim D. (Firma: JDCC) (scheppertreiber)


Lesenswert?

int flagvalue = 1;
setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &flagvalue, sizeof(int));

habe ich eingebaut. Mal sehen ob sich etwas tut ;)

Vielen Dank an alle !

von Hmmm (Gast)


Lesenswert?

Joachim D. schrieb:
> Es dauert
> ca 2 min dann ist der Port wieder frei.

Also genau das Verhalten, das prx und ich mit Hinweis auf SO_REUSEADDR 
beschrieben haben.

Joachim D. schrieb:
> lsof -i

Das bringt nichts, der Socket existiert ja mangels Prozess nicht mehr.

netstat -a hätte Dir den Grund gezeigt.

Joachim D. schrieb:
> Mal sehen ob sich etwas tut ;)

Du kannst das einfach provozieren, indem Du bei bestehender 
TCP-Connection den Ethernet-Stecker der Gegenstelle rausziehst.

von Rolf M. (rmagnus)


Lesenswert?

Εrnst B. schrieb:
> Aber: diesen Wert runterzustellen lindert nur die Symptome, "repariert"
> kriegst du es mit

Ich möchte nochmal anmerken, dass das nicht den Fehler behebt, sondern 
einen work-around darstellt. Der Fehler ist immer noch, dass ein Socket 
nicht ordnungsgemäß geschlossen wurde.

Hmmm schrieb:
> netstat -a hätte Dir den Grund gezeigt.

Ich verwende dafür immer netstat -tulpn

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.