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
このくらいレスポンスが悪いと、ああ、なんとかしなきゃならないなと思いますね。
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
いくつも同時に接続して、配信可能です。
Rubyでサーバを作ってクライアントにデータを配信 vol.3
forkはダメです。forkは別プロセスを立ち上げるので、メモリの共有ができません。 なので、同じデータの一斉配信には向いていません。プロセス間通信とか仕組みが面倒です。
ちゃんと動かないプログラムをとりあえず書いてみました。
require "socket" port = if ARGV[0] then ARGV[0] else 'echo' end gate = TCPServer.open(port) clients = [] pid=Process.pid fork if pid == Process.pid loop do clients << gate.accept p clients end else while msg = STDIN.gets p clients clients.each do |client| if client.closed? clients.delete client else begin client.puts msg.upcase rescue end end end end end gate.close
いきなりforkして、接続待ちと書き出しを別々のプロセスでやっているという 意味のないプログラムです。
Rubyでサーバを作ってクライアントにデータを配信 vol.2
vol.1 のサーバだと、1対1の通信しかできません。これだとサーバ&クライアント型の プログラムとは言い難いので、サーバ1台に対し、クライアント複数台で作ってみます。
require "socket" port = if ARGV[0] then ARGV[0] else 'echo' end gate = TCPServer.open(port) clients = [] s=Thread.new do loop do clients << gate.accept p clients end end while msg = STDIN.gets p clients clients.each do |client| if client.closed? clients.delete client else begin client.puts msg.upcase rescue end end end end gate.close s.join
これだと複数接続できます。接続待ちをするループをThreadにして放置して、 他方でサーバの標準入力待ちを作っています。 接続を切られてしまった場合の処理はどうすればいいのかは よくわかっていません。とりあえず、closed?が返ったり書き込もうとして 例外が返った場合の処理だけしています。 クライアント切っただけではclosed?がtrueにならないのに、 書き込みはできずに例外が返ったりして、よくわかりません。