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