Trường hợp kênh truyền có xảy ra lỗi bit, ta cần tạo ra một phiên bản khác cho giao thức truyền tin tin cậy: rdt2.0. Giao thức rdt2.0 phát hiện lỗi bit bằng cách kiểm tra checksum.

Automatic Repeat Request

Tuy nhiên, khi đã biết gói tin bị lỗi, làm cách nào để bên gửi có thể gửi lại gói tin?

Câu trả lời là sử dụng nguyên lý Automatic Repeat Request (ARQ - yêu cầu lặp lại tự động). Nguyên lý này hoạt động như sau: khi bên gửi gửi một gói tin, nó chờ cho gói tin đó được gửi thành công thì mới tiến hành gửi các gói tin tiếp theo. Nếu gói tin chưa được gửi thành công, gói tin sẽ được gửi lại một cách tự động.

Cụ thể hơn, khi bên gửi gửi gói tin thành công, bên nhận sẽ trả lời bằng một gói tin phản hồi có tên là ACK (Acknowlegdement), giúp cho bên gửi biết được gói tin đã được gửi thành công.

Trong trường hợp gói tin nhận được có lỗi, bên nhận sẽ gửi một gói tin có tên là NACK (hay NAK) (Negative Acknowlegdement). Gói tin này được dùng để thông báo cho bên gửi biết rằng gói tin mà nó gửi có lỗi và cần phải được gửi lại.

Bên gửi sẽ chờ gói tin từ tầng giao thức bên trên, mà cụ thể là từ các ứng dụng. Khi sự kiện rdt_send(data) xảy ra, bên gửi sẽ tạo ra gói tin bao gồm dữ liệu và checksum (make_pkt(data, checksum)). Gói tin này sẽ được gửi đi cho bên nhận (udt_send(sndpkt)).

