Có một mô hình để viết một máy chủ theo lượt giao tiếp với n khách hàng qua ổ cắm không?


14

Tôi đang làm việc trên một máy chủ trò chơi chung quản lý các trò chơi cho một số lượng khách hàng nối mạng TCP tùy ý chơi trò chơi. Tôi có một 'thiết kế' được hack cùng với băng keo đang hoạt động, nhưng dường như vừa mỏng manh vừa không linh hoạt. Có một mô hình được thiết lập tốt cho cách viết giao tiếp máy khách / máy chủ mạnh mẽ và linh hoạt không? (Nếu không, bạn sẽ cải thiện những gì tôi có dưới đây như thế nào?)

Tôi thực sự có điều này:

  • Trong khi thiết lập trò chơi, máy chủ có một luồng cho mỗi ổ cắm người chơi xử lý các yêu cầu đồng bộ từ máy khách và phản hồi từ máy chủ.
  • Tuy nhiên, khi trò chơi đang diễn ra, tất cả các luồng ngoại trừ một lần ngủ và luồng đó sẽ quay vòng qua tất cả người chơi cùng một lúc để thông báo về di chuyển của họ (trong phản hồi yêu cầu đảo ngược).

Đây là sơ đồ về những gì tôi có hiện tại; nhấp để xem phiên bản lớn hơn / dễ đọc hơn hoặc PDF 66kB .

Sơ đồ trình tự dòng chảy

Các vấn đề:

  • Nó yêu cầu người chơi trả lời chính xác lần lượt với thông điệp chính xác. (Tôi cho rằng tôi có thể cho phép mỗi người chơi trả lời bằng những câu chuyện tào lao ngẫu nhiên và chỉ tiếp tục một khi họ cho tôi một nước đi hợp lệ.)
  • Nó không cho phép người chơi nói chuyện với máy chủ trừ khi đến lượt của họ. (Tôi có thể yêu cầu máy chủ gửi cho họ cập nhật về những người chơi khác, nhưng không xử lý yêu cầu không đồng bộ.)

Yêu cầu cuối cùng:

  • Hiệu suất không phải là tối quan trọng. Điều này chủ yếu sẽ được sử dụng cho các trò chơi không phải thời gian thực, và chủ yếu là để đưa AI vào nhau, chứ không phải con người co giật.

  • Chơi trò chơi sẽ luôn luôn theo lượt (ngay cả khi ở độ phân giải rất cao). Mỗi người chơi luôn được xử lý một động tác trước khi tất cả những người chơi khác có lượt.

Việc triển khai máy chủ xảy ra trong Ruby, nếu điều đó tạo ra sự khác biệt.


Nếu bạn sử dụng ổ cắm TCP trên mỗi máy khách, thì có phải nắp này cho (65535-1024) máy khách không?
o0 '.

1
Tại sao đó là một vấn đề, Lohoris? Hầu hết mọi người sẽ đấu tranh để có được 6000 người dùng đồng thời, đừng
bận

@Kylotan Thật vậy. Nếu tôi có hơn 10 AI đồng thời tôi sẽ ngạc nhiên. :)
Phrogz

Vì tò mò, bạn đã sử dụng công cụ nào để tạo sơ đồ đó?
matthias

@matthias Đáng buồn thay, đó là quá nhiều công việc tùy chỉnh trong Adobe Illustrator.
Phrogz

Câu trả lời:


10

Tôi không chắc chính xác những gì bạn muốn đạt được. Nhưng, có một mẫu được sử dụng liên tục trong các máy chủ trò chơi và có thể giúp bạn. Sử dụng hàng đợi tin nhắn.

Để cụ thể hơn: khi khách hàng gửi tin nhắn đến máy chủ, đừng xử lý chúng ngay lập tức. Thay vào đó, phân tích chúng và đưa vào hàng đợi cho khách hàng cụ thể này. Sau đó, trong một số vòng lặp chính (thậm chí có thể trên một luồng khác) lần lượt đi qua tất cả các máy khách, lấy thông điệp từ hàng đợi và xử lý chúng. Khi quá trình xử lý chỉ ra rằng lượt của khách hàng này đã kết thúc, chuyển sang bước tiếp theo.

Bằng cách này, khách hàng không bắt buộc phải làm việc theo từng bước một; chỉ đủ nhanh để bạn có thứ gì đó trong hàng đợi khi khách hàng được xử lý (tất nhiên, bạn có thể đợi khách hàng hoặc bỏ qua lượt của mình nếu nó bị chậm). Và bạn có thể thêm hỗ trợ cho các yêu cầu không đồng bộ bằng cách thêm hàng đợi "không đồng bộ": khi khách hàng gửi yêu cầu đặc biệt, nó sẽ được thêm vào hàng đợi đặc biệt này; hàng đợi này được kiểm tra và xử lý thường xuyên hơn sau đó là của khách hàng.


