19 điểm bởi GN⁺ 2025-11-16 | 1 bình luận | Chia sẻ qua WhatsApp
  • TCP (Transmission Control Protocol) là giao thức cốt lõi của Internet, cho phép truyền dữ liệu tin cậy và đúng thứ tự ngay cả trong môi trường mạng không ổn định
  • Trong khi IP chỉ đảm nhiệm việc chuyển dữ liệu giữa các host, TCP thực hiện giao tiếp giữa các tiến trình dựa trên cổng cùng với khôi phục lỗi, truyền lại và kiểm soát thứ tự
  • Thông qua điều khiển luồng (flow control)điều khiển tắc nghẽn (congestion control), TCP điều chỉnh để không vượt quá giới hạn của bộ đệm nhận hoặc băng thông mạng
  • Qua ví dụ máy chủ TCP đơn giản và máy chủ HTTP được triển khai bằng ngôn ngữ C, bài viết giải thích cụ thể quá trình tạo socket, bind, listen, chấp nhận kết nối, gửi và nhận dữ liệu
  • Cấu trúc bên trong của TCP như số thứ tự, số ACK, window, checksum, cờ (SYN/ACK/FIN/RST) là nền tảng cốt lõi giúp Internet vận hành ổn định ngày nay

Sự cần thiết và vai trò của TCP

  • IP chỉ đảm nhiệm việc chuyển gói tin giữa các host, còn để giao tiếp giữa các tiến trình thì cần tầng vận chuyển như TCP/UDP
    • Địa chỉ IP được ví như “tòa nhà”, còn cổng được ví như “căn hộ”, và mỗi ứng dụng giao tiếp bằng cách bind vào một cổng
  • TCP che giấu sự bất ổn của mạng như mất gói, trùng lặp, đảo thứ tự gói tin, đồng thời bảo đảm độ tin cậy bằng truyền lại, checksum
  • Router được giữ ở mức đơn giản, còn độ tin cậy được xử lý tại hai đầu cuối của kết nối, từ đó giảm độ phức tạp của hạ tầng mạng
  • Nhờ cấu trúc này, các dịch vụ Internet chính như HTTP, SMTP, SSH có thể hoạt động ổn định

Điều khiển luồng và điều khiển tắc nghẽn

  • Phía nhận tạm thời lưu dữ liệu thông qua bộ đệm nhận của kernel (receive buffer), và kích thước bộ đệm được cấu hình bằng net.ipv4.tcp_rmem
  • Phía gửi nhận lượng dữ liệu mà phía nhận cho phép thông qua trường window, rồi điều chỉnh lượng truyền tương ứng
  • Để ngăn tắc nghẽn (congestion) do chênh lệch băng thông trên toàn mạng, TCP áp dụng thuật toán điều khiển tắc nghẽn
    • Sau sự kiện congestion collapse xảy ra vào năm 1986, cơ chế ‘back-off’ đã được bổ sung

Ví dụ về máy chủ TCP và máy chủ HTTP

  • Máy chủ echo TCP cơ bản viết bằng ngôn ngữ C nhận dữ liệu đầu vào từ client, thêm “you sent:” rồi gửi lại
    • Sử dụng Berkeley sockets API như socket(), bind(), listen(), accept(), send(), recv()
    • Khi server đang sleep(), dữ liệu từ client sẽ chờ trong bộ đệm nhận và sau đó được xử lý tuần tự
  • Trong ví dụ máy chủ HTTP/1.1 đơn giản, server chấp nhận yêu cầu qua kết nối TCP rồi gửi header HTTP/1.1 200 OK và phần nội dung
    • Số lần yêu cầu được đếm bằng i, và khi gọi curl localhost:8080 sẽ trả về phản hồi dạng “[1] Yo, I am a legit web server”

Cấu trúc segment TCP và các trường cốt lõi

  • Một segment TCP gồm cổng nguồn/đích, số thứ tự, số ACK, kích thước window, checksum, cờ
    • Mỗi cổng được cấp phát 16 bit nên có thể dùng tối đa 64K cổng
    • Kết nối được định danh bằng bộ 5 (5-tuple) gồm (protocol, source IP, source port, destination IP, destination port)
  • Số thứ tự biểu thị phạm vi byte đã được truyền, còn số ACK biểu thị các byte đã được nhận xong
    • Nếu có dữ liệu bị thiếu, ACK sẽ dừng tại điểm đó và sau khi truyền lại sẽ được cập nhật bằng ACK cộng dồn
  • Các bit cờ điều khiển trạng thái kết nối
    • SYN/ACK thiết lập kết nối thông qua 3-way handshake
    • FIN kết thúc kết nối bằng 4-way handshake
    • RST ngắt kết nối ngay lập tức khi có lỗi hoặc kết thúc bất thường
  • Trường window biểu thị lượng dữ liệu có thể nhận, và có thể kiểm tra trạng thái bộ đệm (rb131072, tb16384) bằng lệnh ss
  • Checksum thực hiện phát hiện lỗi bằng cách cộng theo đơn vị 16 bit bên trong segment

