Ich glaube, dass die Idee, dass der Socket für ein Programm nicht verfügbar ist, darin besteht, dass alle TCP-Datensegmente, die sich noch auf dem Weg befinden, eintreffen und vom Kernel verworfen werden. Das heißt, es ist möglich, dass eine Anwendung close(2)
einen Socket aufruft, aber Verzögerungen oder Pannen weiterleitet, um Pakete zu steuern, oder was Sie der anderen Seite einer TCP-Verbindung erlauben können, für eine Weile Daten zu senden. Die Anwendung hat angegeben, dass sie keine TCP-Datensegmente mehr verarbeiten möchte, daher sollte der Kernel sie einfach verwerfen, sobald sie eingehen.
Ich habe ein kleines Programm in C gehackt, das Sie kompilieren und verwenden können, um zu sehen, wie lange das Timeout dauert:
#include <stdio.h> /* fprintf() */
#include <string.h> /* strerror() */
#include <errno.h> /* errno */
#include <stdlib.h> /* strtol() */
#include <signal.h> /* signal() */
#include <sys/time.h> /* struct timeval */
#include <unistd.h> /* read(), write(), close(), gettimeofday() */
#include <sys/types.h> /* socket() */
#include <sys/socket.h> /* socket-related stuff */
#include <netinet/in.h>
#include <arpa/inet.h> /* inet_ntoa() */
float elapsed_time(struct timeval before, struct timeval after);
int
main(int ac, char **av)
{
int opt;
int listen_fd = -1;
unsigned short port = 0;
struct sockaddr_in serv_addr;
struct timeval before_bind;
struct timeval after_bind;
while (-1 != (opt = getopt(ac, av, "p:"))) {
switch (opt) {
case 'p':
port = (unsigned short)atoi(optarg);
break;
}
}
if (0 == port) {
fprintf(stderr, "Need a port to listen on\n");
return 2;
}
if (0 > (listen_fd = socket(AF_INET, SOCK_STREAM, 0))) {
fprintf(stderr, "Opening socket: %s\n", strerror(errno));
return 1;
}
memset(&serv_addr, '\0', sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
serv_addr.sin_port = htons(port);
gettimeofday(&before_bind, NULL);
while (0 > bind(listen_fd, (struct sockaddr *)&serv_addr, sizeof(serv_addr))) {
fprintf(stderr, "binding socket to port %d: %s\n",
ntohs(serv_addr.sin_port),
strerror(errno));
sleep(1);
}
gettimeofday(&after_bind, NULL);
printf("bind took %.5f seconds\n", elapsed_time(before_bind, after_bind));
printf("# Listening on port %d\n", ntohs(serv_addr.sin_port));
if (0 > listen(listen_fd, 100)) {
fprintf(stderr, "listen() on fd %d: %s\n",
listen_fd,
strerror(errno));
return 1;
}
{
struct sockaddr_in cli_addr;
struct timeval before;
int newfd;
socklen_t clilen;
clilen = sizeof(cli_addr);
if (0 > (newfd = accept(listen_fd, (struct sockaddr *)&cli_addr, &clilen))) {
fprintf(stderr, "accept() on fd %d: %s\n", listen_fd, strerror(errno));
exit(2);
}
gettimeofday(&before, NULL);
printf("At %ld.%06ld\tconnected to: %s\n",
before.tv_sec, before.tv_usec,
inet_ntoa(cli_addr.sin_addr)
);
fflush(stdout);
while (close(newfd) == EINTR) ;
}
if (0 > close(listen_fd))
fprintf(stderr, "Closing socket: %s\n", strerror(errno));
return 0;
}
float
elapsed_time(struct timeval before, struct timeval after)
{
float r = 0.0;
if (before.tv_usec > after.tv_usec) {
after.tv_usec += 1000000;
--after.tv_sec;
}
r = (float)(after.tv_sec - before.tv_sec)
+ (1.0E-6)*(float)(after.tv_usec - before.tv_usec);
return r;
}
Ich habe dieses Programm auf 3 verschiedenen Computern ausprobiert und erhalte eine variable Zeit zwischen 55 und 59 Sekunden, wenn der Kernel einem Benutzer ohne Rootberechtigung das erneute Öffnen eines Sockets verweigert. Ich habe den obigen Code zu einer ausführbaren Datei mit dem Namen "opener" kompiliert und sie folgendermaßen ausgeführt:
./opener -p 7896; ./opener -p 7896
Ich öffnete ein anderes Fenster und tat dies:
telnet otherhost 7896
Das bewirkt, dass die erste Instanz von "opener" eine Verbindung akzeptiert und dann schließt. Die zweite Instanz von "opener" versucht bind(2)
jede Sekunde , auf den TCP-Port 7896 zuzugreifen. "opener" meldet eine Verzögerung von 55 bis 59 Sekunden.
Beim googeln finde ich, dass die Leute dies empfehlen:
echo 30 > /proc/sys/net/ipv4/tcp_fin_timeout
um dieses Intervall zu reduzieren. Es hat bei mir nicht funktioniert. Von den 4 Linux-Rechnern, auf die ich Zugriff hatte, hatten zwei 30 und zwei 60. Ich habe diesen Wert auch auf 10 gesetzt. Kein Unterschied zum "opener" -Programm.
Dies tun:
echo 1 > /proc/sys/net/ipv4/tcp_tw_recycle
Dinge geändert haben. Der zweite "Öffner" brauchte nur ca. 3 Sekunden, um seinen neuen Sockel zu bekommen.
man 2 bind
wenn du mir nicht glaubst. Zugegeben, es ist wahrscheinlich nicht das erste, woran Unix-Leute denken, wenn jemand "bind" sagt, also fair genug.