Làm cách nào để buộc một ổ cắm trong TIME_WAIT?


113

Tôi chạy một chương trình cụ thể trên linux mà đôi khi gặp sự cố. Nếu bạn mở nó nhanh chóng sau đó, nó sẽ nghe trên socket 49201 thay vì 49200 như lần đầu tiên. netstat tiết lộ rằng 49200 đang ở trạng thái TIME_WAIT.

Có chương trình nào bạn có thể chạy để ngay lập tức buộc ổ cắm đó thoát khỏi trạng thái TIME_WAIT không?


1
Nếu bạn ở đây do "quá nhiều TIME_WAITmáy chủ" , chỉ cần bỏ qua ba câu trả lời đầu tiên để tránh câu hỏi thay vì trả lời nó.
Pacerier

Câu trả lời:


148
/etc/init.d/networking restart

Hãy để tôi giải thích. Giao thức điều khiển truyền (TCP) được thiết kế để trở thành giao thức truyền dữ liệu hai chiều, có trật tự và đáng tin cậy giữa hai điểm cuối (chương trình). Trong ngữ cảnh này, thuật ngữ đáng tin cậy có nghĩa là nó sẽ truyền lại các gói nếu nó bị mất ở giữa. TCP đảm bảo độ tin cậy bằng cách gửi lại các gói Xác nhận (ACK) cho một hoặc một loạt các gói nhận được từ máy ngang hàng.

Điều này cũng tương tự đối với các tín hiệu điều khiển như yêu cầu / phản hồi chấm dứt. RFC 793 định nghĩa trạng thái TIME-WAIT như sau:

TIME-WAIT - đại diện cho việc chờ đủ thời gian để vượt qua để chắc chắn rằng TCP từ xa đã nhận được xác nhận về yêu cầu chấm dứt kết nối của nó.

Xem sơ đồ trạng thái TCP sau: văn bản thay thế

TCP là giao thức giao tiếp hai chiều, vì vậy khi kết nối được thiết lập, không có sự khác biệt giữa máy khách và máy chủ. Ngoài ra, một trong hai người có thể gọi thoát, và cả hai đồng nghiệp cần phải đồng ý đóng cửa để đóng hoàn toàn kết nối TCP đã thiết lập.

Chúng ta hãy gọi người đầu tiên gọi những người bỏ cuộc là người chủ động gần hơn, và người khác ngang hàng thụ động gần hơn. Khi hoạt động gần hơn gửi FIN, trạng thái sẽ chuyển đến FIN-WAIT-1. Sau đó, nó nhận được ACK cho FIN đã gửi và trạng thái chuyển đến FIN-WAIT-2. Khi nó nhận được FIN cũng từ thụ động gần hơn, hoạt động gần hơn sẽ gửi ACK đến FIN và trạng thái chuyển sang TIME-WAIT. Trong trường hợp thụ động gần hơn không nhận được ACK đến FIN thứ hai, nó sẽ truyền lại gói FIN.

RFC 793 đặt TIME-OUT gấp đôi Thời gian phân đoạn tối đa hoặc 2MSL. Kể từ MSL, thời gian tối đa một gói có thể đi lang thang trên Internet, được đặt thành 2 phút, 2MSL là 4 phút. Vì không có ACK cho ACK, nên người hoạt động gần hơn không thể làm gì ngoài việc chờ 4 phút nếu nó tuân thủ đúng giao thức TCP / IP, chỉ trong trường hợp người gửi thụ động chưa nhận được ACK đến FIN của nó (về mặt lý thuyết) .

Trong thực tế, các gói bị thiếu có lẽ rất hiếm và rất hiếm nếu tất cả xảy ra trong mạng LAN hoặc trong một máy.

Để trả lời nguyên văn câu hỏi, Làm thế nào để buộc một ổ cắm trong TIME_WAIT?, Tôi vẫn sẽ giữ nguyên câu trả lời ban đầu của mình:

/etc/init.d/networking restart

Thực tế mà nói, tôi sẽ lập trình nó để nó bỏ qua trạng thái TIME-WAIT bằng cách sử dụng tùy chọn SO_REUSEADDR như WMR đã đề cập. Chính xác thì SO_REUSEADDR làm gì?

Tùy chọn ổ cắm này cho hạt nhân biết rằng ngay cả khi cổng này đang bận (ở
trạng thái TIME_WAIT), hãy tiếp tục và sử dụng lại bằng mọi cách. Nếu nó bận, nhưng với một trạng thái khác, bạn vẫn sẽ nhận được một địa chỉ đã bị lỗi sử dụng. Nó rất hữu ích nếu máy chủ của bạn đã bị tắt, và sau đó khởi động lại ngay lập tức trong khi các socket vẫn hoạt động trên cổng của nó. Bạn nên lưu ý rằng nếu có bất kỳ dữ liệu bất ngờ nào xuất hiện, nó có thể gây nhầm lẫn cho máy chủ của bạn, nhưng trong khi điều này là có thể, thì điều đó không có khả năng.


