12.5. Độ tin cậy -- chuỗi số, ACK, truyền lại

Lớp đóng khung phát hiện lỗi thông qua CRC. Lớp độ tin cậy chuyển đổi "lỗi đã phát hiện" thành "ứng dụng không bao giờ nhận được dữ liệu bị hỏng" bằng cách thương lượng truyền lại khi một gói tin không đến nguyên vẹn.

12.5.1. Số thứ tự

Mỗi tiêu đề gói tin mang một số thứ tự một byte, riêng biệt cho mỗi chiều truyền. Bên gửi tăng bộ đếm trước khi truyền; bên nhận kiểm tra rằng số thứ tự của mỗi gói tin nhận được là số trước đó cộng thêm một (theo modulo 256).

Có ba trường hợp có thể xảy ra tại bên nhận thay vì một gói tin đúng thứ tự:

  • Số thứ tự mong đợi, với CRC hợp lệ. Gói tin được chuyển lên lớp tiếp theo.

  • Số thứ tự mong đợi, với CRC không hợp lệ. Bên nhận bỏ gói tin và (nếu ACK đã được thương lượng) gửi NAK yêu cầu truyền lại.

  • Số thứ tự cao hơn một so với mong đợi, với CRC hợp lệ. Bên nhận biết gói tin trước đã bị mất; nó gửi NAK tham chiếu đến số thứ tự bị mất và lưu tạm gói tin mới.

Trường hợp trùng lặp (một lần truyền lại đến sau khi bản gốc cuối cùng đã đi qua) được xử lý bằng cách kiểm tra so với bộ đếm mong đợi: nếu số thứ tự nhỏ hơn số mong đợi, gói tin là bản sao và bên nhận loại bỏ nó sau khi gửi ACK mà bên gửi rõ ràng chưa nhận được lần đầu.

12.5.2. ACK và NAK

Hai bit cờ trong tiêu đề gói tin mang lưu lượng độ tin cậy chính:

  • ACK_REQ được đặt trên một gói tin đi ra nghĩa là "Tôi muốn nhận xác nhận." Các gói tin dữ liệu thường đặt cờ này; các ping trạng thái và sự kiện một lần có thể không.

  • ACK được đặt trên một gói tin nghĩa là "gói tin này là xác nhận cho số thứ tự trong tiêu đề." Nó không mang tải trọng riêng.

  • NAK được đặt nghĩa là "gói tin này từ chối một gói tin trước" -- thường do CRC sai hoặc lỗ hổng số thứ tự. Tiêu đề chỉ cho bên gửi biết số thứ tự nào cần truyền lại.

Bên gửi chạy vòng lặp dừng và chờ: nó truyền một gói tin yêu cầu xác nhận, sau đó chờ ACK (hoặc NAK) tương ứng trước khi gửi gói tiếp theo. Mô hình một gói tin đang chờ giữ trạng thái bên gửi bị giới hạn -- vài trăm byte trên các cam nhỏ nhất -- và phù hợp với vai trò của giao thức như một kênh điều khiển giữa hai điểm cuối thay vì một đường ống tối ưu hóa thông lượng. Khi nhận NAK, bên gửi truyền lại cùng gói tin với cờ RTX được đặt để bên nhận biết đây là lần thử lại.

12.5.3. Thời gian chờ truyền lại

Nếu không có ACK hay NAK nào đến trong thời gian chờ truyền lại, bên gửi tự động truyền lại gói tin đang chờ. Thời gian chờ mặc định là 500 ms và tăng gấp đôi sau mỗi lần thử lại liên tiếp (1 giây, 2 giây, ...). Sau số lần thử lại được cấu hình -- mặc định là ba -- bên gửi bỏ cuộc và báo lỗi truyền tải lên ứng dụng.

Tăng gấp đôi thời gian chờ là mẫu exponential backoff tiêu chuẩn. Thời gian chờ ngắn ban đầu giúp phát hiện gói tin bị mất nhanh chóng; việc tăng gấp đôi có nghĩa là một host bận trong vài trăm mili giây không kích hoạt một loạt bản sao làm tăng thêm tải.

12.5.4. Cấu hình độ tin cậy

Cả hai đầu có thể tắt từng phần của lớp độ tin cậy, theo thỏa thuận, khi ứng dụng có thể chấp nhận mất dữ liệu:

  • protocol.init(ack=False) vô hiệu hóa ACK theo từng gói tin. Bên gửi truyền và quên; bên nhận chuyển bất cứ thứ gì đến. Phù hợp cho dữ liệu cảm biến phát trực tuyến khi một mẫu cũ là chấp nhận được.

  • protocol.init(seq=False) tắt theo dõi số thứ tự, điều này cũng đồng nghĩa tắt ACK. Chỉ hữu ích trên các phương tiện truyền tải hoàn toàn tin cậy.

  • protocol.init(crc=False) tắt xác thực CRC nhưng giữ nguyên phần còn lại của đóng khung. Chỉ đáng làm khi bản thân phương tiện truyền tải đủ mạnh để không xảy ra lỗi CRC.

Các giá trị mặc định -- mọi thứ bật -- là điểm khởi đầu đúng đắn cho bất kỳ phiên gỡ lỗi host-to-cam nào. Khi ứng dụng đưa vào sản xuất, các sự đánh đổi trở nên đặc thù với dữ liệu và phương tiện truyền tải của nó.

12.5.5. Các mã trạng thái

Khi một lỗi truyền tải lan truyền lên đến mã ứng dụng, nó đến dưới dạng mã trạng thái. Thư viện giao thức định nghĩa mười loại:

  • SUCCESS -- thao tác hoàn thành.

  • FAILED -- lệnh thất bại vì lý do không xác định.

  • INVALID -- bên nhận từ chối lệnh hoặc một trong các đối số của nó.

  • TIMEOUT -- bộ định thời thử lại đã hết.

  • BUSY -- cam đang bận (thường là kênh bị khóa).

  • CHECKSUM -- CRC tiêu đề hoặc tải trọng không khớp.

  • SEQUENCE -- số thứ tự không đúng thứ tự vượt quá khả năng phục hồi của lớp.

  • OVERFLOW -- một tải trọng vượt quá mức tối đa đã thương lượng.

  • FRAGMENT -- một thông điệp nhiều mảnh đến với các mảnh bị thiếu.

  • UNKNOWN -- một trường hợp phòng thủ bao quát cho các điều kiện thực sự bất ngờ.

Mã host gọi channel_read() thấy chúng dưới dạng ngoại lệ Python; mã ứng dụng phía cam đã chọn xử lý lỗi tùy chỉnh thấy chúng dưới dạng giá trị trả về từ các hàm gọi lại backend. Hầu hết các ứng dụng cam không cần nhìn vào các mã trạng thái -- thư viện xử lý việc thử lại, và chỉ các lỗi thực sự không thể phục hồi (ví dụ: bản thân phương tiện truyền tải đã biến mất) mới đến ứng dụng.

Khi đóng khung đã sẵn sàng để phát hiện lỗi và độ tin cậy đã sẵn sàng để phục hồi từ đó, công việc ở cấp độ dây đã hoàn thành. Mã ứng dụng thấy các gói tin được đóng khung, có thứ tự, nguyên vẹn; các byte bên trong chúng được tự do mang ý nghĩa bất kỳ mà kênh phía trên muốn.