Kết luận

  • TCP bảo đảm độ tin cậy, thứ tự và tính toàn vẹn, giúp ứng dụng hoạt động bình thường ngay cả trong môi trường Internet không ổn định
  • Vài chục năm trước, việc truyền chỉ vài KB còn khó khăn, nhưng ngày nay đã phát triển đến mức streaming 4K trở nên phổ biến
  • Sự tinh vi trong thiết kế và triển khai TCP chính là nền tảng cho sự tăng trưởng liên tục của Internet

1 bình luận

 
GN⁺ 2025-11-16
Ý kiến trên Hacker News
  • Nếu cố tạo một luồng dữ liệu đáng tin cậy trên một lớp datagram không đáng tin cậy, thì kết quả cuối cùng sẽ có hình dạng gần như giống hệt TCP
    Những giới hạn ban đầu của TCP là kích thước cửa sổ nhỏ, xử lý gói bị mất chưa tốt, và chỉ quản lý một luồng duy nhất
    Để giải quyết các vấn đề này, SCTPQUIC đã ra đời
    Thuật toán kiểm soát tắc nghẽn không phải là một phần của giao thức mà là đoạn mã chạy ở hai đầu của mỗi kết nối
    Các thuật toán ban đầu (Reno, Vegas, v.v.) tuy đơn giản nhưng đủ hiệu quả, và từ đó đến nay nghiên cứu vẫn tiếp tục để xử lý bộ đệm lớn, RTT dài, tính công bằng, v.v.

    • Tôi cũng nghĩ các nhà phát triển web phải chịu trách nhiệm vì đã không tận dụng tốt đa luồng
      Trước đây tôi từng viết một thư viện bằng JavaScript cho phép điều khiển độ ưu tiên và khả năng hủy của nhiều lượt tải xuống trong một luồng duy nhất
      Tôi còn dùng script GreaseMonkey để tải trước ảnh thu nhỏ của một trang web hẹn hò ở chế độ nền, và cho phép preload theo vị trí cuộn
      Kết quả là vừa giảm tải cho máy chủ vừa cải thiện trải nghiệm người dùng
      Thú vị là tôi đã chia sẻ script đó với một người match, và chúng tôi vẫn ở bên nhau cho đến giờ — gần như là Tinder trước cả khi có Tinder
    • Khi TCP được tạo ra, tư duy chuyển mạch kênh (circuit switching) dựa trên mạng điện thoại vẫn đang chi phối
      TCP là một cấu trúc cung cấp mạch ảo trên mạng chuyển mạch gói, và ý tưởng hiện thực hóa độ tin cậy bằng truyền lại bắt nguồn từ mạng Cylades của Pháp
    • Một trong những khiếm khuyết căn bản của TCP là không thể bảo mật được
      Kẻ tấn công có thể chèn (inject) dữ liệu ở bất kỳ đâu trên mạng hoặc cắt kết nối bằng gói RST
      Chặn RST bằng tường lửa sẽ tăng độ ổn định, nhưng tấn công mất đồng bộ hóa do số thứ tự bị giả mạo vẫn có thể xảy ra
      Vì vậy mọi ứng dụng đều phải tự triển khai tính năng resume trên một kết nối riêng, đồng thời phải gánh cả vấn đề slow start của TCP
      Ngoài ra, bản thân khái niệm tách biệt địa chỉ và cổng cũng bị xem là kém hiệu quả
    • Có những ứng dụng vẫn hoạt động hoàn toàn tốt chỉ với một kết nối TCP duy nhất
      Ví dụ trong DNS over TLS(DoT), có thể gửi đồng thời nhiều truy vấn trên một kết nối TCP và nhận phản hồi không theo thứ tự
      Cách này hiệu quả và lịch sự hơn là mở nhiều kết nối
      Không rõ QUIC có nhanh hơn không, nhưng hỗ trợ phía máy chủ vẫn còn hạn chế
      HTTP/1.1 pipelining cũng làm được điều tương tự nhưng phản hồi sẽ đến theo thứ tự
    • Việc thuật toán kiểm soát tắc nghẽn của TCP nằm ngoài giao thức là một ý tưởng cực kỳ đột phá vào thời điểm đó
      Nhưng nhiều khóa học đại học không nhấn mạnh điều này, khiến người ta thường hiểu lầm rằng TCP chỉ có một thuật toán duy nhất
  • Muốn hỏi xem có ai đặc biệt yêu thích SCTP không
    SCTP là giao thức kết hợp tính hướng thông điệp của UDP với độ tin cậy của TCP, hỗ trợ multi-streamingmulti-homing
    Nó có thể truyền song song nhiều luồng độc lập, nên có thể gửi đồng thời văn bản và hình ảnh của một trang web
    Xem thêm tại Wikipedia: Stream Control Transmission Protocol

    • SCTP chỉ giải quyết được một nửa vấn đề và lại đưa vào thêm nhiều khiếm khuyết mới
      Rốt cuộc lời giải tốt nhất là một lớp độ tin cậy trên UDP, tức QUIC
    • Với tư cách là người thích dùng BSD và làm việc bằng Erlang, tôi rất yêu SCTP
  • Tôi từng thắc mắc liệu có thể gửi trực tiếp gói chỉ bằng IP hay không
    Có vẻ như các router trung gian sẽ từ chối những gói không phải TCP hay UDP

    • Có thể tự thao tác và gửi gói IP trực tiếp
      Với IPv4, chỉ cần chỉ định một giá trị từ 0 đến 255 trong danh sách số giao thức IANA
      Router lõi không kiểm tra trường này, nhưng thiết bị NAT hoặc hạ tầng của ISP có thể kiểm tra
      Giữa hai máy chủ Linux, thậm chí có thể giao tiếp bằng các số thử nghiệm (253, 254)
    • Cũng đừng quên ICMP. Trong IPv6 nó còn quan trọng hơn
      Các giao thức như IPsec, GRE, L2TP cũng không phải TCP/UDP
      Trong môi trường tường lửa hoặc NAT của mạng doanh nghiệp, các giao thức tùy ý có thể bị chặn
      NAT đã phá vỡ nguyên tắc end-to-end, và cuối cùng mọi người bắt đầu đặt mọi thứ lên trên TCP hoặc UDP, hoặc thậm chí trên HTTP
    • Nếu không có NAT hay load balancer thì không sao, nhưng ngày nay chúng quá phổ biến nên IPv6 có thể là lựa chọn tốt hơn
    • Router là thiết bị lớp 3, nên không quan tâm đó có phải TCP/UDP hay không
      Chỉ có ảnh hưởng ở mức làm giảm entropy của băm ECMP
      Cuối cùng điều quan trọng là phía bên kia có hiểu giao thức đó hay không
    • TCP và UDP chỉ là payload của IP, router không phân tích chúng
      Số cổng chỉ đơn thuần là định danh dịch vụ bên trong một nút
  • RUDP(Plan9) là một giải pháp thỏa hiệp tuyệt vời giữa TCP và UDP
    Xem Reliable User Datagram Protocol

  • Nhờ TCP trở thành mặc định, nó bị dùng một cách vô điều kiện ngay cả khi không cần độ tin cậy hay đảm bảo thứ tự
    Giờ đây khi HTTP/3 (dựa trên QUIC) đang lan rộng, tình hình có thể sẽ được cải thiện
    Tuy nhiên QUIC phức tạp hơn rất nhiều, và sức mạnh đó chỉ hữu ích với một số người
    UDP + một lớp mã hóa đơn giản kiểu WireGuard có thể là phương án thay thế tốt hơn

  • TCP là một trong những phát minh vĩ đại của nhân loại, nhưng nó đã không lường trước được sự thống trị của mạng bán kết nối (dựa trên NAT)

    • Có người hỏi: “Ý bạn là NAT à?”
    • Nếu quay về năm 1981 và nói rằng “thay vì dùng địa chỉ toàn cầu, hãy dùng địa chỉ trong một phạm vi giới hạn và khiến một số nút không thể truy cập được”
      Các kỹ sư khi đó hẳn sẽ hỏi tại sao lại cố tình làm mọi thứ phức tạp đến vậy
      Rốt cuộc cấu trúc liên kết bất đối xứng ngày nay và sự phân tách client–server đều bắt nguồn từ kiểu tư duy đó
  • Thuật toán kiểm soát tắc nghẽn của TCP tạo ra những hiệu ứng thú vị mà nhiều lập trình viên không biết
    Khi gửi dữ liệu trên một kết nối mới, truyền ban đầu sẽ chậm, và tốc độ tăng lên được quyết định bởi độ trễ (latency)
    Trong trung tâm dữ liệu, chỉ cần giảm RTT vài micro giây cũng có thể tăng tốc rất lớn
    Hầu hết TCP stack tính mức tăng tốc theo đơn vị segment chứ không phải byte, nên nếu dùng jumbo frame thì tốc độ tăng có thể nhanh hơn gấp 6 lần
    Vì lý do này AWS đầu tư rất nhiều vào độ trễ chuyển mạch thấphỗ trợ jumbo frame
    Các chuyên gia có thể tinh chỉnh những thứ này, nhưng đa số mọi người chỉ thắc mắc vì sao đường truyền 10Gbps lại không đạt được 10Gbps

  • Tự tạo giao thức riêng trên IP từng là việc rất đơn giản
    Chỉ khoảng 15 năm trước, người ta còn có thể dùng Python để tự lắp ráp gói tin và thử nghiệm