Как открывать и читать из сокетов?ruby-78

Ruby предоставляет удобные инструменты для работы с сокетами через стандартную библиотеку socket. Рассмотрим основные подходы к работе с сокетами.

Основные типы сокетов

  1. TCP сокеты - надежные потоковые соединения
  2. UDP сокеты - ненадежные датаграммы
  3. UNIX сокеты - межпроцессное взаимодействие

Открытие TCP сокета

require 'socket'

# Создание TCP-соединения
socket = TCPSocket.new('example.com', 80)

# Запись в сокет
socket.puts "GET / HTTP/1.1\r\nHost: example.com\r\n\r\n"

# Чтение ответа
while line = socket.gets
  puts line
end

# Закрытие соединения
socket.close

Создание TCP сервера

require 'socket'

server = TCPServer.new(2000) # Порт 2000
puts "Сервер запущен на порту 2000"

loop do
  client = server.accept    # Ожидаем подключения
  request = client.read     # Читаем запрос

  # Формируем ответ
  response = "HTTP/1.1 200 OK\r\n" +
             "Content-Type: text/plain\r\n" +
             "\r\n" +
             "Hello, Client!\r\n"

  client.write(response)    # Отправляем ответ
  client.close             # Закрываем соединение
end

Неблокирующее чтение

require 'socket'
require 'io/nonblock'

socket = TCPSocket.new('example.com', 80)
socket.nonblock = true

begin
  data = socket.read_nonblock(1024) # Чтение без блокировки
  puts data
rescue IO::WaitReadable
  # Данные еще не готовы
  IO.select([socket])
  retry
rescue EOFError
  puts "Соединение закрыто"
ensure
  socket.close
end

UDP сокеты

require 'socket'

# Клиент
udp_socket = UDPSocket.new
udp_socket.bind('localhost', 1234)
udp_socket.send("Hello", 0, 'localhost', 5678)

# Сервер
server = UDPSocket.new
server.bind('localhost', 5678)
message, sender = server.recvfrom(1024)
puts "Получено: #{message} от #{sender[2]}"

UNIX сокеты

require 'socket'

# Сервер
server = UNIXServer.new('/tmp/socket.sock')
client = server.accept
client.puts "Hello from server"
client.close

# Клиент
socket = UNIXSocket.new('/tmp/socket.sock')
puts socket.read
socket.close
File.unlink('/tmp/socket.sock') # Удаляем файл сокета

Обработка нескольких соединений

require 'socket'

server = TCPServer.new(2000)
sockets = [server]

loop do
  ready = IO.select(sockets)
  readable = ready[0]

  readable.each do |socket|
    if socket == server
      # Новое подключение
      client = server.accept
      sockets << client
    else
      # Чтение данных
      data = socket.gets
      if data
        socket.puts "Echo: #{data}"
      else
        # Соединение закрыто
        sockets.delete(socket)
        socket.close
      end
    end
  end
end

Безопасность и обработка ошибок

  1. Таймауты:
socket = Socket.new(Socket::AF_INET, Socket::SOCK_STREAM, 0)
timeout = 5 # секунд

begin
  # Устанавливаем таймаут на подключение
  addr = Socket.pack_sockaddr_in(80, 'example.com')
  socket.connect_nonblock(addr)
rescue IO::WaitWritable
  if IO.select(nil, [socket], nil, timeout)
    retry
  else
    raise "Connection timeout"
  end
end
  1. SSL/TLS:
require 'socket'
require 'openssl'

tcp_socket = TCPSocket.new('example.com', 443)
ssl_socket = OpenSSL::SSL::SSLSocket.new(tcp_socket)
ssl_socket.connect
ssl_socket.puts "GET / HTTP/1.1\r\nHost: example.com\r\n\r\n"
puts ssl_socket.read

Резюмируем

Работа с сокетами в Ruby включает:

  1. Создание сокетов (TCP/UDP/UNIX)
  2. Установку соединения (для TCP)
  3. Чтение/запись данных
  4. Корректное закрытие соединения

Ключевые моменты:

  • Для простых случаев используйте TCPSocket/UDPSocket
  • Для серверов - TCPServer
  • Для неблокирующих операций - select/nonblock
  • Всегда обрабатывайте ошибки и закрывайте соединения
  • Для безопасного обмена используйте SSL

Помните:

  1. TCP гарантирует доставку, UDP - нет
  2. UNIX сокеты работают только на одном хосте
  3. Для высоконагруженных серверов рассмотрите EventMachine или Async
  4. Всегда устанавливайте таймауты на сетевые операции