Ruby - Socket 套接字编程

Ruby 提供了对网络服务的两个级别的访问。 在底层,您可以访问底层操作系统中的基本套接字支持,这允许您为面向连接和无连接协议实现客户端和服务器。

Ruby 还有一些库,这些库提供对特定应用程序级网络协议(如 FTP、HTTP 等)的更高级别访问。

本章让您了解网络中最著名的概念 − 套接字编程。


什么是套接字?

套接字是双向通信通道的端点。 套接字可以在进程内、同一台机器上的进程之间或不同大陆上的进程之间进行通信。

套接字可以通过多种不同的通道类型实现:Unix 域套接字、TCP、UDP 等。 socket 提供了用于处理公共传输的特定类以及用于处理其余部分的通用接口。

套接字有自己的词汇表 −

序号 Term & 描述
1

domain

将用作传输机制的协议族。 这些值是常量,例如 PF_INET、PF_UNIX、PF_X25 等。

2

type

两个端点之间的通信类型,通常 SOCK_STREAM 用于面向连接的协议,SOCK_DGRAM 用于无连接协议。

3

protocol

通常为零,这可用于识别域和类型内的协议变体。

4

hostname

网络接口的标识符 −

一个字符串,可以是主机名、点分四组地址或冒号(也可能是点)表示法的 IPV6 地址

一个字符串"<broadcast>",它指定一个 INADDR_BROADCAST 地址。

一个零长度字符串,指定 INADDR_ANY,或

一个整数,解释为主机字节顺序的二进制地址。

5

port

每台服务器都侦听调用一个或多个端口的客户端。 端口可以是 Fixnum 端口号、包含端口号的字符串或服务名称。


一个简单的客户端

在这里,我们将编写一个非常简单的客户端程序,它将打开到给定端口和给定主机的连接。 Ruby 类 TCPSocket 提供了 open 函数来打开这样的套接字。

TCPSocket.open(hosname, port )port 上打开到 hostname 的 TCP 连接。

一旦你打开了一个套接字,你就可以像任何 IO 对象一样读取它。 完成后,记得关闭它,就像关闭文件一样。

以下代码是一个非常简单的客户端,它连接到给定的主机和端口,从套接字读取任何可用数据,然后退出 −

require 'socket'        # Sockets are in standard library

hostname = 'localhost'
port = 2000

s = TCPSocket.open(hostname, port)

while line = s.gets     # Read lines from the socket
   puts line.chop       # And print with platform line terminator
end
s.close                 # Close the socket when done

一个简单的服务器

要编写 Internet 服务器,我们使用 TCPServer 类。 TCPServer 对象是 TCPSocket 对象的工厂。

现在调用 TCPServer.open(hostname, port 函数为您的服务指定一个 port 并创建一个 TCPServer 对象。

接下来,调用返回的 TCPServer 对象的 accept 方法。 此方法一直等到客户端连接到您指定的端口,然后返回一个表示与该客户端的连接的 TCPSocket 对象。

require 'socket'                 # Get sockets from stdlib

server = TCPServer.open(2000)    # Socket to listen on port 2000
loop {                           # Servers run forever
   client = server.accept        # Wait for a client to connect
   client.puts(Time.now.ctime)   # Send the time to the client
   client.puts "Closing the connection. Bye!"
   client.close                  # Disconnect from the client
}

现在,在后台运行这个服务器,然后运行上面的客户端来查看结果。


多客户端 TCP 服务器

Internet 上的大多数服务器都旨在随时处理大量客户端。

Ruby 的 Thread 类可以很容易地创建一个多线程的 server.one,它接受请求并立即创建一个新的执行线程来处理连接,同时允许主程序等待更多的连接 −

require 'socket'                 # Get sockets from stdlib

server = TCPServer.open(2000)    # Socket to listen on port 2000
loop {                           # Servers run forever
   Thread.start(server.accept) do |client|
   client.puts(Time.now.ctime)   # Send the time to the client
   client.puts "Closing the connection. Bye!"
   client.close                  # Disconnect from the client
   end
}

在此示例中,您有一个永久循环,当 server.accept 响应时,将创建一个新线程并立即启动以使用传递给线程的连接对象来处理刚刚接受的连接。 但是,主程序会立即循环返回并等待新的连接。

以这种方式使用 Ruby 线程意味着代码是可移植的,并且可以在 Linux、OS X 和 Windows 上以相同的方式运行。


一个小小的网络浏览器

我们可以使用套接字库来实现任何 Internet 协议。 例如,这里是获取网页内容的代码 −

require 'socket'
 
host = 'www.tutorialspoint.com'     # The web server
port = 80                           # Default HTTP port
path = "/index.html"                 # The file we want 

# This is the HTTP request we send to fetch a file
request = "GET #{path} HTTP/1.0\r\n\r\n"

socket = TCPSocket.open(host,port)  # Connect to server
socket.print(request)               # Send request
response = socket.read              # Read complete response
# Split response at first blank line into headers and body
headers,body = response.split("\r\n\r\n", 2) 
print body                          # And display it

要实现类似的 Web 客户端,您可以使用像 Net::HTTP 这样的预构建库来处理 HTTP。 这是与前面代码等效的代码 −

require 'net/http'                  # The library we need
host = 'www.tutorialspoint.com'     # The web server
path = '/index.htm'                 # The file we want 

http = Net::HTTP.new(host)          # Create a connection
headers, body = http.get(path)      # Request the file
if headers.code == "200"            # Check the status code   
   print body                        
else                                
   puts "#{headers.code} #{headers.message}" 
end

请检查类似的库以使用 FTP、SMTP、POP 和 IMAP 协议。


进一步阅读

我们为您提供了 Socket 编程的快速入门。 是个大课题,所以建议大家通读Ruby 套接字库和类方法 以查找更多详细信息。