TCP làm gì khi dữ liệu bị mất?
Ai phát hiện dữ liệu bị mất giữa đường?
Kết nối TCP đã mở. Nhưng bắt tay xong không có nghĩa là mạng bên dưới trở nên đáng tin cậy hơn.
Chiếc xe van năm 1977 ở bài về địa chỉ IP vẫn gửi dữ liệu qua mạng vô tuyến. Sóng có thể bị nhiễu, bị che bởi địa hình hoặc mất hẳn khi xe đi vào vùng không phủ sóng.
Một gói tin có thể biến mất giữa đường.
IP không đảm bảo nó sẽ tới nơi. Router cũng không giữ một bản để gửi lại nếu gói bị mất.
Vậy ai phát hiện dữ liệu bị thiếu?
Việc đó nằm ở hai đầu
Có thể thiết kế mỗi mạng bên dưới tự đảm bảo giao đủ dữ liệu. Nếu một gói mất trên mạng vô tuyến, chính mạng vô tuyến phải phát hiện và gửi lại. Mạng vệ tinh, mạng cáp và các mạng khác cũng phải làm tương tự.
Internet không dựa vào điều đó.
IP cung cấp kiểu chuyển phát cố hết sức (best effort). Một gói có thể tới, tới muộn, tới sai thứ tự hoặc không tới.
Nếu ứng dụng cần dữ liệu đầy đủ và đúng thứ tự, hai máy ở hai đầu phải tự kiểm tra.
Đó là việc của TCP.
Cách thiết kế này cũng giúp mạng ở giữa đơn giản hơn. Router không cần nhớ một file đã truyền được bao nhiêu, byte nào đang thiếu hay kết nối đang chờ gì. Nếu một router hỏng, các gói sau thậm chí có thể đi đường khác mà trạng thái của kết nối vẫn nằm ở hai máy đầu cuối.
David Clark gọi một phần của ý tưởng này là chung số phận (fate-sharing): trạng thái quan trọng của cuộc truyền nên nằm cùng với các máy đang sử dụng nó, thay vì phụ thuộc vào một thiết bị trung gian có thể biến mất.
Vậy TCP biết dữ liệu nào đã mất bằng cách nào?
TCP đánh số dòng dữ liệu
Giả sử bên gửi muốn truyền:
HELLO
TCP coi dữ liệu của ứng dụng như một dòng byte liên tục. Mỗi byte có một vị trí trong dòng đó.
Ta đơn giản hóa thành:
H E L L O
1 2 3 4 5
Trong TCP thật, số thứ tự ban đầu không bắt đầu từ 1. Ở đây ta đếm từ 1 để dễ nhìn.
Giả sử dữ liệu được gửi đi nhưng phần chứa byte số 3 bị mất:
đã tới: 1 2 4 5
đang thiếu: 3
Nhờ số thứ tự (sequence number), bên nhận biết byte 4 và 5 nằm sau một khoảng trống.
Nó chưa thể đưa HELLO hoàn chỉnh cho ứng dụng.
ACK cho biết byte tiếp theo đang chờ
Sau khi nhận dữ liệu, TCP gửi lại một lời xác nhận (acknowledgment, ACK).
ACK về cơ bản cho bên gửi biết:
Tôi đã nhận được một dòng liên tục tới đây. Tiếp theo tôi đang chờ byte này.
Nếu bên nhận đã có byte 1 và 2 nhưng byte 3 chưa tới, ACK vẫn cho biết byte tiếp theo nó cần là số 3.
Ngay cả khi byte 4 và 5 đã tới trước.
Bên gửi nhờ đó biết rằng dòng dữ liệu vẫn còn một chỗ chưa được xác nhận.
Dữ liệu tới sai thứ tự có thể được giữ lại
Byte 4 và 5 tới trước byte 3 không có nghĩa chúng phải bị bỏ đi.
TCP có thể giữ chúng lại:
H E _ L O
↑
thiếu 3
Khi phần chứa byte 3 tới, khoảng trống được lấp:
H E L L O
Lúc này TCP mới có một dòng dữ liệu liên tục để đưa lên ứng dụng.
Ứng dụng không phải tự sắp xếp các gói ngoài mạng. Nó chỉ đọc một dòng byte theo đúng thứ tự.
Thiếu quá lâu thì gửi lại
Bên gửi chưa thể quên dữ liệu ngay sau khi gửi. Nó phải giữ phần chưa được xác nhận để có thể gửi lại nếu cần.
TCP đồng thời theo dõi thời gian.
Nếu một phần dữ liệu chờ quá lâu mà vẫn chưa được xác nhận, TCP coi đó là dấu hiệu dữ liệu có thể đã mất và gửi lại (retransmit).
Thử với HELLO, mỗi đoạn mang một byte. Đoạn chứa byte số 3 sẽ mất giữa đường:
- H #1 ✓
- E #2 ✓
- L #3 ✓
- L #4 ✓
- O #5 ✓
Bên gửi
- H #1 ✓
- E #2 ✓
- L #3 ✓
- L #4 ✓
- O #5 ✓
Bên nhận
Ứng dụng nhận: HELLO
Không router nào cần quay lại báo:
Gói số 3 vừa bị rơi.
Hai đầu tự phát hiện vấn đề từ số thứ tự và ACK.
Mạng vẫn có thể làm mất gói. TCP không ngăn chuyện đó xảy ra.
TCP chỉ đảm bảo rằng phần dữ liệu còn thiếu có thể được gửi lại để dòng byte cuối cùng được ghép đủ.
Cái giá là thời gian: nếu phải gửi lại, ứng dụng phải chờ lâu hơn.
Router không cần biết những chuyện này
Router ở giữa không cần biết file đã truyền được bao nhiêu phần trăm.
Nó không cần biết byte nào đang thiếu.
Nó cũng không giữ trạng thái của kết nối TCP.
Những việc đó nằm ở hai máy đầu cuối.
Đây là một ví dụ của nguyên lý đầu cuối (end-to-end principle): việc kiểm tra dữ liệu cuối cùng có đầy đủ hay không phải được thực hiện ở chính hai đầu đang sử dụng dữ liệu.
Nhờ vậy, một mạng có thể làm mất gói nhưng ứng dụng vẫn có thể nhận được một file đầy đủ.
Thứ TCP đưa cho ứng dụng không còn giống những gói tin có thể mất hoặc tới lộn xộn bên dưới nữa.
Ứng dụng chỉ thấy:
một dòng byte đầy đủ và đúng thứ tự.
Nhưng còn một vấn đề khác.
Giả sử không gói nào bị mất:
Bên gửi đẩy dữ liệu: rất nhanh
Ứng dụng bên nhận đọc: rất chậm
TCP không thể cứ tiếp tục nhét dữ liệu vào máy nhận mãi.
Nếu bên gửi nhanh hơn bên nhận xử lý thì sao?
Nguồn bài viết (7)
- Brief History of the Internet · Internet Society
- The Design Philosophy of the DARPA Internet Protocols (David D. Clark, SIGCOMM '88) · ACM SIGCOMM
- End-to-End Arguments in System Design (Saltzer, Reed, Clark, 1984) · ACM / MIT
- RFC 9293 — Transmission Control Protocol (TCP) · IETF
- RFC 6298 — Computing TCP's Retransmission Timer · IETF
- RFC 5681 — TCP Congestion Control · IETF
- RFC 2018 — TCP Selective Acknowledgment Options · IETF