Google Code Prettify

2015年2月28日 星期六

使用 TortoiseGit 連上 remote bare repository

大部份的 git 文件、書籍教的都是在命令列下進行版本控管,但是,這樣的使用方式絕對是無法被大多數的開發人員接受的! 這裡介紹一個好用的 client 軟體 - TortoiseGit,它就像 TortoiseSVN 一樣的好用 (用過 svn 的人都知道…)。

在使用 TortoiseGit 前,請確定你的電腦已經安裝好以下軟體:
  1. git
  2. TortoiseGit
接著再確定你已經依「ssh公錀認證登入」設定好你的環境,至於 remote bare repository 我們這裡是架設在 linux 上,架設的方法參考「建立一個集中控管的 git repository」,接下來的說明也會在這個環境下進行。
  • 設定 Credential
在檔案總管中按滑鼠右鍵,在跳出的選單中會看到「Git Setting」,點選後出現如下的視窗。


還記得在「建立一個 remote git bare repository」中,我們用 ssh clone remote repository 的資料到 local 時用的 url 嗎? 當時用的是 ssh://192.168.0.115/~/repo.git,當時有說明,ssh://192.168.0.115 是使用 ssh 連線進 linux 的方法,在這裡,如上圖所示,於「Git/Credential」中輸入相同的 url,再於 Username 處輸入要登入的帳號,然後按【Add new/Save】,這樣我們就可以使用這個帳號及 ssh 連線到  remote bare repository 了。
  • 設定 User Info
如下圖所示,輸入 Name、Email,這樣當我們 commit、push 時,git 會把這些資訊記錄在 log 裡。


  • clone remote bare repository
在 D 槽建立一個命名為 TEST 的目錄,在檔案總管中進到該目錄並按滑鼠右鍵,出現如下的選單。


繼擇「Git Clone」會出現如下視窗,於 URL 欄位,輸入我們要 clone 的 remote bare repository,於 Directory 欄位輸入一個 local 的目錄,clone 下來的檔案會放入該目錄,按下【OK】。






  • 新增檔案
在 D:\TEST\repo 目錄下,我們隨意新增一個檔案,如下,我們新增了一個 fourth.txt 的檔案。


現在我們要將它 commit 到 local repository 後,再 push 到 remote bare repository,如下,在該檔案上按滑鼠右鍵,出現如下的選單,選擇「Git Add」後按【OK】。


在於該檔案下按滑鼠右鍵,出現如下選單,選擇「Git Commit」,在接下來出現的對話視窗中的 Message 輸入一些說明文字後按【OK】,這樣我們就將這個檔案 commit 到 local repository 了!


接下來於該目錄的任意空白處按下滑鼠右鍵,選擇「Git Push」,將會跳出如下對視窗,於 Local 處輸入「master」,其實輸入任何名稱都可以,這是 branch 的名稱,一般來說,第一個 branch 我們會將它命名為 master。


至於上圖的 Remote 選單,一開始應該是空白,因為我們尚未指定在 remote repository 我們要使用那個 reference。所以,要先按【Manage】設定好 remote 端的 reference,如下圖,點選「Git/Remote」,然後於 Remote 欄位輸入 origin,這個名稱也不是規定的,只是習慣上第一個參照會取這個名稱,在 URL 欄位輸入  bare repository 的位置後按【確定】即會如上圖所示,然後按【OK】,就將 fourth.txt push 到 remote bare repository 了!


  • 瀏覽 repository 內容
可以 commit、push 後,delete、pull、fetch 等功能在按滑鼠右鍵出現的選單裡都有對應的選項,這裡就不多作說明,不過,大部份的人應該會有個疑問,要怎麼看 repository 裡的內容呢? 在滑鼠右鍵的選單中選擇「Git Repo-browser」,就會出現如下圖 repository 的內容。


上圖要特別注意的是紅框中的 HEAD,在 Git 中 HEAD 永遠指向目前這個 branch 的最新版本,所以視窗中顯示的那四個檔案正是目前 local repository 的最新內容。如果想看 remote bare repository 的最新內容呢? 點選左框中的「HEAD」,出現如下的視窗,藍框的部份是修改的歷程,綠框的部份是最後一項修改的檔案,如果要比對 local 和 remote 的差別,可以在檔名上 double click,即會出現比對的視窗,不過,我們可以再 click 紅框的 master 看一下 remote 的狀況。


