Saya percaya bahwa ide soket tidak tersedia untuk suatu program adalah untuk memungkinkan segmen data TCP yang masih dalam perjalanan untuk tiba, dan dibuang oleh kernel. Yaitu, mungkin saja suatu aplikasi memanggil close(2)
soket, tetapi merutekan keterlambatan atau kecelakaan untuk mengontrol paket atau apa saja yang Anda bisa izinkan sisi lain koneksi TCP mengirim data untuk sementara waktu. Aplikasi telah mengindikasikan bahwa ia tidak lagi ingin berurusan dengan segmen data TCP, jadi kernel harus membuangnya begitu mereka masuk.
Saya meretas sedikit program di C yang dapat Anda kompilasi dan gunakan untuk melihat berapa lama waktu tunggu:
#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;
}
Saya mencoba program ini pada 3 mesin yang berbeda, dan saya mendapatkan waktu variabel, antara 55 dan 59 detik, ketika kernel menolak untuk mengizinkan pengguna non-root untuk membuka kembali soket. Saya mengkompilasi kode di atas ke executable bernama "pembuka", dan menjalankannya seperti ini:
./opener -p 7896; ./opener -p 7896
Saya membuka jendela lain dan melakukan ini:
telnet otherhost 7896
Itu menyebabkan instance pertama dari "pembuka" menerima koneksi, lalu menutupnya. Contoh kedua dari "pembuka" mencoba untuk bind(2)
ke port TCP 7896 setiap detik. "pembuka" melaporkan penundaan 55 hingga 59 detik.
Googling sekitar, saya menemukan bahwa orang merekomendasikan melakukan ini:
echo 30 > /proc/sys/net/ipv4/tcp_fin_timeout
untuk mengurangi interval itu. Itu tidak berhasil untuk saya. Dari 4 mesin linux yang saya akses, dua memiliki 30 dan dua memiliki 60. Saya juga menetapkan nilai serendah 10. Tidak ada perbedaan dengan program "pembuka".
Melakukan ini:
echo 1 > /proc/sys/net/ipv4/tcp_tw_recycle
memang mengubah hal-hal. "Pembuka" kedua hanya membutuhkan waktu sekitar 3 detik untuk mendapatkan soket baru.
man 2 bind
jika Anda tidak percaya padaku. Memang, itu mungkin bukan hal pertama yang orang pikirkan ketika seseorang mengatakan "bind", cukup adil.