8
Câu trả lời tuyệt vời, nhưng không phải là câu trả lời chính xác cho câu hỏi của anh ấy. Khởi động lại mạng sẽ hoạt động, nhưng sau đó sẽ khởi động lại, vì vậy điều này không thể đúng.
Chris Huang-Leaver

3
@Chris Huang-Leaver, câu hỏi là "Có chương trình nào bạn có thể chạy để ngay lập tức buộc ổ cắm đó di chuyển ra khỏi trạng thái TIME_WAIT không?" nếu khởi động lại có thể được coi là chạy một chương trình, thì đó cũng là một câu trả lời đúng. Tại sao bạn nghĩ rằng điều này không thể đúng?
Eugene Yokota

8
WMR có câu trả lời hữu ích nhất (đó là những gì tôi làm khi gặp phải loại vấn đề này). Khởi động lại mạng quá mạnh để trở thành giải pháp và có thể mất nhiều thời gian hơn chỉ đơn giản là chờ hết thời gian. Câu trả lời chính xác cho câu hỏi của anh ấy là 'Không', nhưng SO sẽ không cho phép bạn gõ hai câu trả lời :-)
Chris Huang- Leaver

6
ồ được rồi, lần tới, một số quy trình bị treo trên SIGTERM tôi sẽ chỉ đập vỡ máy tính của mình thay vì sửa nó.
Longpoke

Khái quát của điều này là "khởi động lại dịch vụ mạng". Vị trí cụ thể là dành riêng cho /etc/init.d/networkingnền tảng (Debian?), Vì vậy dòng lệnh chính xác sẽ khác (đôi khi khá triệt để) cho các hệ thống khác. Tôi đồng ý với những người bình luận khác rằng điều này có vẻ như quá mức nghiêm trọng và rõ ràng gây rối cho bất kỳ dịch vụ mạng không liên quan nào.
tripleee

51

Tôi không biết bạn có mã nguồn của chương trình cụ thể mà bạn đang chạy hay không, nhưng nếu vậy bạn chỉ có thể đặt SO_REUSEADDR qua setsockopt(2)đó cho phép bạn liên kết trên cùng một địa chỉ cục bộ ngay cả khi ổ cắm ở trạng thái TIME_WAIT (trừ khi đó Ổ cắm đang tích cực lắng nghe, xem socket(7)).

Để biết thêm thông tin về trạng thái TIME_WAIT, hãy xem Câu hỏi thường gặp về ổ cắm Unix .


nhưng tôi đã không nhận được lỗi đã bị ràng buộc. Khi tôi thực hiện lại chương trình, nó lắng nghe trong bài (123456) tôi cũng có thể thấy rằng hệ thống đang hiển thị TIME_WAIT cho cổng đó nhưng tôi vẫn có thể kết nối. tại sao?
Jayapal Chandran

2
Ngay cả với SO_REUSEADDR, vẫn có thể gặp lỗi "Địa chỉ đã sử dụng". Để biết chi tiết, hãy tham khảo trang web nặng-www.harvard.edu / ~ fine / Tech / addrinuse.html .
Jingguo Yao

@WMR SO_REUSEADDRkhông "đóng" ổ cắm. Nó chỉ cho phép bạn sử dụng lại những cái đã được mở. Vì vậy, câu hỏi vẫn là "Làm thế nào để buộc một ổ cắm trong TIME_WAIT?"
Pacerier

Đây là câu trả lời đúng, nhưng câu hỏi không hoàn toàn chính xác. Ít nhất đã giải quyết vấn đề của tôi một cách độc đáo (không giống như khởi động lại toàn bộ mạng phá vỡ tất cả các kết nối khác).
V-Mark

SO_REUSEADDRsẽ cho phép bind()tiến hành; nhưng nếu sau đó bạn muốn nghe ổ cắm đó, listen()sẽ trả lại EADDRINUSEtất cả như cũ. Nói cách khác, câu trả lời này có thể giúp phần mềm máy khách sử dụng các cổng phù du, nhưng không giải quyết được vấn đề cho phần mềm máy chủ.
Sẽ

33

Theo như tôi biết, không có cách nào buộc phải đóng ổ cắm ngoài việc viết một trình xử lý tín hiệu tốt hơn vào chương trình của bạn, nhưng có một tệp / Proc kiểm soát thời gian chờ. Các tập tin là

/proc/sys/net/ipv4/tcp_tw_recycle

và bạn có thể đặt thời gian chờ thành 1 giây bằng cách này:

echo 1 > /proc/sys/net/ipv4/tcp_tw_recycle 

Tuy nhiên, trang này chứa cảnh báo về các vấn đề độ tin cậy có thể có khi đặt biến này.

Ngoài ra còn có một tập tin liên quan

/proc/sys/net/ipv4/tcp_tw_reuse