點選 master 後出現如下視窗,右邊的選個項目第一個「headers/master」指向 local repository 目前的 master 這個 branch 的最新狀況,第二個「remotes/origin/HEAD」指向目前所選擇的 branch 的 remote 端最新狀況,第三個「remotes/origin/master」則指向 master 這個 branch 於 remote 端的最新狀況,因為在這裡只有一個 master branch,所以第二、三項是指向同一個地方。


問題是,我們想看的是有那些檔案及檔案的內容,該怎麼做呢? 於上圖右邊選單選擇我們要看的指標,例如,我們要看「heads/master」,就在這個項目上 double click,將會出現如下圖,該選項的所有版控歷程。這時候就可以任意選擇一個版本,在上面按右鍵選單中的「Browse repository」,就會出現該版本的所有檔案,要看檔案內容,再於檔案上 double click 即會出現檔案內容。


2015年2月25日 星期三

scientific linux 下安裝 chrome


半年多前,為了在 scientific linux 上安裝 chrome 花了點時間,後來找到底下這一篇文章,總算解決了…

How to install Google Chrome 28+ on RHEL/CentOS 6 or 7

今天意外發現,還有更簡單的方法,如下:
  • 在 /etc/yum.repos.d/google-chrome.repo 中新增如下內容:
[google-chrome]
name=google-chrome
baseurl=http://dl.google.com/linux/chrome/rpm/stable/$basearch
enabled=1
gpgcheck=1
gpgkey=https://dl-ssl.google.com/linux/linux_signing_key.pub
  • yum info google-chrome-stable
  • yum install google-chrome-stable
三個步驟就搞定了!

2015年2月24日 星期二

建立一個 remote git bare repository

當一個團隊一起開發程式,會需要一個共用的 git repository 來控管程式碼,這時候該怎麼做呢? 這裡要讓 git repository 安裝在 Linux 上,所以請先依「ssh 公鑰認證登入」將自己的帳號設定好,這裡用的帳號是 steven,所以需要讓 steven 這個帳號在 Git Bash 下可以直接登入到 Linux server,如下圖所示。


接著按照下面的步驟做:
  • 建立 git 帳號
建立一個 git 帳號,用來管理 git repository,基本上這個帳號只需要一般使用者帳號即可。


上面的指令指定了這個新建立的帳號為 git,是系統帳號,使用的是 bash shell,家目錄在 /home/git,記得將這個目錄的權限改為 770,讓同群組的成員也可以存取。
  • 將開發人員帳號加入 git 群組
這是為了讓開發人員有權限可以存取 git repository。


如上,可以看到 steven 現在已經屬於 git 群組的成員。
  • 在 git 目錄下建立 bare repository
在建立一個共用的 repository (bare repository) 前,先建立一個一般的 repository,如下:


然後在 /home/git 目錄下,使用 git clone 指令,建立一個 bare repository,如下:


我們將這個 bare repository 命名為 repo.git,後面多了 .git 這是一般的命名習慣,不一定要遵守。
  • 在開發人員家目錄建立連結到 bare repository
  • 將 bare repository 拷貝一份到 local 端
設定環境變數 GIT_SSH,我的 Git 是安裝在 E:\Git 目錄下,所以 GIT_SSH 設定為 E:\Git\usr\bin\ssh.exe。
開啟 Git Bash,輸入 git clone ssh://192.168.0.115/~/repo.git,如下:
注意看上面的 ssh://192.168.0.115,這其實是表示用 ssh 連線到 192.168.0.115 這個伺服器,就是我們上面的第一個步驟,登入後就拷貝家目錄下的 repo.git 到 local 端來。
  • push 檔案到 bare repository
設定好了之後當然要試看看對不對,如下圖所示,在 ~/repo 目錄下新增一個 first.txt 的檔案,並將它 push 到 bare repository。


2015年2月16日 星期一

