मेरा मानना है कि किसी प्रोग्राम के लिए सॉकेट का अनुपलब्ध होना किसी भी टीसीपी डेटा सेगमेंट को अभी भी पारगमन में आने की अनुमति देना है, और कर्नेल द्वारा छोड़ दिया जाना है। यही है, किसी एप्लिकेशन के लिए close(2)
सॉकेट पर कॉल करना संभव है , लेकिन पैकेटों को नियंत्रित करने के लिए देरी या हादसों को नियंत्रित करना या आपके पास टीसीपी कनेक्शन के दूसरे पक्ष को थोड़ी देर के लिए डेटा भेजने की अनुमति दे सकता है। एप्लिकेशन ने संकेत दिया है कि यह अब टीसीपी डेटा सेगमेंट से निपटना नहीं चाहता है, इसलिए कर्नेल को उन्हें छोड़ देना चाहिए क्योंकि वे अंदर आते हैं।
मैंने C में एक छोटा प्रोग्राम हैक किया है जिसे आप संकलित कर सकते हैं और यह देखने के लिए उपयोग कर सकते हैं कि टाइमआउट कितना लंबा है:
#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;
}
मैंने 3 अलग-अलग मशीनों पर इस कार्यक्रम की कोशिश की, और मुझे एक चर समय मिलता है, 55 और 59 सेकंड के बीच, जब कर्नेल एक गैर-रूट उपयोगकर्ता को सॉकेट को फिर से खोलने की अनुमति देने से इनकार करता है। मैंने उपरोक्त कोड को "ओपनर" नामक एक निष्पादन योग्य के लिए संकलित किया, और इसे इस तरह से चलाया:
./opener -p 7896; ./opener -p 7896
मैंने एक और खिड़की खोली और यह किया:
telnet otherhost 7896
किसी कनेक्शन को स्वीकार करने के लिए "ओपनर" का पहला उदाहरण है, फिर इसे बंद करें। "ओपनर" का दूसरा उदाहरण bind(2)
टीसीपी पोर्ट 7896 के लिए हर सेकंड की कोशिश करता है । "ओपनर" 55 से 59 सेकंड देरी से रिपोर्ट करता है।
चारों ओर घूमते हुए, मुझे पता चलता है कि लोग ऐसा करने की सलाह देते हैं:
echo 30 > /proc/sys/net/ipv4/tcp_fin_timeout
उस अंतराल को कम करने के लिए। यह मेरे लिए काम नहीं किया। जिन 4 लिनक्स मशीनों तक मेरी पहुंच थी उनमें से दो में 30 और दो के पास 60 थे। मैंने भी उस मूल्य को 10 से कम बताया। "ओपनर" कार्यक्रम से कोई फर्क नहीं पड़ा।
यह कर रहा हूं:
echo 1 > /proc/sys/net/ipv4/tcp_tw_recycle
चीजों को बदल दिया। दूसरे "ओपनर" को अपना नया सॉकेट प्राप्त करने में केवल 3 सेकंड का समय लगा।
man 2 bind
अगर आपको मुझ पर विश्वास नहीं है तो कोशिश करें । बेशक, यह शायद पहली बात नहीं है कि यूनिक्स के लोग सोचते हैं कि जब कोई "बाइंड" कहता है, तो यह काफी उचित है।