kiểm soát xem các socket TIME_WAIT có thể được sử dụng lại hay không (có lẽ không có thời gian chờ nào).

Ngẫu nhiên, tài liệu kernel cảnh báo bạn không thay đổi một trong hai giá trị này mà không có 'lời khuyên / yêu cầu của các chuyên gia kỹ thuật'. Mà tôi thì không.

Chương trình phải được viết để thử liên kết với cổng 49200 và sau đó tăng thêm 1 nếu cổng đã được sử dụng. Do đó, nếu bạn có quyền kiểm soát mã nguồn, bạn có thể thay đổi hành vi này để chờ vài giây và thử lại trên cùng một cổng, thay vì tăng dần.


nghĩ rằng hai ví dụ thứ hai nên là s / rw / tw / Tôi sẽ chỉnh sửa, nhưng thiếu đủ đại diện.

1
Lấy từ tài liệu kernel: Chú ý. Cả tcp_tw_recycle và tcp_tw numuse đều có thể gây ra sự cố. Bạn không nên kích hoạt mà không hiểu cấu trúc liên kết mạng ở giữa (các) nút đang sử dụng hoặc sử dụng bởi nút nơi tham số được bật. Các kết nối đi qua các nút nhận biết trạng thái kết nối TCP, như tường lửa, NAT hoặc bộ cân bằng tải có thể bắt đầu giảm khung vì cài đặt. Vấn đề sẽ trở nên rõ ràng khi có số lượng kết nối đủ lớn.

Đặt nó 1hoạt động cho các kết nối trong tương lai, nhưng những kết nối hiện tại đã được mở thì sao?
Pacerier

18

Trên thực tế có một cách để giết một kết nối - killcx . Họ tuyên bố nó hoạt động ở bất kỳ trạng thái nào của kết nối (mà tôi chưa xác minh). Bạn cần phải biết giao diện nơi giao tiếp xảy ra mặc dù, nó dường như giả sử eth0 theo mặc định.

CẬP NHẬT: một giải pháp khác là máy cắt trong kho của một số bản phân phối linux.


3

Một tùy chọn khác là sử dụng tùy chọn SO_Linger với thời gian chờ là 0. Cách này, khi bạn đóng ổ cắm bị buộc đóng, gửi RST thay vì đi vào hành vi đóng FIN / ACK. Điều này sẽ tránh trạng thái TIME_WAIT và có thể phù hợp hơn cho một số mục đích sử dụng.


2
Nó cũng mất bất kỳ dữ liệu gửi đi nào vẫn đang trong quá trình và có thể gây ra lỗi ở đầu kia. Không được khuyến khích.
dùng207421

@EJP Không thành công sớm luôn luôn là cuộc gọi đúng. Mạng không đáng tin cậy, và chiến đấu sẽ làm mọi thứ chậm lại. Một ứng dụng bị lỗi không thể cho rằng bất kỳ dữ liệu nào được đưa ra an toàn.
Tobu

1
Trên thực tế, tôi khuyên bạn nên làm điều này bất cứ ngày nào khi điểm cuối kia là một cổng xe buýt công nghiệp nhúng có lỗi, thực hiện vận chuyển đáng tin cậy ở tầng ứng dụng của mình qua TCP, trong đó giao thông ngăn chặn kết nối không bao giờ đóng trừ khi nhận được RST và do đó lấp đầy giới hạn kết nối trên cổng đó. Đó Tôi đã cho bạn một ví dụ rất cụ thể và rất thực tế rằng, thật đáng buồn, đòi hỏi phải dùng đến những bản hack như thế này.
andyn

@Tobu Mạng không đáng tin cậy, nhưng TCP cố gắng, và làm cho điều đó tệ hơn không cấu thành nên mọi thứ tốt hơn và để TCP thực hiện công việc của mình không cấu thành 'chiến đấu' bất cứ điều gì.
dùng207421

2

Một giải pháp thay thế sẽ là có một số phần mềm chuyển tiếp proxy hoặc cổng đáng tin cậy nghe trên cổng 49200, sau đó chuyển tiếp kết nối đến một trong một số trường hợp của chương trình ít tin cậy hơn của bạn bằng cách sử dụng các cổng khác nhau ... HAPROXY.

Ngẫu nhiên cổng kết nối của bạn khá cao. Bạn có thể thử sử dụng một cái không sử dụng ngay trên phạm vi 0-1024. Hệ thống của bạn ít có khả năng sử dụng số cổng thấp hơn làm cổng phù du.


0

TIME_WAIT là vấn đề phổ biến nhất trong kiến ​​trúc máy chủ lập trình socket. Đợi vài giây cố gắng định kỳ là giải pháp tốt nhất cho nó. Đối với các ứng dụng thời gian thực, họ cần máy chủ phải thức dậy ngay lập tức Có tùy chọn SO_REUSEADDR cho họ.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.