OSX El Capitanにrbenvで1.8.7-p375を入れる

もういい加減にしたいと思っている1.8.7なのですが、 残っているところがある以上、こちらでも開発環境を用意しなければならないのです。

rbenv install 2.2.3

とかは問題なくインストールできました。しかし、

rbenv install 1.8.7-p375

とやってみたのですが、エラーが出ます。

ERROR: This package must be compiled with GCC, but ruby-build couldn't
find a suitable `gcc` executable on your system. Please install GCC
and try again.

DETAILS: Apple no longer includes the official GCC compiler with Xcode
as of version 4.2. Instead, the `gcc` executable is a symlink to
`llvm-gcc`, a modified version of GCC which outputs LLVM bytecode.

For most programs the `llvm-gcc` compiler works fine. However,
versions of Ruby older than 1.9.3-p125 are incompatible with
`llvm-gcc`. To build older versions of Ruby you must have the official
GCC compiler installed on your system.

TO FIX THE PROBLEM: Install Homebrew's apple-gcc42 package with this
command: brew tap homebrew/dupes ; brew install apple-gcc42

You will need to install the official GCC compiler to build older
versions of Ruby even if you have installed Apple's Command Line Tools
for Xcode package. The Command Line Tools for Xcode package only
includes `llvm-gcc`.

BUILD FAILED (OS X 10.11 using ruby-build 20150818-4-g8d6ff29)

こんなエラーが出ます。エラーメッセージのとおりにやってみたのですが、 全然ダメでした。仕方ないのでビルドディレクトリに移動してみて、 ./configureをやったらあっさり通ります。 なんだ、ruby-buildの問題か。というわけで、デバッグをしはじめました。 ちょっとだけ追ってみたら、require_gccのあたりのようで、 gccのチェックをしている模様。

--- a/share/ruby-build/1.8.7-p375
+++ b/share/ruby-build/1.8.7-p375
@@ -1,3 +1,3 @@
-require_gcc
+# require_gcc

これであっさり通るようになりました。 ちゃんと直すのは面倒だなぁ。っというわけで誰かお願いします。 bashの動きを調べるのめんどい。

あなたならどうするこのルーター設定

とあるシステムと接続するにあたって、 サービス元のIPアドレス 192.168.10.1/24 持ち込みルータのIPアドレス 192.168.20.1/24 サービス先のIPアドレス 192.168.30.1/24 ポート番号10000 ってな感じで、全部サブネットと違っていて、ポートが指定されてしまっている。 NATで変換したいんだけど、間に192.168.30.0/24のネットワークを 構築する余地もない。

f:id:xibbar:20151005165356p:plain

さてどうする?

解1. がんばってLANを構築し直して、192.168.30.0/24のネットワークにしてしまう。

解2. 静的ルーティングで192.168.100.2のサーバに192.168.30.0/24のルートを向けて、ループバックに192.168.30.2を指定する。仕方ないのでポートはそのまま使う。

どちらの案もNAT使っていないんだけど、通過するだけのパケットのIPとかポートを 捻じ曲げる方法があるって人は教えて下さい。 もうわからん。

追記

lan1にサービス先のサブネットを振り、Static NATをすることで実現できました。 試していないのですが、ポートとIPを組み合わせてmasquaradeする方法でもいけるでしょう。

ip lan1 address 192.168.20.1/24
ip lan1 secondary address 192.168.30.2/24
ip lan1 nat descriptor 1
nat descriptor type 1 nat
nat descriptor address outer 1 192.168.30.1-192.168.30.254
nat descriptor address inner 1 192.168.20.1-192.168.20.254
nat descriptor static 1 1 192.168.30.1=192.168.100.1 254

f:id:xibbar:20151006151627p:plain

16進数が文字列になっちゃっているものを配列にしたい

お題 "FF2E0A" => [255, 46, 10] と変換したい

16進数が文字列として入っている。

"FF2E0A".scan(/.{2}/).map{|c|Integer("0x#{c}")}

これだとやはり長い。

["FF2E0A"].pack("H*").bytes

これがやはり最強だな。knuさんに教えてもらったけど、考えてもすぐには出てこない。

["FF2E0A"].pack("H*")

これだけだと文字列になりますので、これはこれで使えます。

pack / unpack は最強。

C言語の外部コマンドへの入出力がよくわからない

とりあえず、現状で必要なのはgzip圧縮されたデータを受信して、展開したいだけ。 あっさりzlibライブラリで解決するかと思いきや、 zlibを読んだ感じでは、gzipはファイル化しなけりゃならないっぽい。 ファイル化とか面倒だと思ってgunzip -cに吸い込ませた結果を 読みこめばいいかとRubyのOpen3感覚で思っていたらハマってしまった。

