Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationEventAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
11
Help us understand the problem. What are the problem?

More than 1 year has passed since last update.

@MikuriyaHiroshi

RubyでいろいろなSocket作って遊んでみた。

概要

Socketをつかってプロセス間、ネットワーク間で通信する機会があったので、学んだことをまとめてみる。

今回は下表の4種類のSocketを作って検証した。

STREAM
コネクションあり(通信保証あり)
DGRAM
コネクションレス(通信保証なし)
UNIX
プロセス通信
UNIX ドメイン
ストリーム型ソケット
UNIX ドメイン
DGRAM型ソケット
INET
ネットワーク通信
TCP UDP

Rubyでは、Socketクラスを使って上記4種類の通信を実現できる。
また、TCP、UDP、UNIX ドメインストリーム型ソケットにはラッパークラスも用意されており、より簡単な実装できるようだ。(UNIX ドメインDGRAM型ソケットのラッパーは見当たらない)

以下に、私が行った実装を載せておく。

動作環境

  • macOS Mojave (10.14.2)
  • ruby 2.3.3p222 (2016-11-21 revision 56859) [x86_64-darwin16]

クライアント側

# frozen_string_literal: true

require 'socket'

Socket.open(:INET, :DGRAM) do |sock|
  addr = Socket.sockaddr_in(9001, '127.0.0.1')
  sock.send('hello inet dgram', 0, addr)
end

Socket.open(:UNIX, :DGRAM) do |sock|
  addr = Socket.sockaddr_un('/tmp/unix_dgram.socket')
  sock.send('hello unix dgram', 0, addr)
end

Socket.open(:INET, :STREAM) do |sock|
  addr = Socket.sockaddr_in(9002, '127.0.0.1')
  sock.connect addr
  sock.write 'hello inet stream'
  p sock.gets
end

Socket.open(:UNIX, :STREAM) do |sock|
  addr = Socket.sockaddr_un('/tmp/unix_stream.socket')
  sock.connect addr
  sock.write 'hello unix stream'
  p sock.gets
end

UDPSocket.open do |sock|
  sock.send('hello udp', 0, '127.0.0.1', 9003)
end

TCPSocket.open('127.0.0.1', 9004) do |sock|
  sock.write 'hello tcp'
  p sock.gets
end

UNIXSocket.open('/tmp/unix_server.socket') do |sock|
  sock.write 'hello unix server'
  p sock.gets
end

サーバー側

# frozen_string_literal: true

require 'socket'

# INET DGRAM ( = UDP)
inet_dgram = Socket.new(:INET, :DGRAM)
inet_dgram.bind Socket.sockaddr_in(9001, '<any>')

# INET STREAM ( = TCP)
inet_stream = Socket.new(:INET, :STREAM)
inet_stream.bind Socket.sockaddr_in(9002, '<any>')
inet_stream.listen(5)

# UNIX DGRAM
unix_dgram = Socket.new(:UNIX, :DGRAM)
file0 = '/tmp/unix_dgram.socket'
File.unlink(file0) if File.exist? file0
unix_dgram.bind Socket.sockaddr_un(file0)

# UNIX STREAM
unix_stream = Socket.new(:UNIX, :STREAM)
file1 = '/tmp/unix_stream.socket'
File.unlink(file1) if File.exist? file1
unix_stream.bind Socket.sockaddr_un(file1)
unix_stream.listen(5)

# UDPSocket ( = INET DGRAM)
udp = UDPSocket.open
udp.bind('<any>', 9003)

# TCPServer ( = INET STREAM)
tcp = TCPServer.open(9004)

# UNIXServer ( = UNIX STREAM)
file2 = '/tmp/unix_server.socket'
File.unlink(file2) if File.exist? file2
unix_server = UNIXServer.new(file2)

sockets = [
  inet_dgram,
  unix_dgram,
  inet_stream,
  unix_stream,
  udp,
  tcp,
  unix_server
]

loop do
  res = IO.select(sockets)
  res.first.each do |sock|
    case sock
    when inet_dgram, unix_dgram, udp
      p sock.recvfrom(1024)
    when inet_stream, unix_stream, tcp, unix_server
      client, from = sock.accept
      data = client.recv(1024)
      p from
      p data
      client.puts 'OK'
      client.close
    end
  end
end

sockets.each(&:close)

実行結果

サーバー側のみ

["hello inet dgram", #<Addrinfo: 127.0.0.1:55843 UDP>]
["hello unix dgram", #<Addrinfo: empty-path-AF_UNIX-sockaddr SOCK_DGRAM>]
#<Addrinfo: 127.0.0.1:50513 TCP>
"hello inet stream"
#<Addrinfo: empty-path-AF_UNIX-sockaddr SOCK_STREAM>
"hello unix stream"
["hello udp", ["AF_INET", 53561, "127.0.0.1", "127.0.0.1"]]
nil
"hello tcp"
nil
"hello unix server"

通信成功したことがわかる。
また送信元のIPアドレス(127.0.0.1のこと)が取得できるケースとそうでないケース(nil)があるようだった。(TCPServerとUNIXServerだとこのやり方では取得できないっぽい。する方法はあると思うけど。)

所感

今回はRubyで実装したが、socket通信は言語の壁を超えるので学んでおくと便利そうだなと感じた。(JavaプロセスとPythonプロセスが通信する、WindowsPCとMacOSが通信するなど)
C言語でも同様の処理を実装してみたが、やはりRubyのほうが楽だと感じた。

本件、学び始めて日も浅いので、指摘等ございましたらいただけると幸いです。

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
11
Help us understand the problem. What are the problem?