Программирование сокетов на Ruby

Ruby предоставляет два уровня доступа к сетевым службам. На низком уровне вы можете обращаться к операционной системе, что позволяет реализовать базовую поддержку сокетов для клиентов и серверов для протоколов с установлением соединения и без установления соединения.

Ruby обеспечивает унифицированную поддержку прикладных сетевых протоколов, таких как FTP, HTTP и т. д.

Будь то высокоуровневый или низкоуровневый, Ruby предоставляет некоторые базовые классы, которые позволяют взаимодействовать со многими протоколами, такими как TCP, UDP, SOCKS, не будучи привязанными к сетевому уровню. Эти классы также предоставляют вспомогательные классы, которые позволяют легко читать с серверов и записывать на серверы.

Далее давайте изучим, как выполнять программирование сокетов на Ruby.


Что такое сокеты

Когда прикладной уровень взаимодействует с транспортным уровнем для передачи данных, TCP и UDP сталкиваются с проблемой одновременного предоставления сервисов нескольким прикладным процессам. Несколько TCP-соединений или несколько прикладных процессов могут нуждаться в передаче данных через один и тот же порт протокола TCP. Чтобы различать разные прикладные процессы и соединения, многие компьютерные операционные системы предоставляют интерфейс, называемый сокетом, для взаимодействия приложений с протоколами TCP/IP, различая сетевую коммуникацию и соединения между разными прикладными процессами.

Создание сокета в основном включает три параметра: IP-адрес назначения для связи, используемый протокол транспортного уровня (TCP или UDP) и используемый номер порта. Изначальное значение Socket — «сокет». Комбинируя эти три параметра и привязывая их к «сокету», прикладной уровень может использовать интерфейс сокета с транспортным уровнем, чтобы различать связь от разных прикладных процессов или сетевых соединений, обеспечивая сервисы параллельной передачи данных.

Термины сокетов:

ОпцияОписание
domainУказывает используемое семейство протоколов, обычно PF_INET, PF_UNIX, PF_X25 и т. д.
typeУказывает тип сокета: SOCK_STREAM или SOCK_DGRAM. Интерфейс Socket также определяет сырые сокеты (SOCK_RAW), позволяя программам использовать низкоуровневые протоколы.
protocolОбычно присваивается 0.
hostnameИдентификатор сетевого интерфейса:
  • Строка, может быть именем хоста или IP-адресом
  • Строка "<broadcast>", указывает адрес INADDR_BROADCAST.
  • Строка нулевой длины, задаёт INADDR_ANY
  • Целое число, интерпретируемое как двоичный адрес в порядке байтов хоста.
portport — это номер порта. Каждый сервер прослушивает один или несколько номеров портов для клиентских подключений. Номер порта может быть номером порта Fixnum или строкой, содержащей имя сервера и порт.

Простой клиент

Ниже мы написали пример простого клиента, использующего заданные хост и порт. Класс Ruby TCPSocket предоставляет метод open для открытия сокета.

TCPSocket.open(hosname, port) открывает TCP-соединение.

После открытия соединения Socket вы можете читать из него как из объекта IO. По завершении нужно закрыть соединение, как при закрытии файла.

Следующий пример демонстрирует, как подключиться к указанному хосту, прочитать данные из сокета и в конце закрыть сокет:

Пример

require 'socket' #Sockets — это стандартная библиотека hostname = 'localhost' port = 2000 s = TCPSocket.open(hostname, port) while line = s.gets #Считывает каждую строку данных из сокета puts line.chop #Вывести в терминал end s.close #Закрыть сокет

Простой сервер

В Ruby можно использовать класс TCPServer для написания простого сервиса. Объект TCPServer — это фабричный объект для TCPSocket.

Теперь мы используем TCPServer.open(hostname, port) для создания объекта TCPServer.

Затем вызовите метод accept класса TCPServer. Этот метод ожидает, пока клиент подключится к указанному порту, а затем возвращает объект TCPSocket, представляющий соединение с этим клиентом.

Пример

require 'socket' #Подключить стандартную библиотеку socket server = TCPServer.open(2000) #Сокет прослушивает порт 2000 loop { #Запускать сервис бесконечно client = server.accept #Ожидать подключения клиента client.puts(Time.now.ctime) #Отправить время клиенту client.puts "Закрываем соединение. Пока!" client.close #Закрыть клиентское соединение }

Теперь запустите приведённый выше код на сервере и посмотрите результат.


Многоклиентская служба TCP

В Интернете большинство служб имеют большое количество клиентских соединений.

Класс Thread в Ruby позволяет легко создавать многопоточные службы. Один поток обрабатывает клиентские соединения, а главный поток ожидает новые соединения.

Пример

require 'socket' #Подключить стандартную библиотеку socket server = TCPServer.open(2000) #Сокет прослушивает порт 2000 loop { #Запускать службу бесконечно Thread.start(server.accept) do |client| client.puts(Time.now.ctime) #Отправить время клиенту client.puts "Закрываю соединение. Пока!" client.close #Закрыть клиентское соединение end }

В этом примере сокет работает бесконечно. Когда server.accept получает клиентское соединение, создаётся новый поток, который сразу начинает обработку запроса. Затем главная программа немедленно возвращается к циклу и ожидает новые соединения.


Крошечный веб-браузер

Мы можем использовать библиотеку socket для реализации любого интернет-протокола. Следующий код показывает, как получить содержимое веб-страницы:

Пример

require 'socket' host = 'www.w3cschool.cc' #Веб-сервер port = 80 #Порт HTTP по умолчанию path = "/index.htm" #Путь к файлу для получения #Это HTTP-запрос request = "GET #{path} HTTP/1.0\r\n\r\n" socket = TCPSocket.open(host,port) #Подключение к серверу socket.print(request) #Отправка запроса response = socket.read #Чтение полного ответа # Разделение ответа по первой пустой строке на заголовки и тело headers,body = response.split("\r\n\r\n", 2) print body #Вывод результата

Чтобы реализовать клиент, подобный веб-клиенту, можно использовать готовые библиотеки для HTTP, такие как Net::HTTP.

Следующий код эквивалентен предыдущему коду:

Пример

require 'net/http' #Библиотека, которая нам нужна host = 'www.w3cschool.cc' #Веб-сервер path = '/index.htm' #Файл, который нам нужен http = Net::HTTP.new(host) #Создать соединение headers, body = http.get(path) #Запросить файл if headers.code == "200" #Проверить код состояния print body else puts "#{headers.code} #{headers.message}" end

Выше мы лишь кратко рассмотрели применение сокетов в Ruby. Для получения дополнительной документации см.:Библиотека сокетов Ruby и методы класса

Другие расширения