Vì vậy, tôi đã thực hiện một số thử nghiệm. Dựa trên kết quả của tôi, rõ ràng là chúng phụ thuộc vào việc triển khai hệ điều hành cơ bản. Để tham khảo, tôi đã thử nghiệm điều này với một hạt nhân Fedora cổ phiếu: 2.6.35.10-74.fc14.x86_64
.
Điểm mấu chốt là async_resolve()
có vẻ là trường hợp duy nhất bạn có thể có thể thoát ra mà không cần đặt deadline_timer
. Đó là thực tế cần thiết trong mọi trường hợp khác cho hành vi hợp lý.
async_resolve()
Một cuộc gọi đến async_resolve()
dẫn đến 4 truy vấn 5 giây ngoài. Trình xử lý được gọi là 20 giây sau khi yêu cầu có lỗi boost::asio::error::host_not_found
.
Trình phân giải của tôi mặc định là hết thời gian chờ là 5 giây với 2 lần thử (resolv.h
), do đó dường như gửi gấp đôi số truy vấn được định cấu hình. Hành vi có thể sửa đổi bằng cách đặt options timeout
và options attempts
trong /etc/resolv.conf
. Trong mọi trường hợp, số lượng truy vấn được gửi là gấp đôi bất cứ điều gì attempts
được đặt thành và trình xử lý được gọi với lỗi host_not_found
sau đó.
Để kiểm tra, máy chủ định cấu hình đơn được định tuyến lỗ đen.
async_connect()
Calling async_connect()
với một điểm đến lỗ đen-chuyển dẫn đến việc xử lý được gọi với các lỗi boost::asio::error::timed_out
sau ~ 189 giây.
Ngăn xếp đã gửi SYN ban đầu và 5 lần thử lại. Lần thử đầu tiên được gửi sau 3 giây, với thời gian chờ thử lại tăng gấp đôi mỗi lần (3 + 6 + 12 + 24 + 48 + 96 = 189). Số lần thử lại có thể thay đổi:
% sysctl net.ipv4.tcp_syn_retries
net.ipv4.tcp_syn_retries = 5
Giá trị mặc định của 5 được chọn để phù hợp với RFC 1122 (4.2.3.5):
[Các tính giờ truyền lại] cho một phân đoạn SYN PHẢI đặt đủ lớn để cung cấp truyền lại phân đoạn trong ít nhất 3 phút. Ứng dụng có thể đóng kết nối (tức là, từ bỏ nỗ lực mở) sớm hơn, tất nhiên.
3 phút = 180 giây, mặc dù RFC dường như không chỉ định giới hạn trên. Không có gì ngăn cản việc triển khai thử lại mãi mãi.
async_write()
Chừng nào của ổ cắm gửi đệm là không đầy đủ, xử lý này luôn gọi ngay lập tức.
Thử nghiệm của tôi đã thiết lập kết nối TCP và đặt hẹn giờ để gọi async_write()
một phút sau đó. Trong phút nơi kết nối được thành lập nhưng trước async_write()
cuộc gọi, tôi đã cố gắng tất cả các loại tình trạng lộn xộn:
- Thiết lập một bộ định tuyến hạ lưu để giao tiếp theo lỗ đen đến đích.
- Xóa phiên trong tường lửa hạ lưu để nó trả lời bằng RST giả mạo từ đích đến.
- Rút Ethernet của tôi
- Chạy
/etc/init.d/network stop
Không có vấn đề gì tôi đã làm, tiếp theo async_write()
ngay lập tức sẽ gọi handler của mình để báo cáo thành công.
Trong trường hợp tường lửa giả mạo các RST, kết nối đã bị đóng cửa ngay lập tức, nhưng tôi không có cách nào để biết rằng cho đến khi tôi đã cố gắng các tiếp theo hoạt động (mà ngay lập tức sẽ báo cáo boost::asio::error::connection_reset
). Trong các trường hợp khác, kết nối sẽ vẫn mở và không báo cáo lỗi cho tôi cho đến khi nó hết thời gian chờ 17-18 phút sau đó.
Trường hợp xấu nhất cho async_write()
là nếu máy chủ được truyền lại và bộ đệm gửi đầy.Nếu bộ đệm đầy, async_write()
sẽ không gọi trình xử lý của nó cho đến khi hết thời gian truyền lại. Linux mặc định là 15 truyền lại:
% sysctl net.ipv4.tcp_retries2
net.ipv4.tcp_retries2 = 15
Hiện giữa truyền lại tăng sau mỗi lần (và được dựa trên nhiều yếu tố như thời gian khứ hồi ước tính của kết nối cụ thể) nhưng được kẹp ở 2 phút. Vì vậy, với 15 lần truyền lại mặc định và thời gian chờ tối thiểu là 2 phút, giới hạn trên là 30 phút để xử lý async_write()
được gọi. Khi được gọi, lỗi được đặt thành boost::asio::error::timed_out
.
async_read()
này không bao giờ nên gọi handler của nó chừng nào kết nối được thiết lập và không có dữ liệu nhận được. Tôi không có thời gian để kiểm tra nó.
Tôi cũng phải dính vào việc tạo ra 'deadline_timer' của riêng mình, vì vậy tôi sẽ rất quan tâm để xem điều đó có cần thiết hay không. – chrisaycock
+1 câu hỏi thú vị. Tôi nghĩ câu trả lời sẽ phụ thuộc phần lớn vào nền tảng. Tôi giả sử bạn quan tâm đến Linux? –