ssh 公鑰認證登入

大部份情況下用 ssh 連線到 Linux 主機,就是連上去後打帳號、密碼後登入,但其實 ssh 還提供公鑰認證的方式,就是在本機安裝一把私鑰,在 Linux 主機上安裝一把公鑰,那麼使用 ssh 連線時,就可以直接連線,不需要輸入帳號、密碼,方法如下: (這裡使用 putty 連線 Linux)
  • 產生公鑰和私鑰
執行如下指令,產生 ssh 所需要的公鑰和私鑰。
ssh-keygen -f steven
這樣會產生命名為 steven 的私鑰及命名為 steven.pub 的公鑰,接著開啟 PuTTY Key Generator,點選上方選單「Conversions/Import Key」,如下圖,按【Save private key】,將 private key 存成 putty 所要的格式,這裡假設存成的檔名為 steven.ppk。

  • 設定 Putty
如下圖,輸入要連線的主機的 IP 或 host name,並為這個 session 取個名稱後按【Save】。
於「Connection/Data」的 Auto-login username 處輸入要登入的帳號。
於「Connection/SSH/Auth」的 Private key file for authentication 設定私鑰的檔名,然後點選左邊的 Session,回到最開頭的那個畫面後按【Save】,將上述的設定儲存。

  • 部署公鑰
使用 ssh-copy-id 將公鑰部署到 Linux Server 上,steven.pub  是公鑰檔名,steven 是帳號,192.168.253.128 是主機的 IP。
ssh-copy-id -i steven.pub steven@192.168.253.128
部署上去後,在 Linux 主機上的 /home/steven/.ssh 目錄下,會出現一個名為 authorized_keys 的檔案,公鑰被放入這個檔案裡了! 如果這個檔案原本就已存在,這一次部署的公鑰會被 append 在檔案後頭。部署後要記得檢查一下 /home/steven 目錄的權限必須是 700.ssh 目錄的權限也必須是 700,且 authorized_keys 這個檔案的權限必須是 600,.ssh 及 authorized_keys 的使用者和群組需要是 steven steven,如果不是當然就自己改一下… (權限錯誤在連線時有可能會出現 "server refused our key" 的錯誤訊息!)
chmod 600 authorized_keys
chown steven.steven authorized_keys
【註】一般的 Windows 上不會有這支程式,我的電腦因為有安裝 Cygwin 才會有,這個軟體提供了在 Windows 作業系統下的 UNIX 模擬環境,不想安裝這個軟體的話,可以把公鑰上傳到 Linux Server 後,將公鑰放入 .ssh 目錄,然後執行如下指令:
cat steven.pub >> authorized_keys
也就是把公鑰匯入 authorized_keys 檔就可以了。
  • 修改 ssh 設定檔
使用 root 權限,開啟 /etc/ssh/sshd_config,這個檔有很多選項,下面只列出要設定的部份,因為我們產生的是 RSA 演算法的公私鑰,所以進行如下設定,這些設定簡單的說,就是開啟 RSA public key 授權,並指明 public key  所放置的位置。其實這些選項原本就都存在於 sshd_config 檔裡,只是被 # 設成註解,只要找到這些選項將 # 移除就可以了。
复制代?
HostKey /etc/ssh/ssh_host_rsa_key

RSAAuthentication yes
PubkeyAuthentication yes
AuthorizedKeysFile      .ssh/authorized_keys
复制代?
修改完 ssh 設定必須重啟服務,如下:
service sshd restart
  • 測試
開啟 putty 選擇一開始我們儲存的那個 session,按【Open】進行連線,不需要輸入帳號、密碼,即可以看到如下畫面,表示已登入成功。
ps. 如果一直無法登入,可以開啟 debug log 來查原因,在 /etc/ssh/sshd_config 檔中,找到 LogLevel INFO,將它改為 LogLevel DEBUG3。重新登入後,打開 /var/log/secure 檔查看 log。

  • Git Bash 的 ssh 連線