Khi bên nhận nhận được gói tin (rdt_rcv(rcvpkt)), nó sẽ kiểm tra lỗi của gói tin.

  • Trường hợp gói tin không có lỗi (notcorrupt(rcvpkt)): bên nhận sẽ trích xuất dữ liệu (extract(rcvpkt,data)), chuyển dữ liệu lên tầng trên (deliver_data(data)) và gửi lại gói tin ACK cho bên gửi (udt_send(ACK)). Khi thấy gói tin phản hồi của bên nhận là ACK (isACK(rcvpkt)), bên gửi sẽ tiếp tục lắng nghe sự kiện rdt_send(data) ở tầng giao thức bên trên.

    ![[rdt2.0-with-no-errors.webp|#x-small]]

  • Trong trường hợp gói tin có lỗi (corrupt(rcvpkt)): bên nhận sẽ tiến hành gửi gói tin NAK cho bên gửi (udt_send(NAK)). Nếu thấy gói tin nhận được là NAK (isNAK(rcvpkt)), bên gửi sẽ tiến hành gửi lại gói tin cuối cùng mà nó đã gửi.

    ![[rdt2.0-corrupted-packet.webp|#x-small]]

Có thể thấy, khi bên gửi đang ở trong trạng thái chờ gói tin ACK hay NAK, nó sẽ không thể nhận dữ liệu từ tầng giao thức bên trên thông qua sự kiện rdt_send(data). Sự kiện này chỉ được xuất hiện khi nó đã nhận được gói tin ACK từ bên nhận. Vì thế, bên gửi sẽ không gửi tiếp các gói tin tiếp theo cho đến khi biết chắc chắn rằng bên nhận đã nhận được chính xác gói tin hiện hành. Do đó, giao thức rdt2.0 còn được gọi là giao thức stop and wait (dừng và chờ).

Sequence Number

Fail

Giao thức rdt2.0 tồn tại một vấn đề, xảy ra khi gói tin ACK hoặc gói tin NAK bị lỗi. Lúc này, dù cho nhận được ACK hay NAK, bên gửi cũng không biết gói tin mà nó vừa gửi đi có thành công hay không.

Ta có thể thêm checksum vào gói tin ACK hoặc NAK để kiểm tra lỗi cho các gói tin này. Khi có lỗi xảy ra ở ACK hoặc NAK, chúng ta có thể:

  • Gửi một gói tin phản hồi cho biết ACK hay NAK có bị lỗi bit hay không. Tuy nhiên, gói tin vẫn có khả năng bị lỗi bit, ta lại quay về vấn đề ban đầu.
  • Gửi thêm thông tin vào gói tin gửi đi để bên nhận có thể phục hồi dữ liệu nếu như phát hiện có lỗi bit xảy ra trong gói tin nhận được.
  • Bên gửi gửi lại gói tin cuối cùng nếu ACK hoặc NAK bị lỗi. Tuy nhiên, cách này có thể dẫn đến trường hợp các gói tin mà bên nhận nhận được bị trùng. Vì bên nhận không biết gói tin báo nhận (ACK hay NAK) mà nó vừa gửi có chính xác hay không, nên nó sẽ không biết gói tin mà bên gửi vừa gửi lại là gói tin mới đến (không bị trùng) hay là gói tin gửi lại (bị trùng).

Success

Để giải quyết vấn đề này, ta cần đánh số các gói tin để nhận biết sự trùng lặp. Bên nhận sẽ có trách nhiệm loại bỏ gói tin bị trùng lặp (không chuyển lên tầng trên). Cụ thể hơn, ta sẽ thêm vào các gói tin một số thứ tự tiếp nối (sequence number). Bằng cách thêm sự cải tiến này, ta tạo ra giao thức rdt2.1.

Trạng thái của bên gửi sẽ tăng lên gấp đôi, tùy vào số lượng gói tin cần gửi. Khi bên gửi gửi một gói tin đến bên nhận (giả sử gói tin 0), nó sẽ chuyển sang trạng thái chờ gói tin báo nhận (ACK hoặc NAK). Nếu gói tin mà bên gửi gửi đi không có lỗi, bên nhận sẽ chuyển sang trạng thái chờ gói tin tiếp theo (gói tin 1) và gửi về gói tin báo nhận.

Nếu gói tin báo nhận của gói tin 0 bị lỗi (corrupt(rcvpkt)), nó sẽ gửi lại gói tin cuối cùng (cũng là gói tin 0) và tiếp tục chờ ACK hay NAK của gói tin 0 (vòng lặp ở góc phần tư thứ nhất trong hình dưới).

![[rdt2.1-sender.webp|#x-small]]

Quay lại bên nhận, khi gói tin mà nó nhận được không có số thứ tự mà nó mong muốn (gói tin 1) thông qua hàm has_seq1, nó sẽ tiến hành gửi lại ACK hoặc NAK cho bên gửi (hai vòng lặp bên phải của hình dưới). Như vậy, với số thứ tự này, bên nhận có thể biết được gói tin này là gói tin mới đến hay là gói tin đã nhận.

![[rdt2.1-receiver.webp|#x-small]]

Nếu gói tin ACK hoặc NAK không có lỗi, bên gửi sẽ bước sang trạng thái chờ sự kiện (rdt_send(data)) cho gói tin tiếp theo (gói tin 1).

Do chúng ta chỉ giả sử các gói tin bị lỗi bit chứ không bị mất gói, nên không cần phải thêm sequence number vào các gói tin ACK hay NAK. Vì bên nhận tự biết gói tin ACK hay NAK mới đến là gói tin phản hồi cho gói tin vừa gửi. Dẫn đến, chỉ cần 1 bit (gồm hai số 01) là đủ cho trường số thứ tự.

Note

Vì không có gói tin phản hồi cho gói tin ACK hoặc NAK, bên nhận sẽ không biết được gói tin phản hồi mà nó vừa gửi có thành công hay không cho đến khi nhận được gói tin mới.

Điều này dẫn đến việc bên nhận sẽ không biết thông tin về gói phản hồi cuối cùng nếu nó đã được gửi thành công đến bên gửi. Giả sử, bên gửi cần gửi tổng cộng n gói tin, bên nhận sẽ không biết được thông tin của gói tin phản hồi cho gói tin thứ n mà nó vừa gửi cho bên gửi đã thành công. Nó chỉ biết được khi gói tin phản hồi đó bị lỗi.

NAK-free

Chúng ta có thể thay thế gói tin NAK thành gói tin ACK nhưng vẫn thu được hiệu quả tương tự như giao thức rdt2.1. Giao thức không sử dụng NAK này được gọi là giao thức NAK-free, hay rdt2.2.

Giao thức rdt2.2 thêm số thứ tự của gói tin đang phản hồi vào gói tin ACK. Điều này giúp bên gửi biết được gói tin ACK mà nó nhận được đang phản hồi cho gói tin nào.

![[rdt2.2-receiver.webp|#x-small]]

Trường hợp gói tin mà bên nhận nhận được bị lỗi, nó sẽ gửi lại ACK cho gói tin nhận được cuối cùng mà không bị lỗi. Nói cách khác, giao thức rdt2.2 chỉ gửi lại ACK mới khi nào gói tin mà bên nhận nhận được không bị lỗi.

![[rdt2.2-sender.webp|#x-small]]

Example

Ví dụ, nếu gói tin 0 đã nhận thành công và gói tin 1 bị lỗi, bên nhận sẽ gửi lại gói tin ACK của gói tin 0. Trong trường hợp này, khi bên gửi nhận được hai gói tin ACK của cùng một gói tin (gói tin 0), bên gửi sẽ biết được rằng bên nhận đã nhận được gói tin 1 (hay gói tin sau gói tin được phản hồi ACK hai lần) bị lỗi.