使用 Ruby 進行 Socket 編程
網絡是千萬臺計算機通過 TCP/IP 通信的結果??蛻舳苏埱髨绦心承┎僮?,服務端執行該操作并響應客戶端,這種被我們成為請求-響應模型。宏觀上來看,就是當用戶通過瀏覽器瀏覽網站時,請求發送到適當的 web 服務器,服務器通過發送適當的HTML頁面來響應客戶端。本章節主要帶大家了解一下 Ruby中如何進行 Socket 應用程序開發。
1. 端口
在學習Socket編程之前我們要了解一下概念。
端口不是物理設備,而是促進服務器和客戶端之間通信的抽象概念。
端口是由一個 2 的 16 次冪的整數表示的,所以,一臺機器最多可以有65536個端口(0~65535)。
端口一共分為三個種類:
- 知名端口:0 ~ 1023(例如:80端口用于http,25端口用于smtp)。
- 注冊端口:1024 ~ 49151。
- 動態/私有端口:49152 ~ 65535。
2. IP地址
這里我們只說 IPV4,它是由 4 個 4 個字節數字組成的主機地址,比如:124.56.124.103。
127.0.0.1(localhost)代表了本地回送地址,它是一個特殊的地址,代表了本地計算機。
3. Socket
3.1 什么是 Socket
Socket 表示兩個網絡應用程序之間的單個連接。這兩個應用程序名義上可以在不同的計算機上運行,并且Socket也可以在單臺計算機上實現進程之間的通信。應用程序可以創建多個用于相互通信的Socket。Socket 是雙向的,代表連接它的任何一方都可以發送和接收數據。
3.2 Ruby中Socket類
Ruby 中擁有非常豐富的 Socket 的類型。
上面是一張出自《Programming Ruby》的圖片,介紹了 Rub y中 Socket 的類型。
如上圖所示,所有的類都繼承了 IO 類。
下面的例子中我們會使用TCPSocket
來進行 Socket 的連接,使用TCPServer
來創建一個TCP的服務器。
Tips:所有的Socket類都是標準庫的一部分(不是核心類的一部分),因此要使用
require 'socket'
才可以在程序中使用它。
下面的是服務端的代碼:
實例:
require 'socket'
server = TCPServer.new('127.0.0.1', 18000)
loop do
Thread.start(server.accept) do |s|
puts "#{s} is accepted."
s.write(Time.now)
puts "#{s} is gone."
s.close
end
end
解釋:
我們使用TCPServer
在本地創建了一個TCP的Socket服務器server
,Thread.start
創建了一個新的線程(線程詳細會在多線程章節中解釋)并執行塊中的代碼指令。server.accept
方法等待server
上的鏈接,并返回鏈接到調用者的新TCPSocket
對象s。loop do
會一直迭代,直到退出循環。s.write
會將時間寫入到Socket
之中。s.close
會關閉socket。
這個腳本需要先啟動,剛啟動時你不會看到任何輸出內容。
下面是客戶端的代碼:
實例:
require 'socket'
stream = TCPSocket.new('127.0.0.1', 18000)
string = stream.recv( 100 )
puts string
stream.close
解釋:
我們使用TCPSocket.new('127.0.0.1', 18000)
打開了TCP連接。語句string = stream.recv( 100 )
定義我們從socket最多接收100個字節。然后打印從服務器端獲取到的時間,最后關閉socket。
服務端輸出內容:
#<TCPSocket:0x00007fb54497d898> is accepted.
#<TCPSocket:0x00007fb54497d898> is gone.
客戶端輸出內容:
2020-08-24 00:23:14 +0800
這是一個服務端發送,客戶端接收的例子。反過來依舊可以。
下面是服務端的代碼:
require 'socket'
server = TCPServer.new('127.0.0.1', 18000)
loop do
Thread.start(server.accept) do |s|
puts "#{s} is accepted."
string = s.recv( 100 )
puts string
puts "#{s} is gone."
s.close
end
end
下面是客戶端的代碼:
require 'socket'
stream = TCPSocket.new('127.0.0.1', 18000)
stream.write(Time.now)
服務端輸出結果:
#<TCPSocket:0x00007fd6d714d1f0> is accepted.
2020-08-24 00:29:15 +0800
#<TCPSocket:0x00007fd6d714d1f0> is gone.
客戶端輸出結果:
# 無內容
4. 小結
本節我們討論了 Socket 編程的基本類(例如Socket類),初步了解了 IP 以及端口號,以及有助于簡化 Ruby 中 Socket 編程的類,例如TCPSocket
和TCPServer
,學習了使用 Socket 進行交互的例子。