上面介紹的是使用 putty 的 ssh 連線方式,如果是要用 Git Bash 中的 ssh 連到遠端的 Linux server,環境要怎麼設定? 如下:
  1. 在 C:/Windows/System32/drivers/etc/hosts 中,新增 192.168.253.128 gitserver。
  2. 開啟 Git Bash,因為我在登入 Windows 時,是用 Steven 這個帳號,開啟 Git Bash 時,預設會連到他的家目錄 /c/Users/Steven。
  3. 進入 .ssh 目錄,開啟或新建一個 config 檔,然後在這個檔案裡輸入如下內容:
host gitserver
            user steven
            hostname 192.168.253.128
            port 22
            identityfile /d/seal/steven


這樣的設定是指,要使用 steven 這個帳號登入 192.168.253.128 這台主機,私鑰的檔案是放在 /d/seal/steven,設定好後輸入如下指令,即可登入遠端的 Linux Server。
ssh gitserver

如果不想設定這個 config 檔,可以直接將私鑰放在家目錄的 .ssh 子目錄下,也就是將私鑰放到 /c/Users/Steven/.ssh 目錄也可以。

2015年2月11日 星期三

安裝最新的 git

在 linux 下,要安裝 git 照理說,只要用 root 權限執行如下指令就好了:
yum install git-core
不過,這樣安裝的 git 不是最新版,想安裝特定版本,可以下載 souce code 自己 compile,這裡說明一下怎麼做。
  • 移除舊版 git
yum remove git-core
  • 安裝相依的函式庫
yum install curl-devel expat-devel gettext-devel openssl-devel zlib-devel
  • 下載原始碼
wget https://www.kernel.org/pub/software/scm/git/git-2.3.0.tar.gz
當然,如果是在公司裡,可能有些 server 基於各種理由無法直接連線到 internet,那麼就用瀏覽器連到  https://www.kernel.org/pub/software/scm/git 選擇要下載的版本下載。
  • 解壓縮
tar zxf git-2.3.0.tar.gz
  • 進入原始碼的目錄
cd git-2.3.0
  • 編譯
./configure
編譯時有可能會出現如下錯誤訊息:
Can't locate ExtUtils/MakeMaker.pm in @INC (@INC contains ... 
 這可能是 perl 的模組沒有安裝,試著執行下列指令安裝相關模組:
 yum -y install perl-devel perl-ExtUtils-Install perl-ExtUtils-ParseXS
  • make
make all
  • 安裝
make install
這樣就安裝好了,接下來看一下安裝到那個目錄,以及版本是否如我們下載的一樣。




2015年2月7日 星期六

Tibco RV request/reply 的同步與非同步

