+1

Cách xây dựng máy chủ TCP thô khi mỗi mili giây đều quan trọng

TCP cực kỳ nhanh. Khi mỗi mili-giây đều quan trọng - trong các hệ thống thời gian thực, công cụ cơ sở dữ liệu, bộ nhớ đệm, message brokers, phần mềm quan trọng – các hệ thống này giao tiếp bằng TCP thô.

Bởi vì khi tốc độ và khả năng kiểm soát là ưu tiên hàng đầu, thì sự trừu tượng chính là kẻ thù.

HTTP, dù đáng tin cậy, nhưng đi kèm với chi phí. Nó hoạt động trên TCP và thêm nhiều lớp xử lý: headers, footers, mã hóa, giải mã – độ trễ bắt đầu xuất hiện.

Chi phí của HTTP Overhead

Hãy xem điều gì xảy ra bên trong khi một HTTP response được gửi đi:

StatusCode        : 200
StatusDescription : OK
Content           : {72, 101, 108, 108...}
RawContent        : HTTP/1.1 200 OK
                    Content-Length: 12
                    Hello world!
Headers           : {[Content-Length, 12]}
RawContentLength  : 12

HTTP là giao thức dựa trên văn bản (text-based). Nó yêu cầu phân tích cú pháp, xử lý bổ sung và nhiều bước trước khi dữ liệu thực sự được truyền qua mạng.

So sánh với TCP thô:

const data = Buffer.from("hello")
tcpserverClientConnection.write(data)

Ta thấy rằng:

  • Không có overhead.
  • Không cần mã hóa hoặc giải mã thêm.
  • Không có giao thức phức tạp.
  • Chỉ đơn thuần là dữ liệu, đi từ điểm A đến điểm B nhanh nhất có thể.

UDP thậm chí còn nhanh hơn TCP – được sử dụng trong các ứng dụng thời gian thực như trò chơi P2P và phát trực tiếp video. Nhưng UDP đánh đổi độ tin cậy để lấy tốc độ.

TCP: Ba tầng dưới HTTP

Càng đi sâu vào hệ thống, bạn càng có nhiều quyền kiểm soát:

HTTP
└── Encoding/Decoding
    └── Binary/Buffers
        └── TCP

Nếu bạn muốn trở thành một kỹ sư hơn là chỉ xây dựng CRUD, nếu bạn muốn thiết kế các hệ thống mạng – thì việc hiểu TCP là điều bắt buộc.

TCP: Giới thiệu thực tiễn

Một trong những điều mạnh mẽ nhất về Node.js? Nó chính là những gì bạn tạo ra từ nó.

  • Cần một CRUD API? ✅
  • Cần một hệ thống xử lý dữ liệu? ✅
  • Cần mở rộng nó bằng C++? ✅

Cách bạn nhìn nhận Node.js sẽ quyết định những gì bạn có thể xây dựng.

Bắt đầu đơn giản: Làm việc trực tiếp với bộ nhớ thô

const data = Buffer.alloc(4)
data.writeInt32BE(7) // Store the number 7 as raw binary

Chuyện gì đang xảy ra ở đây?

  • Cấp phát 4 byte (32 bit) bộ nhớ.
  • Ghi số nguyên 32-bit (7) vào bộ nhớ dưới dạng dữ liệu thô.

Đây là JavaScript ở cấp độ thấp hơn.

Bây giờ, hãy đi sâu hơn và xây dựng một Raw TCP Server!

Viết máy chủ TCP trong Node.js

Dưới đây là cách bạn có thể tạo một Raw TCP Server trong Node.js:

// server.js
const net = require("node:net")
const server = net.createServer((c) => {
    c.on("data", (data) => console.log(data))
    c.on("error", (err) => console.log(err))
    c.write("hello world") // Implicitly converts string to a buffer
    c.end()
})
server.listen(3000)

Chạy máy chủ và thử kết nối bằng curl

curl http://localhost:3000

ERROR: Lỗi có thể gặp phải

curl: The server committed a protocol violation.

