9.10. TCP -- luồng byte đáng tin cậy

TCP, giao thức điều khiển truyền dẫn, là dịch vụ tầng vận chuyển còn lại trên nền IP. Nếu UDP được mô tả ngắn gọn là "gửi gói tin và hy vọng", thì TCP là "mở một kết nối giữa hai điểm cuối và xem nó như một đường ống hai chiều của các byte mà phía kia chắc chắn nhận được, đúng thứ tự, và chính xác một lần". Hầu hết lưu lượng internet sử dụng nó, và phần lớn những gì camera làm trên mạng cũng dùng nó.

9.10.1. Những gì TCP bổ sung cho IP

TCP làm nhiều hơn UDP rất nhiều. Nó bổ sung:

  • Một kết nối. Trước khi dữ liệu được truyền, hai điểm cuối trao đổi một chuỗi bắt tay ngắn để xác nhận họ đang giao tiếp. Kết nối có một trạng thái -- "mở", "đóng một nửa", "đóng" -- mà cả hai bên đều theo dõi.

  • Phân phối đáng tin cậy. Mỗi byte mà bên gửi đặt vào đều được bên nhận xác nhận. Bất kỳ thứ gì không được xác nhận trong một khoảng thời gian chờ sẽ được gửi lại. Ứng dụng không bao giờ thấy một byte bị mất; nó thấy độ trễ trong khi giao thức gửi lại.

  • Phân phối đúng thứ tự. Các byte đến theo đúng thứ tự chúng được gửi. Ngay cả khi các gói tin đến bên nhận không đúng thứ tự, TCP sắp xếp lại chúng trước khi ứng dụng đọc.

  • Kiểm soát luồng. Nếu bên nhận chậm, bên gửi được yêu cầu làm chậm lại; kết nối thích nghi với tốc độ của đầu yếu hơn.

  • Kiểm soát tắc nghẽn. Nếu mạng ở giữa bắt đầu bỏ gói tin, bên gửi lùi lại cho đến khi mọi thứ phục hồi. Điều này ngăn bất kỳ một kết nối nào làm sụp đổ một liên kết bão hòa.

Tất cả những điều này là tự động. API Python mà ứng dụng sử dụng chỉ đơn giản là send(bytes)recv(n); TCP xử lý mọi thứ còn lại bên dưới.

9.10.2. Quá trình bắt tay

Một kết nối TCP mở bằng trao đổi ba bước trước khi bất kỳ dữ liệu nào được phép đi qua:

A diagram with two columns labelled "client" and "server". An arrow from client to server labelled "SYN", then an arrow from server to client labelled "SYN-ACK", then an arrow from client to server labelled "ACK". Below that a thick arrow labelled "data flowing both ways".

Quá trình bắt tay ba bước. Khi cả hai bên đã xác nhận, kết nối được mở và dữ liệu có thể truyền.

Client gửi gói tin SYN (đồng bộ hóa) yêu cầu mở. Server trả lời bằng SYN-ACK (đồng bộ hóa + xác nhận), chấp nhận. Client gửi ACK cuối cùng để xác nhận. Sau lượt đi-về này, cả hai bên đồng ý rằng kết nối đang mở và đã đồng bộ hóa bộ đếm của họ để theo dõi những byte nào đã được gửi và nhận.

Quá trình bắt tay có thời gian trễ một lượt đi-về trước khi byte hữu ích đầu tiên được truyền qua. Đối với mạng cục bộ, đó là một mili giây; đối với các kết nối xuyên lục địa, đó là khoảng 100 ms. Đây là chi phí chính của TCP và lý do tại sao các thông điệp ngắn, một lần đôi khi nên dùng UDP thay thế.

9.10.2.1. Quá trình đóng kết nối

Các kết nối TCP cũng đóng bằng trao đổi (một FIN từ mỗi bên). Một trong hai đầu có thể đóng một nửa kết nối của nó một cách độc lập; trạng thái half-closed khi một bên đã hoàn thành việc gửi nhưng phía kia vẫn đang nói là hợp lệ, dù không phổ biến. Ứng dụng thông thường chỉ gọi close() và để giao thức xử lý chuỗi tắt kết nối.

9.10.3. Những gì TCP không đảm bảo

Một vài điều mà TCP đôi khi bị giả định là làm nhưng thực ra không:

  • Ranh giới thông điệp. Ứng dụng gửi một luồng byte, không phải luồng thông điệp. Hai lệnh gọi send(b"hello") có thể đến dưới dạng một recv() của b"hellohello", hoặc dưới dạng hai recv()s với kích thước khác nhau. Nếu ứng dụng muốn đóng khung thông điệp, nó phải tự thêm khung (một dòng mới, tiền tố độ dài, hoặc bất kỳ thứ gì). Ví dụ, gửi tài liệu JSON qua TCP cần mỗi tài liệu được phân tách bằng dòng mới hoặc một dấu phân cách khác.

  • Mã hóa. TCP mang các byte mà ứng dụng cung cấp, không được mã hóa, suốt toàn bộ quá trình. Nếu nội dung cần được bảo mật, ứng dụng phải bọc kết nối trong TLS (xem Socket mã hóa và TLS).

  • Xác thực. TCP đảm bảo các byte đến nguyên vẹn. Nó không nói gì về ai đã gửi chúng. Xác thực cũng là mối quan tâm của tầng cao hơn.

  • Trạng thái hoạt động trên kết nối yên tĩnh. Nếu cả hai bên không gửi dữ liệu trong một thời gian dài, kết nối về mặt kỹ thuật vẫn mở nhưng không thể phát hiện rằng đầu kia đã bị treo hoặc biến mất. Các thăm dò Keepalive có thể được kích hoạt trên socket để khắc phục điều này khi cần.

9.10.4. Khi nào nên dùng TCP

TCP là câu trả lời đúng cho hầu hết mọi cuộc trò chuyện phù hợp với hình thức "client mở kết nối đến server, họ trao đổi một số byte, kết nối đóng khi xong". Các yêu cầu HTTP và HTTPS, phiên SSH, truy vấn cơ sở dữ liệu, truyền file, tải ảnh lên -- tất cả qua TCP.

Chỉ dùng UDP khi cuộc trò chuyện không phù hợp với hình thức đó: các thông điệp độc lập tự chứa khi mất mát có thể chấp nhận được, lưu lượng multicast, đồng bộ hóa thời gian, tra cứu tên, hoặc các trường hợp cực kỳ nhạy cảm với độ trễ khi chi phí bắt tay quá cao.

Với cổng, UDP và TCP đều có sẵn, câu chuyện tầng vận chuyển đã hoàn tất. API Python hiển thị cả hai được trình bày ở trang tiếp theo: Đối tượng socket.