Tibco RV 有提供 request/reply 模式,也有提供 publish/subscribe 模式,這兩種模式的用途分別是,request/reply 用在一對一的狀況下,而 publish/subscribe 則是用在一對多。雖然 request/reply 是用在一對一,Tibco RV 仍提供了同步與非同步兩種模式,在說明同步和非同步之前,先看一下 server 端的程式,如下:
  1 package idv.steven.rv;
  2 
  3 import com.tibco.tibrv.Tibrv;
  4 import com.tibco.tibrv.TibrvException;
  5 import com.tibco.tibrv.TibrvListener;
  6 import com.tibco.tibrv.TibrvMsg;
  7 import com.tibco.tibrv.TibrvMsgCallback;
  8 import com.tibco.tibrv.TibrvMsgField;
  9 import com.tibco.tibrv.TibrvRvdTransport;
 10 import com.tibco.tibrv.TibrvTransport;
 11 
 12 public class Server implements TibrvMsgCallback {
 13     private TibrvTransport transport = null;
 14     
 15     private String service = null;
 16     private String network = null;
 17     private String daemon  = null;
 18     private String subject = null;
 19     
 20     private double server_timeout = 60;
 21        
 22     public void run(String[] args) {
 23         boolean eventReceived = false;
 24         
 25         int i = loadParameters(args);
 26         if (i > args.length-1) {
 27             usage();
 28             return;
 29         }
 30         
 31         try {
 32             Tibrv.open(Tibrv.IMPL_NATIVE);
 33             
 34             transport = new TibrvRvdTransport(service,network,daemon);
 35             subject = args[args.length-1];
 36             new TibrvListener(Tibrv.defaultQueue(), this, transport, subject, null);
 37             
 38             while (!eventReceived) {
 39                 eventReceived = Tibrv.defaultQueue().timedDispatch(server_timeout);
 40                 if (eventReceived) {
 41                     System.out.println("receive a message");
 42                 }
 43                 else {
 44                     System.out.println("timeout");
 45                 }
 46             }
 47         } catch (TibrvException | InterruptedException e) {
 48             e.printStackTrace();
 49         }
 50         finally {
 51             try {
 52                 Tibrv.close();
 53             } catch (TibrvException e) {
 54                 e.printStackTrace();
 55             }
 56         }
 57     }
 58     
 59     @Override
 60     public void onMsg(TibrvListener listener, TibrvMsg msg) {
 61         String replySubject = msg.getReplySubject();
 62         if (replySubject == null) {
 63             System.out.println("no reply subject,discard client's request");
 64             return;
 65         }
            System.out.println("TibrvMsg's reply subject: " + replySubject);
 66         
 67         try {
 68             TibrvMsgField field = msg.getField("sendData");
 69             String sendData = (String) field.data;
 70             System.out.println("sendData: " + sendData);
 71 
 72             TibrvMsg replyMsg = new TibrvMsg();
 73             replyMsg.setSendSubject(replySubject);
 74             replyMsg.update("replyData", "Nice to meet you.");
 75             transport.send(replyMsg);
 76         } catch (TibrvException e) {
 77             e.printStackTrace();
 78         }
 79     }
 80     
 81     int loadParameters(String[] args)
 82     {
 83         int i=0;
 84         while(i < args.length-1 && args[i].startsWith("-"))
 85         {
 86             if (args[i].equals("-service"))
 87             {
 88                 service = args[i+1];
 89                 i += 2;
 90             }
 91             else
 92             if (args[i].equals("-network"))
 93             {
 94                 network = args[i+1];
 95                 i += 2;
 96             }
 97             else
 98             if (args[i].equals("-daemon"))
 99             {
100                 daemon = args[i+1];
101                 i += 2;
102             }
103             else
104                 usage();
105         }
106         return i;
107     }
108     
109     void usage()
110     {
111         System.err.println("Usage: java idv.steven.rv.Server [-service service] [-network network]");
112         System.err.println("            [-daemon daemon] <subject>");
113         System.exit(-1);
114     }
115 
116     public static void main(String[] args) {
117         new Server().run(args);
118     }
119 }
要執行上面的程式,可於命令列下如下指令:
java idv.steven.rv.Server -service 7500 -network ;225.1.1.1 -daemon tcp:7500 TEST.RV
這指令指出,UDP 的廣播網址是 225.1.1.1,client 端要連線到 daemon 的話,使用 TCP 7500 port,傾聽的 subject 是 TEST.RV,所以,client 端送出的訊息的 subject 也要是 TEST.RV。關於 service、network、daemon 的詳細說明可以參考 Oracle 官網上的說明 http://docs.oracle.com/cd/E21455_01/common/tutorials/connector_rendezvous_daemon.html
server 的程式說明如下:
  1. Line 12: 這個類別實作了 TibcoMsgCallback 介面,當收到訊息時,RV 會呼叫 onMsg method。
  2. Line 39: 訊息分派機制,如果沒有寫這一行,RV 的訊息沒辦法分派,上面是呼叫 timedDispatch(timeout),也就是每隔 timeout 的秒數,就會離開這個 method,另一個用法是使用 dispatch(),這個方法就不會 timeout,會一直停在那一行直到有訊息時進行分派。
  3. Line 73: 將 client 指定的 reply subject 設定給 send subject,這樣 client 端才能收到訊息。


  • 同步