Tại sao? Vì chúng ta đang giao tiếp bằng TCP thô, trong khi curl mong đợi một giao thức HTTP có headers, status codes và structured responses.

Dưới đây là một TCP Client để kết nối với máy chủ của chúng ta:

//client.js
const net = require("node:net")
const c = net.createConnection({ port: 3000, host: "localhost" })
c.on("data", (data) => console.log(data))
c.write("hello")

Chạy máy chủ và máy khách, và đây là kết quả:

<Buffer 48 65 6c 6c 6f 20 77 6f 72 6c 64 21>

Mặc dù dữ liệu được gửi đi dưới dạng raw bytes, nhưng vì chỉ là chuỗi (string), nên chúng ta có thể dễ dàng giải mã nó:

c.on("data", (data) => {
   console.log(data.toString())  // "hello world"
})

Sức mạnh của Raw TCP

Tại sao điều này quan trọng?

Tại sao bạn nên quan tâm đến TCP thô?

Bởi vì một số hệ thống huyền thoại mà bạn sử dụng hàng ngày đều chạy trên TCP thuần túy, không cần HTTP:

  • MySQL clients – Giao tiếp trực tiếp với cơ sở dữ liệu.
  • RabbitMQ – Hệ thống hàng đợi tin nhắn (message queue) tốc độ cao.
  • Redis – Bộ nhớ đệm hiệu suất cao, giao tiếp bằng TCP raw commands.
  • Neo4j – Cơ sở dữ liệu đồ thị mạnh mẽ.
  • Email clients (SMTP, IMAP, POP3) – Gửi và nhận email thông qua các giao thức TCP chuyên biệt.

Không có những thứ thừa thãi của HTTP:

✅ Không cookies ✅ Không headers ✅ Không cần JSON parsing

Chỉ là dữ liệu thô, nhanh chóng, giao thức tùy chỉnh, giúp tối ưu hóa hiệu suất.

Xây dựng một giao thức tùy chỉnh

Một giao thức đơn giản chỉ là một thỏa thuận giữa máy khách (client) và máy chủ (server): "Khi bạn gửi dữ liệu cho tôi, tôi mong đợi nó theo định dạng này, nếu không tôi sẽ từ chối."

Dưới đây là một HTTP request cơ bản:

HTTP/1.1 200 OK
Content-Length: 12
Hello world!

Hãy xây dựng giao thức nhị phân đơn giản của riêng mình dựa trên Raw TCP.

Xác định giao thức

Chúng ta sẽ cấu trúc bộ đệm của mình như thế này:

buffer = msg length (4 bytes) | msg (variable length) | metadata (variable length)

Trong đó:

  • 4 byte đầu tiên → Độ dài của tin nhắn
  • Các byte tiếp theo → Nội dung tin nhắn
  • Các byte còn lại → Metadata (dữ liệu bổ sung)

Dưới đây là cách chúng ta có thể mã hóa dữ liệu trước khi gửi đi:

const server = net.createServer((c) => {
    c.on("data", (data) => console.log(data.toString()))
    const data = Buffer.from("hello world") // Encode message
    const metadata = Buffer.from([0x00]) // No metadata
    const len = Buffer.alloc(4) // Allocate 4 bytes for length
    len.writeInt32BE(data.length, 0) // Store message length
    const combinedBuffer = Buffer.concat([len, data, metadata])
    c.write(combinedBuffer) // Send to client
    c.end()
})

Bây giờ, ở phía máy khách , chúng ta sẽ giải mã thông điệp có cấu trúc này:

const c = net.createConnection({ port: 3000, host: "localhost" })
c.on("data", (data) => {
    console.log(data)
    const len = data.readInt32BE(0)  // Read message length
    console.log(len) 
    const msg = data.subarray(4, 4 + len)  // Extract message
    console.log(msg.toString())
     const metadata = data.subarray(4 + len) // Extract metadata
    console.log(metadata)
})

c.write("hello")

Và cứ như thế, chúng ta đã xây dựng được một hệ thống nhắn tin TCP có cấu trúc.


All rights reserved

Bình luận

Đăng nhập để bình luận
Avatar
+1
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í