1

Các chủ đề phần cứng không đủ quy mô để biến "mỗi người chơi" thành một ý tưởng hợp lý cho số lượng người chơi gồm 3 chữ số và logic để biết khi nào đánh thức họ là một sự phức tạp sẽ tăng lên. Một ý tưởng tốt hơn là tìm gói I / O không đồng bộ cho Ruby, cho phép bạn gửi và nhận dữ liệu mà không phải tạm dừng toàn bộ chuỗi chương trình trong khi hoạt động đọc hoặc ghi diễn ra. Điều này cũng giải quyết vấn đề chờ người chơi phản hồi, vì bạn sẽ không có bất kỳ chủ đề nào bị treo trên thao tác đọc. Thay vào đó, máy chủ của bạn chỉ có thể kiểm tra xem thời hạn đã hết hạn hay không và sau đó thông báo cho người chơi khác.

Về cơ bản, 'I / O không đồng bộ' là 'mẫu' bạn đang tìm kiếm, mặc dù nó không thực sự là một mẫu, hơn nữa là một cách tiếp cận. Thay vì gọi một cách rõ ràng 'đọc' trên một ổ cắm và tạm dừng chương trình cho đến khi dữ liệu đến, bạn thiết lập hệ thống để gọi trình xử lý 'onRead' của bạn bất cứ khi nào nó có sẵn dữ liệu và bạn tiếp tục xử lý cho đến lúc đó.

mỗi ổ cắm có một lượt

Mỗi người chơi có một lượt và mỗi người chơi có một ổ cắm gửi dữ liệu, một chút khác nhau. Một ngày bạn có thể không muốn một ổ cắm cho mỗi người chơi. Bạn có thể không sử dụng ổ cắm nào cả. Giữ các khu vực trách nhiệm riêng biệt. Xin lỗi nếu điều này nghe có vẻ như là một chi tiết không quan trọng nhưng khi bạn đưa ra các khái niệm trong thiết kế của mình nên khác đi thì sẽ khó tìm và thảo luận các cách tiếp cận tốt hơn.


1

Chắc chắn có nhiều cách để làm điều đó, nhưng cá nhân tôi, tôi hoàn toàn bỏ qua các luồng riêng biệt và chỉ sử dụng một vòng lặp sự kiện. Cách bạn làm điều này sẽ phụ thuộc phần nào vào thư viện I / O bạn đang sử dụng, nhưng về cơ bản, vòng lặp máy chủ chính của bạn sẽ trông như thế này:

  1. Thiết lập nhóm kết nối và ổ cắm nghe cho các kết nối mới.
  2. Chờ đợi một cái gì đó xảy ra.
  3. Nếu một cái gì đó là một kết nối mới, thêm nó vào nhóm.
  4. Nếu thứ gì đó là yêu cầu từ khách hàng, hãy kiểm tra xem đó có phải là thứ bạn có thể xử lý ngay lập tức không. Nếu có, làm như vậy; nếu không, hãy đặt nó trong hàng đợi và (tùy chọn) gửi xác nhận đến máy khách.
  5. Đồng thời kiểm tra xem có thứ gì trong hàng đợi mà bạn có thể xử lý bây giờ không; nếu vậy, hãy làm nó
  6. Quay trở lại bước 2.

Ví dụ: giả sử bạn có n khách hàng tham gia vào một trò chơi. Khi đầu tiên , 1 trong số họ gửi di chuyển, bạn chỉ cần kiểm tra xem di chuyển đó có hợp lệ không và gửi lại một thông báo nói rằng di chuyển đã được nhận nhưng bạn vẫn đang chờ người chơi khác di chuyển. Sau khi tất cả n người chơi đã di chuyển, bạn xử lý tất cả các di chuyển bạn đã lưu và gửi kết quả cho tất cả người chơi.

Bạn cũng có thể tinh chỉnh lược đồ này để bao gồm thời gian chờ - hầu hết các thư viện I / O nên có một số cơ chế chờ đợi cho đến khi dữ liệu mới đến hoặc một khoảng thời gian nhất định đã trôi qua.

Tất nhiên, bạn cũng có thể triển khai một cái gì đó như thế này với các luồng riêng lẻ cho mỗi kết nối, bằng cách yêu cầu các luồng đó chuyển đến bất kỳ yêu cầu nào mà chúng không thể xử lý trực tiếp đến một luồng trung tâm (mỗi một trò chơi hoặc một trên mỗi máy chủ) chạy một vòng lặp như hiển thị ở trên, ngoại trừ việc nó nói chuyện với các luồng xử lý kết nối thay vì trực tiếp với các máy khách. Cho dù bạn thấy điều này đơn giản hay phức tạp hơn phương pháp đơn luồng là tùy thuộc vào bạn.

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.