接下來,我們先看一下當 client 端選擇同步的模式時,程式要怎麼寫。
 1 package idv.steven.rv;
 2 
 3 import com.tibco.tibrv.Tibrv;
 4 import com.tibco.tibrv.TibrvException;
 5 import com.tibco.tibrv.TibrvMsg;
 6 import com.tibco.tibrv.TibrvRvdTransport;
 7 import com.tibco.tibrv.TibrvTransport;
 8 
 9 public class SyncClient {
10     private String service = null;
11     private String network = null;
12     private String daemon  = null;
13     
14     private TibrvTransport transport = null;
15     private double timeout = 5; //second
16     
17     public void run(String[] args) {
18         int i = loadParameters(args);
19         if (i > args.length-2) {
20             usage();
21             return;
22         }
23         
24         try {
25             Tibrv.open(Tibrv.IMPL_NATIVE);
26             
27             transport = new TibrvRvdTransport(service,network,daemon);
28             
29             String subject = args[args.length-2];
30             String sendData = args[args.length-1];
31             
32             TibrvMsg msg = new TibrvMsg();
33             msg.setSendSubject(subject);
34             msg.update("sendData", sendData);
35             
36             TibrvMsg replyMsg = null;
37             replyMsg = transport.sendRequest(msg, timeout);
38             
39             if (replyMsg == null)
40                 System.out.println("request time-out");
41             else 
42                 System.out.println("Receive reply msg:" + replyMsg);
43         } catch (TibrvException e) {
44             e.printStackTrace();
45         }
46         finally {
47             try {
48                 Tibrv.close();
49             } catch (TibrvException e) {
50                 e.printStackTrace();
51             }
52         }
53     }
54     
55     int loadParameters(String[] args)
56     {
57         int i=0;
58         while(i < args.length-1 && args[i].startsWith("-"))
59         {
60             if (args[i].equals("-service"))
61             {
62                 service = args[i+1];
63                 i += 2;
64             }
65             else
66             if (args[i].equals("-network"))
67             {
68                 network = args[i+1];
69                 i += 2;
70             }
71             else
72             if (args[i].equals("-daemon"))
73             {
74                 daemon = args[i+1];
75                 i += 2;
76             }
77             else
78                 usage();
79         }
80         return i;
81     }
82     
83     void usage()
84     {
85         System.err.println("Usage: java idv.steven.rv.Client [-service service] [-network network]");
86         System.err.println("            [-daemon daemon] <subject> <messages>");
87         System.exit(-1);
88     }
89 
90     public static void main(String[] args) {
91         new SyncClient().run(args);
92     }
93 
94 }
要執行上面的程式,可於命令列下如下指令:
java idv.steven.rv.SyncClient -service 7500 -network ;225.1.1.1 -daemon tcp:7500 TEST.RV Hello
這裡只是簡單的傳送一個 Hello 訊息給 server,server 收到後會透過 System.out.println 印出來,接下說說明一下上面的程式:
  1. Line 37: 送訊息給 server 時,使用 sendRquest,並等待 server 回覆訊息,等待的時間是 timeout 指定的秒數。
  2. Line 48: 程式結束前,記得關閉 Tibco 回收資源,否則程式無法正常結束。

請注意看 server 的紅色部份 (line 73、75),這裡不管 client 送過來的訊息是以 request/reply 方式傳送,或是以 publish/subscribe 方式傳送,都用同樣的方式回覆,但是,在 Tibco 的官方文件裡,這種方式只用在 publish/subscribe,而 request/reply 不需要 setReplySubject,send 則要改為 sendReply。這個範例程式雖然可以收到並回覆訊息,但是否會有意料之外的事發生? 我無法確定!

另外,注意看 server 收到 client 傳來的訊息時,client msg 的 reply subject 是由 TibcoRV 產生的,會是類似 _INBOX.C0A80064.5548C4953B2.1 這樣以 _INBOX 開頭的字串,_INBOX 是 TibcoRV 的保留字,將這個 reply subject 設到要回傳的訊息的 send subject,就可以將訊息傳回原來的 client 程式,但是,如上面所說,這是非正規的寫法。
  • 非同步
