Đóng gói dữ liệu hiệu quả cho mạng máy khách-máy chủ


8

Ngôn ngữ: C ++

Câu hỏi của tôi là như sau: Tôi muốn biết điều gì sẽ là tốt nhất hoặc ít nhất là một cách tốt để đóng gói và gửi dữ liệu từ máy khách đến máy chủ và ngược lại. Sẽ có một vài dữ liệu tạo thành một gói duy nhất. Một gói sẽ có "id", xác định nó dùng để làm gì, sau đó dữ liệu theo thứ tự được xác định trước cho "hành động" mà gói tương ứng.

Đối với các hệ thống phụ thuộc ít hiệu năng hơn, tôi sẽ chỉ gửi các chuỗi, được phân tách bằng khoảng trắng, chúng là dữ liệu của "hành động" và "từ" đầu tiên của mã định danh gói và chỉ chuỗi nếu các câu lệnh kiểm tra khi có khớp .

Bây giờ đối với một hệ thống quan trọng hơn, những gì tôi nghĩ cho đến nay là một thứ như thế này:

Tạo một chuỗi với id gói và dữ liệu, và gửi nó. Sau đó, để giải nén, tôi có thể trích xuất số nguyên đầu tiên trong chuỗi và bằng cách có một mảng các trình xử lý gói, với các chỉ số tương ứng với id gói mà chúng xử lý và chỉ cần thực hiện một số thứ như packHandlers [packID] .Process (packData).

Bạn nghĩ gì về nó, bất kỳ đề nghị? rất đáng kinh ngạc!

Câu trả lời:


10

Đầu tiên, xác nhận bạn thực sự cần một giao thức hiệu quả, lạ mắt trước khi lãng phí tài nguyên để phát triển nó. Đừng quên gỡ lỗi / sửa đổi trò chơi của bạn sẽ khó khăn và tốn thời gian hơn do giao thức ưa thích của bạn. Tôi sẽ chỉ trừu tượng truyền thông mạng đi để việc triển khai thực tế có thể dễ dàng hoán đổi cho một mạng hiệu quả hơn nếu cần trong tương lai. Sử dụng giao thức đơn giản nhất có thể cho đến khi bạn gặp vấn đề về hiệu suất. Một ưu điểm khác của việc thiết kế giao thức của bạn sau này là giao thức có thể được tối ưu hóa cho dữ liệu thực tế được vận chuyển so với những gì bạn dự đoán sẽ được vận chuyển.

Sau khi xác nhận bạn cần một giao thức ưa thích, hãy xem các giao thức mà người khác đã dành thời gian đáng kể để phát triển. Vài ví dụ:

  • (cập nhật) Nhà phát triển ban đầu của Bộ đệm giao thức (v2) đã phát triển một giao thức mới có tên Cap'n Proto. Ông giải thích các quyết định thiết kế của mình và so sánh với các thư viện tương tự khác đã được phát hành gần đây: Cap'n Proto, FlatBuffers và SBE .
  • Nút thắt chính của Google là giao tiếp mạng giữa các máy tính, vì vậy họ có thể đã cân nhắc hiệu quả khi phát triển Bộ đệm giao thức . Duyên dáng xử lý khả năng tương thích tiến / lùi ( khi bạn quyết định thay đổi cấu trúc dữ liệu của mình). Được sử dụng bởi mọi sản phẩm chính của Google (Gmail, Tìm kiếm, v.v.)
  • Apache Thrift là một giao thức tương tự được sử dụng bởi Facebook.
  • RakNet là một thư viện mạng nguồn mở được thiết kế đặc biệt để phát triển trò chơi.
  • ZoidCom là một thư viện mạng khác hướng đến phát triển trò chơi. Nó không phải là nguồn mở, nhưng bạn vẫn có thể nghiên cứu nó cho các gợi ý thiết kế.

Nguyên tắc tối ưu hóa chương trình đầu tiên: Đừng làm điều đó.
Quy tắc tối ưu hóa chương trình thứ hai (chỉ dành cho chuyên gia!): Đừng làm điều đó.

[ Michael A. Jackson ]

