Tôi tin rằng ý tưởng về ổ cắm không có sẵn cho một chương trình là cho phép mọi phân đoạn dữ liệu TCP vẫn đang trong quá trình chuyển đến và bị loại bỏ bởi kernel. Nghĩa là, ứng dụng có thể gọi close(2)
trên một ổ cắm, nhưng việc định tuyến trễ hoặc rủi ro để kiểm soát các gói hoặc những gì bạn có thể cho phép phía bên kia của kết nối TCP gửi dữ liệu trong một thời gian. Ứng dụng đã chỉ ra rằng nó không còn muốn xử lý các phân đoạn dữ liệu TCP, vì vậy kernel chỉ nên loại bỏ chúng khi chúng đi vào.
Tôi đã hack một chương trình nhỏ trong C mà bạn có thể biên dịch và sử dụng để xem thời gian chờ là bao lâu:
#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;
}
Tôi đã thử chương trình này trên 3 máy khác nhau và tôi nhận được thời gian thay đổi, trong khoảng từ 55 đến 59 giây, khi hạt nhân từ chối cho phép người dùng không root để mở lại ổ cắm. Tôi đã biên dịch mã ở trên thành một "cái mở" có thể thực thi được và chạy nó như thế này:
./opener -p 7896; ./opener -p 7896
Tôi đã mở một cửa sổ khác và làm điều này:
telnet otherhost 7896
Điều đó khiến cho phiên bản đầu tiên của "công cụ mở" chấp nhận kết nối, sau đó đóng lại. Ví dụ thứ hai của "cái mở" cố gắng đến bind(2)
cổng TCP 7896 mỗi giây. "Người mở" báo cáo độ trễ 55 đến 59 giây.
Googling xung quanh, tôi thấy rằng mọi người khuyên bạn nên làm điều này:
echo 30 > /proc/sys/net/ipv4/tcp_fin_timeout
để giảm khoảng thời gian đó. Nó không làm việc cho tôi. Trong số 4 máy linux tôi có quyền truy cập, hai máy có 30 và hai máy có 60. Tôi cũng đặt giá trị đó ở mức 10. Không khác biệt với chương trình "mở".
Làm điều này:
echo 1 > /proc/sys/net/ipv4/tcp_tw_recycle
đã thay đổi mọi thứ. "Cái mở" thứ hai chỉ mất khoảng 3 giây để có được ổ cắm mới.
man 2 bind
nếu bạn không tin tôi. Phải thừa nhận rằng, đây có lẽ không phải là điều đầu tiên mọi người nghĩ đến khi ai đó nói "ràng buộc", như vậy là đủ công bằng.