非同步的 client 端寫法很類似 server 端,程式如下:
  1 package idv.steven.rv;
  2 
  3 import com.tibco.tibrv.Tibrv;
  4 import com.tibco.tibrv.TibrvException;
  5 import com.tibco.tibrv.TibrvListener;
  6 import com.tibco.tibrv.TibrvMsg;
  7 import com.tibco.tibrv.TibrvMsgCallback;
  8 import com.tibco.tibrv.TibrvMsgField;
  9 import com.tibco.tibrv.TibrvRvdTransport;
 10 import com.tibco.tibrv.TibrvTransport;
 11 
 12 public class AsyncClient implements TibrvMsgCallback {
 13     private String service = null;
 14     private String network = null;
 15     private String daemon  = null;
 16     
 17     private TibrvTransport transport = null;
 18     private boolean running = true;
 19     
 20     public void run(String[] args) {
 21         int i = loadParameters(args);
 22         if (i > args.length-2) {
 23             usage();
 24             return;
 25         }
 26         
 27         try {
 28             Tibrv.open(Tibrv.IMPL_NATIVE);
 29             
 30             transport = new TibrvRvdTransport(service,network,daemon);
 31             
 32             String subject = args[args.length-2];
 33             String sendData = args[args.length-1];
 34             String replySubject = transport.createInbox();
 35             new TibrvListener(Tibrv.defaultQueue(), this, transport, replySubject, null);
 36             
 37             TibrvMsg msg = new TibrvMsg();
 38             msg.setSendSubject(subject);
 39             msg.setReplySubject(replySubject);
 40             msg.update("sendData", sendData);
 41             transport.send(msg);
 42             
 43             while (running) {
 44                 Tibrv.defaultQueue().dispatch();
 45             }
 46         } catch (TibrvException | InterruptedException e) {
 47             e.printStackTrace();
 48         }
 49         finally {
 50             try {
 51                 Tibrv.close();
 52             } catch (TibrvException e) {
 53                 e.printStackTrace();
 54             }
 55         }
 56     }
 57     
 58     @Override
 59     public void onMsg(TibrvListener listener, TibrvMsg msg) {
 60         System.out.println("subject: " + listener.getSubject());
 61         
 62         try {
 63             TibrvMsgField field;
 64             field = msg.getField("replyData");
 65             String replyData = (String) field.data;
 66             System.out.println("replyData: " + replyData);
 67         } catch (TibrvException e) {
 68             e.printStackTrace();
 69         }
 70         
 71         running = false;
 72     }
 73     
 74     int loadParameters(String[] args)
 75     {
 76         int i=0;
 77         while(i < args.length-1 && args[i].startsWith("-"))
 78         {
 79             if (args[i].equals("-service"))
 80             {
 81                 service = args[i+1];
 82                 i += 2;
 83             }
 84             else
 85             if (args[i].equals("-network"))
 86             {
 87                 network = args[i+1];
 88                 i += 2;
 89             }
 90             else
 91             if (args[i].equals("-daemon"))
 92             {
 93                 daemon = args[i+1];
 94                 i += 2;
 95             }
 96             else
 97                 usage();
 98         }
 99         return i;
100     }
101     
102     void usage()
103     {
104         System.err.println("Usage: java idv.steven.rv.Client [-service service] [-network network]");
105         System.err.println("            [-daemon daemon] <subject> <messages>");
106         System.exit(-1);
107     }
108 
109     public static void main(String[] args) {
110         new AsyncClient().run(args);
111     }
112 }
要執行的話,在命令列打入如下指令:
java idv.steven.rv.AsyncClient -service 7500 -network ;225.1.1.1 -daemon tcp:7500 TEST.RV Hello
程式說明如下:
  1. Line 12: 實作 TibrvMsgCallback,當收到訊息時 RV 會呼叫 onMsg。
  2. Line 34: 產生一個 InBox,這樣的話,當送出訊息時,RV 會讓 client 和 server 直接連線,也就是說,Line 35 看起來 client 端是透過傾聽 inbox 這個 subject 來接收訊息,似乎別的程式如果知道這個 inbox 字串的話,也可以傾聽相同的 subject 得到訊息內容,事實上是沒辦法的! 採用 InBox 的模式,RV 會將 server 的訊息直接送給 client 端,而不是用 UDP 群播的方式。
  3. Line 41: 送出訊息使用的是 send,和同步的方式不同!
  4. Line 44: 因為是非同步,需要等待 RV 將訊息回送,也要有訊息的分派機制。