とりあえず、やりたかったことをRubyで書いた。

gzipdata = open(ARGV[0], "rb").read

# やりたいことは、gzip圧縮されたデータを展開したいだけ
pipe = IO.popen("gunzip -c", "r+")
pipe.write gzipdata
pipe.close_write

data = pipe.read
pipe.close

# 結果の出力
result_file = open("result.jpg", "wb")
result_file.write data
result_file.close

これ、C言語でやろうとするとめんどくさい。 ネットなどで検索すると、pipeを作ってforkしろと書いてある。 プロセス複製とかしなきゃならんのか〜とか思ってちょっと萎える。

もう、gzipデータをRubyプログラムに丸っとパイプかドメインソケットで渡して そっちで処理してくれというプログラムにしちまうか。。

というわけで、誰か偉い人教えて下さい。

  • Cでforkせずともできるものなのか
  • Cではforkするのが常識なのか
  • そもそも何か間違っているのか
  • zlibでファイル作らずともあっさり出来るよ〜みたいな情報

もうわからん。

Rubyでサーバを作ってクライアントにデータを配信 vol.6

vol.5のように時間がかかる処理があると待たされてしますので、 それを解決したコードが次のコードです。

require "socket"

port = if ARGV[0] then ARGV[0] else 'echo' end
gate = TCPServer.open(port)

sockets = [gate, STDIN]
clients = []
loop do
  r_sockets = IO.select(sockets+clients)[0]
  r_sockets.each do |socket|
    case socket
    when STDIN
      str = socket.gets
      threads = []
      clients.each do |client|
        threads << Thread.new do
          client.puts str.upcase
          sleep 2
        end
      end
      threads.each{|t|t.join}
    when TCPServer
      client = socket.accept
      clients << client
      p clients
    when TCPSocket
      unless socket.eof?
        str=socket.gets
        socket.puts str.upcase
      else
        socket.close
        clients.delete(socket)
        p clients
      end
    end
  end
end

gate.close

スレッドを使って次々と並列して処理を開始し、最後にjoinで処理が全て終了するのを待ちます。

とりあえずRubyではここまで検証をしたかったので一旦終了。

Rubyでサーバを作ってクライアントにデータを配信 vol.5

vol.4のプログラムはclientsにひとつずつ配信しています。 一瞬で終わるならいいのですが、ネットワークなんて遅延しまくりなので、 あまりよろしくありません。一度に配信処理をしたいものです。 試しに2秒遅延を入れてみると、こうなります。

require "socket"

port = if ARGV[0] then ARGV[0] else 'echo' end
gate = TCPServer.open(port)

sockets = [gate, STDIN]
clients = []
loop do
  r_sockets = IO.select(sockets+clients)[0]
  r_sockets.each do |socket|
    case socket
    when STDIN
      str = socket.gets
      clients.each do |client|
        client.puts str.upcase
        sleep 2
      end
    when TCPServer
      client = socket.accept
      clients << client
      p clients
    when TCPSocket
      unless socket.eof?
        str=socket.gets
        socket.puts str.upcase
      else
        socket.close
        clients.delete(socket)
        p clients
      end
    end
  end
end

gate.close

このくらいレスポンスが悪いと、ああ、なんとかしなきゃならないなと思いますね。


multisend04 - YouTube

Rubyでサーバを作ってクライアントにデータを配信 vol.4

スレッドなどでそれぞれ待ち受けるという手もありますが、 IOの入出力を待つ手段としては一般的ではないようです。 IOの動きを監視して待ち受け、動きがあったら場合分けをして 処理をするのが一般的なようです。 これをReactorパターンといいます。 IO.selectで配列を引数に入れると、 引数で入れたものに動きがあるまで待ち続けます。 動きがあると、次のステップに進むので、そこで場合分けをして処理をします。

require "socket"

port = if ARGV[0] then ARGV[0] else 'echo' end
gate = TCPServer.open(port)

sockets = [gate, STDIN]
clients = []
loop do
  r_sockets = IO.select(sockets+clients)[0]
  r_sockets.each do |socket|
    case socket
    when STDIN
      str = socket.gets
      clients.each do |client|
        client.puts str.upcase
      end
    when TCPServer
      client = socket.accept
      clients << client
      p clients
    when TCPSocket
      unless socket.eof?
        str=socket.gets
        socket.puts str.upcase
      else
        socket.close
        clients.delete(socket)
        p clients
      end
    end
  end
end

gate.close

いくつも同時に接続して、配信可能です。 f:id:xibbar:20150817141657p:plain