GTK+ とソケットを使ってチャット通信(Ruby)

20180917214400
 
20180917214517

Ruby/GTK2 の使い方が少しづつわかってきたので、ソケットを使ってチャット通信ソフトを作ってみました。上の例では Linux Mint 18.3 と Ubuntu Budgie を使って実行しています。

まずはサーバ側を立ち上げます。これはふつうに

$ ruby oshaberi.rb

と実行するだけです。次にクライアント側では、localhost や サーバの IPアドレスを指定して

$ ruby oshaberi.rb 192.168.11.7

みたいな感じで立ち上げます。もちろんインターネットに出てはいけません。LAN 内で遊んで下さい。

次に名前を訊いてくるので入れて下さい。

あとは上のエントリーバーから文字を入力すると、自分と相手側のスクロール・ウィンドウに会話の文字が表示されます。というように、簡単でしょう?


コードです。
oshaberi.rb

require 'gtk2'
require 'socket'

module Oshaberi
  class MyWindow < Gtk::Window
    def initialize
      super("Oshaberi")
      set_resizable(true)
      set_size_request(500, 300)
      
      entry = Gtk::Entry.new
      entry.set_editable(true)
      entry.set_height_request(40)
      entry.modify_font(Pango::FontDescription.new("13"))
      
      tv = Gtk::TextView.new
      tv.modify_font(Pango::FontDescription.new("12"))
      tv.set_editable(false)
      tv.set_cursor_visible(false)
      buf = tv.buffer
      
      sock1, sock2 = ARGV[0] ? client(ARGV[0]) : server
      sock1.set_encoding('UTF-8')
      sock2.set_encoding('ASCII-8BIT')
      
      myname, hisname = get_names(sock1, sock2)
      
      entry.signal_connect('activate') do
        st = entry.text
        buf.insert_interactive(buf.end_iter, myname + ": " + st + "\n", true)
        tv.scroll_mark_onscreen(buf.create_mark(nil, buf.end_iter, true))
        entry.set_text("")
        sock2.puts st
      end
      
      sw = Gtk::ScrolledWindow.new
      sw.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC)
      sw.add(tv)
      
      box = Gtk::VBox.new
      add(box)
      box.pack_start(entry, false, true, 0)
      box.pack_start(sw   , true , true, 0)
      
      Thread.new(sock1, tv, buf, hisname) do |sock, tv, buf, name|
        ioc = GLib::IOChannel.new(sock)
        ioc.add_watch(GLib::IOChannel::IN) do |i|
          buf.insert_interactive(buf.end_iter, name + ": " + (i.gets || exit), true)
          tv.scroll_mark_onscreen(buf.create_mark(nil, buf.end_iter, true))
          true    #繰り返し
        end
      end
      
      signal_connect("destroy") {Gtk.main_quit}
      show_all
    end
    
    Port = 29753
    
    def server
      s = TCPServer.open(Port)
      
      puts "接続を待っています..."
      sock2 = s.accept
      sock1 = s.accept
      puts "接続しました"
      [sock1, sock2]
    end
    
    def client(host)
      [TCPSocket.open(host, Port), TCPSocket.open(host, Port)]
    end
    
    def get_names(sock1, sock2)
      print "名前を入力して下さい: "
      myname = STDIN.gets.chomp
      myname = "" if myname.empty?
      sock2.puts myname
      hisname = sock1.gets.chomp
      hisname = "相手" if hisname == ""
      puts "相手の名前は #{hisname} です。"
      [myname, hisname]
    end
  end
  
  def self.start
    MyWindow.new
    Gtk.main
  end
end

Oshaberi.start

 

Ruby/GTK2 については下も参考にしてみて下さい。
marginalia.hatenablog.com