iT邦幫忙

第 11 屆 iThome 鐵人賽

DAY 3
1
Software Development

從讀遊戲原始碼學做連線遊戲系列 第 3

Day 03 - Client 與 Server 的溝通 - 同時處理多個連線

在上一篇的範例中,我們會發現無法「多人連線」,這是因為 Ruby 的程序會因為有一個連線進入後就呈現了阻塞(Blocking)的狀態,為了處理這樣的問題在大多數的入門書中都會用 Thread(執行緒)來做示範,透過作業系統的機制將發生阻塞的處理延後,利用快速交替切換處理的方式來達到類似同時處理的效果。

我們先稍微改寫一下上一篇的 EchoServer 讓他能支援多人連線

# frozen_string_literal: true

require 'socket'

server = TCPServer.new 12_000

loop do
  client = server.accept # 等待連線是阻塞的狀態
  Thread.new do # 產生 Thread
    ping = client.gets
    puts "< #{ping}"
    client.puts ping
    puts "> #{ping}"
    client.close
  end
end

實際上我們只是在 server.accept 後面插入了 Thread.new 並且將原本處理連線的機制放到 Thread 裡面。在 Ruby 中要產生新的 Thread 是非常簡單的,不過實際上在 Ruby 裡面的 Thread 並不像 C 語言或者其他語言那麼的有效率(能夠真正的同時處理),這是為了避免兩個 Thread 同時對一個變數修改時,無法判定先後順序。也因為這樣,實際上 Ruby 的 Thread 效率上並沒有我們想像中的那麼好,而且為了紀錄這些資訊還會額外花費不少記憶體在上面。

接下來我們開始進入 Unlight 原始碼的閱讀,一般來說要閱讀一個陌生的專案,我們通常會先去看看套件管理(Package Manager)的設定檔有使用哪些套件,以 Ruby 來說就是使用 Rubygems 以及 Bundler 來管理套件。

在 CPA 公開的 Unlight 原始碼中,我們可以在 app/server 這個目錄下找到 Gemfile 這個檔案,如此一來我們就可以參考 Unlight 有使用過的套件來預判一些東西。

在 Unlight 的專案中 Gemfile 少了成對的 Gemfile.lock 是不太恰當的使用方式,大多數的專案都會將 .lock 檔案一起保存,這樣才能讓其他建構專案的人能夠使用相同版本的套件。不過運氣不錯的是 Unlight 使用的套件即使在較新的版本中也沒有過大的變動,讓遊戲能夠順利的運行起來。

在專案中跟處理網路連線最相關的套件是 EventMachine,所以我們要先來看一下 EventMachine 是什麼。

如果你有聽過 Node.js 的話,EventMachine 的運作原理跟 Node.js 是類似的。他們都使用了非同步(Async IO)的方式,以事件驅動的方式來處理程式的運行,因為不會等待阻塞的 IO 處理完再做下一個任務,自然就不會有前面提到阻塞的問題。

我們先以 Unlight 定期檢查伺服器線上玩家的程式碼為例子來看,在 EventMachine 中提供了不少跟 JavaScript 類似的 API 可以使用。

# 1分に一回でソケットの生き死にをチェック
EM::PeriodicTimer.new(60, proc {
  begin
    AuthServer.check_connection
  rescue =>e
    SERVER_LOG.fatal("AuthServer: [check_connection:] fatal error #{e}:#{e.backtrace}")
  end
})

是不是跟 JavaScript 的 setInterval() 的使用方式很像,都是給定一個時間和 Callback (回呼)函式,就可以達到定時執行某個動作的效果。

setInterval(function() { console.log('Check Connection') }, 60 * 1000)

在 Ruby 裡面想要製作出可以被呼叫的物件可以利用 proc, lambda 或者 Block,在語言層面上(Ruby 2.6.2 為例)即使是方法(Method)也都是基於 Proc 的物件,使用起來就有點類似 JavaScript 的匿名函式,特性是會有 #call 這個方法。

會使用 EventMachine 是因為 EventMachine 提供了各種不同類型 API 供我們使用,其中 EventMachine::Connection 就提供了建立 TCP 連線以及處理的 API,有不少早期的 Ruby 專案都會使用 EventMachine 來做非同步行為的處理,不過因為後來 EventMachine 有一些其他的問題,近年大多使用其他專案(如 nio4r 這類套件)作為替代。

我的個人部落格是弦而時習之平常會把自己發現的一些新技巧紀錄在上面,也歡迎大家來逛逛。


上一篇
Day 02 - Client 與 Server 的溝通 - 暖身一下
下一篇
Day04 - Client 與 Server 的溝通 - 使用 EventMachine 管理連線
系列文
從讀遊戲原始碼學做連線遊戲33

尚未有邦友留言

立即登入留言