2011-02-07 7 views
17

Tôi có một máy khách và máy chủ sử dụng boost::asio không đồng bộ. Tôi muốn thêm một số thời gian chờ để đóng kết nối và có khả năng thử lại nếu có sự cố.Thực hiện bất kỳ việc tăng cường nào :: asio async gọi tự động hết giờ?

Suy nghĩ ban đầu của tôi là bất kỳ lúc nào tôi gọi hàm async_ tôi cũng nên bắt đầu deadline_timer để hết hạn sau khi tôi mong đợi thao tác không đồng bộ hoàn tất. Bây giờ tôi tự hỏi nếu điều đó là cần thiết trong mọi trường hợp.

Ví dụ:

  • async_resolve có lẽ sử dụng phân giải của hệ thống trong đó có timeouts xây dựng vào nó (ví dụ RES_TIMEOUT trong resolv.h có thể ghi đè bởi cấu hình trong /etc/resolv.conf). Bằng cách thêm bộ hẹn giờ của riêng tôi, tôi có thể xung đột với cách người dùng muốn người giải quyết của mình hoạt động.

  • Đối async_connect, các connect(2) syscall có một số loại thời gian chờ xây dựng vào nó

  • , vv

Vì vậy đó (nếu có) async_ cuộc gọi được đảm bảo để gọi xử lý của họ trong một " Khung thời gian hợp lý? Và nếu một hoạt động [có thể] không timeout sẽ xử lý được thông qua các lỗi basic_errors::timed_out hoặc cái gì khác?

+0

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

+0

+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? –

Trả lời

29

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 timeoutoptions 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ó.

+2

Câu trả lời này phải là câu trả lời ... cung cấp nhiều chi tiết có liên quan hơn so với câu trả lời ngắn của tôi. – diverscuba23

10

Hai cuộc gọi này CÓ THỂ có thời gian chờ được chuyển đến trình xử lý của bạn, nhưng bạn có thể được giả mạo ở khoảng thời gian cần thiết trước một trong những lần đó. (Tôi biết tôi đã để cho một kết nối chỉ ngồi và cố gắng kết nối trên một cuộc gọi kết nối duy nhất trong hơn 10 phút với boost::asio trước khi giết chết quá trình). Ngoài ra, các cuộc gọi async_readasync_write không có thời gian chờ được liên kết với chúng, vì vậy nếu bạn muốn có thời gian chờ trên số lần đọc và viết của mình, bạn sẽ vẫn cần số deadline_timer.

+1

+1 khi nghi ngờ, hãy rõ ràng. Thêm bộ hẹn giờ của riêng bạn nếu hành vi không được ghi lại. –

+0

Đúng. Đây là khá nhiều những gì tôi tìm thấy. Mặc dù 'async_read' và' async_write' * do * có thời gian chờ theo nghĩa rằng quá trình truyền lại quá mức sẽ khiến kết nối đóng sau hàng chục phút. – eater