Soketin bir program için kullanılamaması fikrinin, hala transit olan TCP veri segmentlerinin gelmesine ve çekirdek tarafından atılmasına izin vermek olduğuna inanıyorum. Yani, bir uygulamanın close(2)
bir soketi çağırması mümkündür , ancak yönlendirme gecikmeleri veya paketleri kontrol etmek veya yanlış bir şekilde TCP bağlantısının diğer tarafının bir süre veri göndermesine izin verebilirsiniz. Uygulama artık TCP veri segmentleriyle uğraşmak istemediğini belirtti, bu yüzden çekirdek sadece geldikçe onları atmalıdır.
C derlemek ve zaman aşımı süresini görmek için kullanabileceğiniz küçük bir program kesmek:
#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;
}
Bu programı 3 farklı makinede denedim ve çekirdek olmayan bir kullanıcının bir soketi yeniden açmasına izin vermeyi reddettiği 55 ila 59 saniye arasında değişken bir zaman alıyorum. Yukarıdaki kodu "opener" adlı bir yürütülebilir dosya derledi ve böyle koştu:
./opener -p 7896; ./opener -p 7896
Başka bir pencere açtım ve bunu yaptım:
telnet otherhost 7896
Bu, ilk "açıcı" örneğinin bir bağlantıyı kabul etmesine ve ardından kapatmasına neden olur. İkinci "açıcı" örneği bind(2)
her saniye 7896 numaralı TCP bağlantı noktasına gitmeye çalışır . "açıcı" 55 ila 59 saniye gecikme bildiriyor.
Etrafta dolaşırken, insanların bunu yapmasını önerdiğini görüyorum:
echo 30 > /proc/sys/net/ipv4/tcp_fin_timeout
bu aralığı azaltmak için. Benim için işe yaramadı. Erişebildiğim 4 linux makineden ikisinin 30 ve ikisinin 60'ı vardı. Ayrıca bu değeri 10'a kadar düşürdüm. "Açıcı" programında fark yok.
Bunu yapmak:
echo 1 > /proc/sys/net/ipv4/tcp_tw_recycle
bazı şeyleri değiştirdi. İkinci "açıcı" yeni yuvasının alınması sadece 3 saniye sürdü.
man 2 bind
Bana inanmıyorsan dene . Kuşkusuz, muhtemelen birileri "bağla" derken, bu kadar adil olan unix insanların ilk düşündüğü şey değil.