Nói cách khác: tham số tối ưu hóa chính của bạn phải là: tuổi thọ (số năm sống trên mỗi lần thực hiện chương trình). [ Cách lập trình trò chơi độc lập. Slide 21. Jonathan Thổi. ]


1
người đàn ông đọc tốt cảm ơn rất nhiều vì câu trả lời hoàn chỉnh, nó chắc chắn đã giúp ích và tôi hoàn toàn làm theo lời khuyên của bạn, chỉ trừu tượng hóa nó, làm cho nó đủ đơn giản cho đến bây giờ :)
Grimshaw

Khá chắc chắn rằng trường hợp sử dụng của Google cho bộ đệm giao thức không phải là hiệu quả theo nghĩa tiêu chuẩn, nhưng để tối đa hóa khả năng tương thích giữa tất cả các nền tảng có thể và bất kỳ phiên bản dữ liệu nào trong tương lai (đó là hiệu quả trong một ánh sáng khác). Tôi sẽ ở đây để đọc các ghi chú khác của bạn, bộ sưu tập rất hay để tôi làm quen với chủ đề này.
Patrick Hughes

RakNet không phải là nguồn mở.
Gerstmann

@Gerstmann: Raknet là mã nguồn mở (một lần nữa): github.com/OculusVR/RakNet
Leftium

0

Tại sao sử dụng hai sơ đồ mã hóa khác nhau? Chỉ cần sử dụng cái thứ hai cho mọi hệ thống. Chỉ cần giữ cho nó đơn giản.
Xem xét sử dụng nén delta. Tức là gửi một giá trị đầy đủ và sau đó chỉ những điều đã thay đổi. Sau một vài lần lặp lại trò chơi gửi lại một giá trị đầy đủ.
Một mã hóa khác bạn có thể xem xét là Base 128 Varint. Google Protobufs sử dụng nó. Hãy xem trang "Mã hóa" trong hướng dẫn dành cho nhà phát triển của họ: Mã hóa bộ đệm giao thức có thể tiết kiệm một vài byte.


Hệ thống đầu tiên tôi nói đến là một ví dụ đơn thuần từ các dự án khác :) Tôi chắc chắn rất thích ý tưởng nén delta, cảm ơn người bạn đời!
Grimshaw

0

Điều gì có thể là một ví dụ về dữ liệu bạn đang gửi? Tôi không thấy bất kỳ lý do để làm bất cứ điều gì quá mức ưa thích. Khi dữ liệu được tải đầy đủ vào bộ đệm của người nhận, hãy kiểm tra dữ liệu đầu tiên intdựa trên giá trị của nó, sau đó bạn biết cách xử lý phần còn lại của dữ liệu.

Vì vậy, một gói tin rằng có bốn phần dữ liệu id, val1, val2, và val2có thể trông như thế này:

// I'm using words (one byte) so my sample data is short
00000001 00101000 00010110 00010100 

Khi bạn đọc byte đầu tiên (mà bạn biết sẽ luôn ở đó), bạn quyết định cách xử lý bộ dữ liệu tiếp theo. Nếu 00000001bạn biết từ đầu tiên (id) thì có thêm ba từ theo sau và đó là phần cuối của gói. Tiếp tục ví dụ này, bạn có thể có id = 00000010và đặc điểm kỹ thuật của bạn nói với bạn rằng cho id giá trị 2, bạn xử lý float, float, floattheo thứ tự đó, mà có thể chỉ định một vị trí máy nghe nhạc trong không gian thế giới.

Hãy nghĩ về nó như viết hệ thống tệp nhị phân của riêng bạn, bạn có một giá trị tiêu đề, mô tả phần còn lại của dữ liệu, vị trí của nó (vị trí nào) và loại dữ liệu để xử lý nó.


Có nó, câu hỏi duy nhất tôi còn lại là: đóng gói tất cả các số nguyên và số float trong một chuỗi tiêu chuẩn đủ hay tôi nên đi với các loại dữ liệu nhẹ hơn?
Grimshaw

Như đã nói, hãy bắt đầu với chuỗi và xem điều gì sẽ xảy ra. Rất có thể, bạn sẽ có một cổ chai ở nơi khác trong mã của bạn.
Nate
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.