+2

Cơ chế client kết nối server tưởng dễ hóa khó

1. Mở đầu

Khi nói đến việc client kết nối với server, nhiều người nghĩ rằng đó chỉ là một quá trình đơn giản: gửi yêu cầu, nhận phản hồi. Nhưng thực tế, đằng sau một kết nối thành công là cả một cơ chế phức tạp với nhiều bước phối hợp giữa client, server và hệ điều hành. Hãy cùng khám phá chi tiết cơ chế này để hiểu rõ hơn về cách mọi thứ vận hành.

2. Luồng xử lý

Trong quá trình phát triển ứng dụng, chúng ta thường đưa các giải pháp tối ưu database, coding,... Một phần rất quan trọng và ít được động chạm đến để phân tích là phần network giao tiếp giữa client và server. Bài viết này sẽ đi sâu vào việc phân tích quy trình khởi tạo kết nối giữa client và server sau đó chúng ta sẽ thử suy nghĩ xem có điểm nào trong toàn bộ quy trình gặp vấn đề gi trong thực tế hay không.

Đầu tiên, chúng ta sẽ phân tích quy trình thiết lập kết nối giữa client và server được mô tả chi tiết trong hình sau:

3. Một số vấn đề thường gặp

Vấn đề bảo mật: Chờ đợi ACK

Lỗ hổng ban đầu: Trong quá trình chờ gói ACK cuối cùng từ client để hoàn tất kết nối, các client độc hại (malicious client) có thể lợi dụng việc này để thực hiện tấn công từ chối dịch vụ (DoS). Cụ thể, client gửi gói SYN, nhận phản hồi SYN-ACK từ server, nhưng không gửi gói ACK cuối cùng. Kết quả là một "slot" trong SYN queue bị chiếm giữ, khiến hàng đợi này nhanh chóng đầy. Kẻ tấn công có thể lặp lại hành vi này nhiều lần, dẫn đến việc server không thể chấp nhận thêm bất kỳ kết nối mới nào.

Giải pháp: SYN Cookies

Vấn đề này đã được giải quyết từ lâu bằng cách sử dụng các cơ chế như SYN Cookies và Timeout. SYN Cookies: Server không lưu trữ trạng thái của kết nối (stateless) mà mã hóa thông tin cần thiết vào SYN-ACK. Chỉ khi client gửi lại ACK, thông tin mới được khôi phục và kết nối hoàn tất. Timeout: Nếu server không nhận được gói ACK trong một khoảng thời gian nhất định (ví dụ: 100-520ms), kết nối sẽ bị hủy.

Điều gì xảy ra nếu không gọi hàm accept?

Nếu bạn không chấp nhận kết nối vì bất kỳ lý do nào: Ứng dụng backend của chúng ta bị treo. Hoặc chúng ta xử lý quá chậm, không gọi accept đủ nhanh do đang xử lý quá nhiều việc. Kết nối sẽ tồn tại trong accept queue cho đến khi: Hết thời gian chờ. Hoặc kernel không thể thêm thêm kết nối hoàn chỉnh nào khác vào accept queue, vì queue này đã đầy.

Điều này gây ra vấn đề nghiêm trọng:

  • Hàng đợi accept đầy khiến kernel không thể xử lý thêm các gói SYN mới từ client.
  • Điều này làm các kết nối mới từ client bị từ chối hoặc thất bại.

Giải pháp

Chúng ta cần:

  • Tối ưu hóa hiệu suất backend: Gọi hàm accept càng nhanh càng tốt để giải phóng các kết nối khỏi accept queue. Tăng cường khả năng xử lý nhiều kết nối cùng lúc, ví dụ: sử dụng mô hình đa luồng hoặc đa tiến trình.
  • Phát hiện và xử lý các kết nối độc hại: Sử dụng dịch vụ như Cloudflare hoặc các giải pháp tương tự. Các dịch vụ này đầu tư nghiên cứu lớn để phân biệt hành vi giữa kẻ tấn công và người dùng hợp lệ, giúp bảo vệ hệ thống của bạn khỏi các cuộc tấn công như DoS/DDoS.
  • Cấu hình accept queue: Tăng kích thước hàng đợi accept để xử lý được nhiều kết nối hơn. Tuy nhiên, điều này chỉ là giải pháp tạm thời; cần kết hợp với các tối ưu hóa khác để đảm bảo hiệu quả lâu dài.

4. Thông tin kết nối

Nếu anh em muốn trao đổi thêm về bài viết, hãy kết nối với mình qua LinkedIn và Facebook:

Rất mong được kết nối và cùng thảo luận!


All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí