2011年8月31日 星期三

Ruby 的 Simple Echo Server 多人連線版本

image 

 

閱讀這篇之前,你可以先閱讀

Ruby 的 Simple Echo Server/Client

 

上一篇提到,若要能多人連線,必須用 Multi-Thread 來實作 Server 端,google 看看有沒有可以用的輪子,找到這個:

https://gist.github.com/313649#file_echo.rb

 

同樣地,上面的程式碼當遇到 Client 不正常關閉的時候,還是會導致 Server 異常,實際應用面上,Client 亂搞 Server 是很常見的,還是得想辦法處理 Client 不正常關閉的問題。(目前仍是用 begin / rescue 來處理)

 

程式碼

echoServerMultiThread.rb

require 'socket'
 
def disconnect(socket)
    dummy, port, ip = socket.peeraddr
    socket.close()
    puts "Close #{socket} from #{ip} port #{port}"
end
 
server = TCPServer.new('', 5555)
 
loop do
    Thread.start(server.accept()) do |client|
        dummy, port, ip = client.peeraddr
        puts "Accept #{client} from #{ip} port #{port}"
        
        loop do
            begin
                eof = client.eof?
            rescue
                disconnect(client)
                break
            else
                if eof
                    disconnect(client)
                    break
                else
                    line = client.readline().chomp()
                    puts "#{ip} #{port}:#{line}"
                    client.puts(line)
                end
            end
        end
    end
end

 

還可以研究看看…

  • Simple Web Server

2011年8月30日 星期二

Ruby 的 Simple Echo Server/Client

image 

今天稍微研究了一下,試著寫出 Ruby 版本的 Echo Server/Client,但目前只能一對一的連著,要等前一個 Client 斷線了,下一個 Client 再連才不會異常。若要能一對多,可能得在 Server 端加入 multi-thread 的機制。

對我來說比較奇怪的地方,是 eof 的行為,若 Client 端不正常關閉(就是直接把 console 關掉),則 eof 指令也會出錯,所以最後才用 begin / rescue 來處理,錯誤訊息如下:

image

可能要先用某個指令檢查連線是否正常,而不能直接用 eof 來問狀況,有空再來更深入的研究看看。

 

底下程式碼是包含 begin / rescue 可正常運行的版本:

程式碼

下載 echoServer.rb

require 'socket'
 
def disconnect(socket)
    dummy, port, ip = socket.peeraddr
    socket.close()
    puts "Close #{socket} from #{ip} port #{port}"
end
 
def listen(server, socket = nil)
    client = server.accept()
    dummy, port, ip = client.peeraddr
    puts "Accept #{client} from #{ip} port #{port}"
    return client, ip, port
end
 
server = TCPServer.new('', 5555)
client, ip, port = listen(server)
 
loop do
    begin
        eof = client.eof?
    rescue
        disconnect(client)
        client, ip, port = listen(server)
    else
        if eof
            disconnect(client)
            client, ip, port = listen(server)
        else
            line = client.readline().chomp()
            puts "#{ip} #{port}:#{line}"
            client.puts(line)
        end
    end
end

 

下載 echoClient.rb

require 'socket'
 
client = TCPSocket.new('127.0.0.1', 5555)
 
while true
    print "Input? "
    client.puts(gets)  
    line = client.readline().chomp()
    puts "Echo back:#{line}"
end

 

還可以研究看看…

  • 追蹤 eof 的錯誤。
  • 使用 multi-thread 讓 Server 可接收多個連線。
  • 可將傳入的訊息廣播給所有連線著的 Client。

2011年8月27日 星期六

Balsamiq Mockups - 超好用的 UI 設計工具

SNAGHTML2cc098a

 

前些日子在 PTT 上看到 adahsu 版友提供的 Balsamiq Mockups 資源連結,點擊進去下載試用了一會,實在是驚為天人!

 

底下皆以 Web UI 為例(Balsamiq Mockups 也可以用在 Web 以外的設計)

 

首先要特別強調的,就是幾乎零學習門檻這件事情。

我把這個工具丟給職業是會計的朋友玩,她只摸了10分鐘不到就可以順利的拉出想要表達的網頁畫面了。再來把 Link 的影片丟給她看,看完之後,我又解釋個 10 分鐘左右(其實重複講了 n 遍)就知道如何使用 Link 了。也就是說,只要是常用網頁的人,在30分鐘內一定可以上手!

 

使用無負擔,生產速度一流!

用 Dreamweaver、Frontpage…等工具來拉出「簡單的 demo 畫面」其實並不簡單,功能太過強大,使用起來一定較為繁瑣。但 Balsamiq Mockups 幾乎完全沒有任何多餘的操作,一切都是為 Mockup 而生,自然生產力會非常驚人!

 

手繪風格帶來的好處 

  1. 用 Dreamweaver、Frontpage 拉出來的頁面,若不多花點心思配色、貼圖,會非常的單調死板;而手繪風格並不需要為此多下功夫,本身就具有設計感。
  2. 擬真雖然比較接近實際的樣子,但其實 demo 階段的產物與最後成品通常會有落差,容易造成與客戶/老闆的期待不符;而手繪風格很清楚的告訴客戶/老闆這只是 demo。(這點其實要視專案性質)

 

其它

google 下去,其實已經有非常多的人介紹了,但我還是忍不住再雞婆的介紹一次,希望可以讓多點人知道這個好用的工具!(至少我是最近才知道的 Orz…)

PTT 的另一位 kurtisgod 版友說 Axure RP 也很不錯,可以找時間玩玩看。

2011年8月24日 星期三

Ruby 兩種寫檔方式的不同

前言

因工作關係,所以開始接觸 Ruby,只摸到一點皮毛,還無法對這個語言有太多的見解,這邊記錄兩種寫檔方式的不同影響。

 

第一種寫法

require 'net/http'
 
File.open('yahoo_logo.gif', 'wb') do |f|
    image = Net::HTTP.get('l.yimg.com', '/tw.yimg.com/i/tw/hp/spirit/yahoo_logo.gif')
    f.write(image)
end

 

第二種寫法

require 'net/http'
 
f = File.open('yahoo_logo.gif', 'wb')
image = Net::HTTP.get('l.yimg.com', '/tw.yimg.com/i/tw/hp/spirit/yahoo_logo.gif')
f.write(image)
f.close()
 

 

兩種方法主要差異在

  • 第一種寫法不用撰寫 f.close() ,檔案也會自行關閉;而第二種寫法,是需要多下達 f.close() 的。
  • 第一種寫法在執行的空間不會留下 f 這個變數;第二種寫法在執行的空間會留下 f 這個變數。

 

結論

原則上第一種比較方便,也比較不容易忘記關檔,在 PC 上運作的話,應該不會有需要使用到第二種。

 

插曲

另外發現到一件有趣的事情,採用第二種寫法時,我故意將 f.close() 註解起來,的確會導致檔案被程式 lock 住,這很合理。

接著我故意將變數 f 的內容換掉,讓 File.open 的 token 在我的程式裡消失,看看是否會導致檔案在 runtime 期間永遠被 lock 住,剛改掉 f 的內容時,檔案的確還是被 lock 住的,沒想到過一段時間後,便 unlock 了!我猜測應該是 garbage collection 掃描頻率到了,將沒有被引用的資源回收了起來,這才將檔案 unlock。

2011年8月11日 星期四

LuaUnit

前言

在撰寫 bmclass 前,search 了一下 Lua 的 unittest 工具,竟有10種以上的套件任君挑選...實際都把玩過,各有各的好,但有些過於簡單,有些過於複雜,當初也是因為這樣才誕生 bmclass,所以我選了一個中庸的 LuaUnit。

資源連結

http://phil.freehackers.org/programs/luaunit/

bmclass 的使用範例

 
-- include bmclass
class = dofile("../bmclass.lua")
 
 
-- incldue test module
require("luaunit")
 
 
-- test start
Testbmlass = {}
 
function Testbmlass:setUp() end
 
function Testbmlass:tearDown() end
 
function Testbmlass:testCreateSimpleClass1()
    local cls = class("cls")
    local obj = cls()
    function obj:__tostring() return "obj" end
    
    assertEquals(tostring(cls), "cls")
    assertEquals(tostring(obj), "obj")
end
 
-- test end
 
LuaUnit:run()

對我來說,其實單元測試只要有 assert 以及 setUp/tearDown 便非常足夠了,剩下的特殊例子大可另外想辦法進行測試,不需要將單元測試搞得太過複雜,當寫測試令人感到無比的負擔,這麼一來會令許多工程師打退堂鼓的。