利用程式發送 MSN 訊息

11/22/2008
會寫這個程式,主要是因為一個重要的 Web 應用程式三不五時會掛掉,而系統管理員希望能在第一時間發現這個情況(以免使用者來電開罵)。

要偵測伺服器是否掛點,可以用 .NET 的 TcpClient 嘗試連接伺服器的特定 port,這種簡單的方法不僅可以用在 Web 伺服器,也可以用於資料庫伺服器(例如:嘗試連接 Oracle 伺服器的 1521 port)。

一旦發現伺服器無法正常運作,程式可以發送 mail 通知系統管理員,可是系統管理員不見得會經常收信,這樣的時效性就不夠快。現在 MSN messenger 已經很普遍,如果發現系統出問題時,能夠發即時訊息給相關人員,似乎是個不錯的辦法。

這裡我用來發送 MSN 即時訊息的元件是 MSNPSharp。這個元件有附一個 Windows Forms 範例程式可以參考,此範例就像一個陽春型的 MSN Messenger,可以讓你登入 MSN 伺服器、發送訊息給聯絡人等等。

仔細研究這個範例程式,大概就可以瞭解 MSNPSharp 的基本用法,同時也可以發現,此元件大量運用了非同步處理的程式撰寫模型。這種非同步的撰寫方式不僅增加了學習門檻,除錯也比較麻煩,更重要的是,這並不是我想要的。

我的需求很簡單:當程式需要發送 MSN 訊息時,就先登入 MSN 伺服器,然後依事先設定的聯絡人逐一發送文字訊息。訊息發送的對象有可能是離線狀態,這沒關係,繼續發送給下一位聯絡人即可。

針對上述需求,我寫了一個簡單的 wrapper 類別:SimpleMsnMessenger。這裡我不打算詳細說明原理,謹列出幾個重點步驟:
  1. 建立 Messenger 物件 => messenger。
  2. 利用 messenger.Connect() 登入伺服器。
    關鍵事件:messenger.Nameserver.SignedIn
  3. 利用 messenger.CreateConversation() 建立 Conversation 物件 => conversation。
    關鍵事件:messenger.ConversationCreated
  4. 利用 conversation.Invite(("someone@hotmail", ClientType.PassportMember); 邀請對方加入對話。
    關鍵事件:conversation.Switchboard.ContactJoined (已成功邀請對方加入對話)
  5. 利用 conversation.SwitchBoard.SendTextMessage() 發送文字訊息。
有興趣研究的朋友可以參考這篇文章:MSNPSharp傳送信息程序詳解,大致瞭解其設計原理後,再對照範例程式來研究,應該就比較容易瞭解 MSNPSharp 的基本用法。

SimpleMsnMessenger 的完整程式碼可按此連結下載。Note: 此程式碼還有許多改進空間,尤其是錯誤處理的部分,但基本上還堪用。

以下是使用此 wrapper class 的範例:

class ServerAliveChecker

{

private SimpleMsnMessenger msn = new SimpleMsnMessenger("robot@hotmail.com", "password");

private void SendMsn(StringBuilder text)

{

string receivers = ConfigurationManager.AppSettings["ReceiverMsn"];

if (String.IsNullOrEmpty(receivers))

{

return;

}

lblStat.Text = "正在傳送 MSN 訊息...";

Application.DoEvents();

string[] msnMail = receivers.Split(';');

try

{

msn.Connect();

// 等待登入動作完成。

while (msn.IsConnecting && msn.ErrorCount <= 0)

{

Application.DoEvents();

}

if (msn.Connected && msn.SignedIn)

{

// 登入成功

foreach (string mail in msnMail)

{

msn.SendTextMessage(mail, text.ToString());

}

// 等待訊息全部發完,但只等待一段時間。

DateTime now = DateTime.Now;

while (!msn.MessageQueueEmpty)

{

Application.DoEvents();

System.Threading.Thread.Sleep(500);

TimeSpan ts = DateTime.Now.Subtract(now);

if (ts.Minutes >= 3)

{

break;

}

}

msn.Disconnect();

}

else

{

string err = "MSN Messenger 登入失敗";

if (msn.ErrorCount > 0)

{

LogError(err + ": " + msn.LastError.Message);

}

else

{

LogError(err + "。" + msn.LastError.Message);

}

return;

}

}

catch (Exception ex)

{

LogError(ex.ToString());

}

}

}



按此連結下載完整的類別與範例原始碼

OOADA3:程式語言的編年史與排行榜

11/15/2008
《Object-Oriented Analysis and Design with Applications 3rd edition》的附錄 A 裡面有一張圖描繪了自 1960 年以來的部分程式語言編年史(圖 A-1)。這張圖有個蠻明顯的錯誤,就是所有的年代都標示為 1960。

中文版會修正這個錯誤,如下圖所示:



若讀者有興趣看看完整版的程式語言編年史,可以到 Éric Lévénez 的網站:http://www.levenez.com/lang/。

另外,原文書的表 A-1 是一份程式語言受歡迎程度的排行榜,其中第一名是 C 語言,Java 位居第二。此排行榜係取自 TIOBE 的 TCP-index,但從官方網站的長期趨勢圖可以看出,Java 從 2005 年擠下 C 語言奪得冠軍寶座之後,就一直獨占鰲頭。可見原文書的表 A-1 應該是 2005 年以前的排名結果。中文版會改用 2008 年 11 月份的 TCP-index 排名。

Position
Nov 2008
Position
Nov 2007
Delta in PositionProgramming LanguageRatings
Nov 2008
Delta
Nov 2007
Status
1 1 Java 20.299% -0.24% A
2 2 C 15.276% +1.31% A
3 4 C++ 10.357% +1.61% A
4 3 (Visual) Basic 9.270% -0.96% A
5 5 PHP 8.940% +0.25% A
6 7 Python 5.140% +0.91% A
7 8 C# 4.026% +0.11% A
8 11 Delphi 4.006% +1.55% A
9 6 Perl 3.876% -0.86% A
10 10 JavaScript 2.925% 0.00% A
11 9 Ruby 2.870% -0.21% A
12 12 D 1.442% -0.26% A
13 13 PL/SQL 0.939% -0.24% A
14 14 SAS 0.729% -0.40% A--
15 18 ABAP 0.570% -0.08% B
16 19 Pascal 0.511% -0.13% B
17 17 COBOL 0.510% -0.20% B
18 25 ActionScript 0.506% +0.04% B
19 23 Logo 0.489% -0.04% B
20 16 Lua 0.473% -0.27% B

Configuration Management:形態管理?

11/01/2008
Configuration Management 這個術語比較常見的譯法有「組態管理」和「建構管理」,另外還有「配置管理」、「形態管理」等等。

我在翻譯時採用「組態管理」,主要原因是:
  • Configuration management 跟我們一般講的軟體「建構」不完全一樣。的確,它是有包含軟體建構過程中的版本控制與發行作業,但這些並不等同於 configuration management。
  • 「建構管理」很容易聯想成 "construction management"。
  • Configuration 個人多譯為「組態」。
這裡無意比較各種譯法,只是......把 configuration management 譯為「形態管理」,並將 configuration 解釋為「形」(外觀)和「態」(功能),這會不會有點牽強呢?摘錄其解釋如下:

形態管理 (Configuration Management, 以下縮寫 CM ) = 產品 的 "形" 與 "態" 的 管理 !!
"形" = 物理特性 (Physical Characteristics) = 產品 的 外觀 (How the Product looks like)

"態" = 功能特性 (Functional Characteristics) = 產品 的 功能 (What the Product is suppose to do)

套用在軟體開發領域總覺得怪...

前 100 名最佳軟體工程書籍

9/16/2008

舊文打包:Java 網站應用程式入門實作

9/10/2008
有位網友來信詢問五年前寫的幾篇 Java Web 應用程式設計入門的文章:

1.0 前言
2.0 Servlet 程式設計初步(含 JDK 與 Tomcat 的安裝與環境設定)
3.0 JSP 程式設計初步
4.0 JSP、Servlet 與 JavaBean 的組合應用
5.0 JDBC 入門
6.0 Model-View-Controller 範例

我把以上文章全都包成一個壓縮檔,有需要的朋友可按此下載。解開後開啟 index..htm 即可。

不過,這些文章有點舊了,裡面有些東西或許有一些過時了,建議可以找一些新發表的文章或書籍。

讀蔣勳《孤獨六講》的一點聯想

8/02/2008
我的記性不好,平日工作的一些瑣事常過耳即忘,以致於常出槌、鬧笑話,不僅如此,就連小時候的童年記憶都非常少。每聽到有人回憶童年往事,不禁納悶,為什麼小時候的事情我都想不太起來?能記得的事情寥寥可數。照某些心理學的說法,可能我的童年大都是些不堪回首的痛苦回憶,所以大腦選擇忘記它們吧!

雖然能記得的童年往事不多,但有些記得的部分卻格外印象深刻,只要受到一些外在事物的刺激,那些記憶就會突然湧現。閱讀蔣勳的《孤獨六講》,便令我想起小時候祖父過世的情景。當時家裡長輩請了和尚來誦經,我們家三個小孩年紀都很小,對喪禮的規矩並不清楚,只知道依大人指示行禮、跪拜。我記得的其中一幕,是一些親戚長輩們對我們猛喊:「快哭!要哭!哭大聲點!」我跟祖父相處時間不多,不能說感情深厚,只覺得他是個慈祥又有點滑稽的老人,可是聽到那些大人的命令,我反而更哭不出來,只覺想笑。不知為何,別的記不牢,這件事情的印象卻特別深刻。

蔣先生在書裡面說:「如果你仔細觀察,便會發現在群體文化中,婚禮喪禮都是表演,與真實的情感無關。」

群體文化從小時候便不知不覺地影響著我們,逼迫每個人就範,必須待在大人畫的框框裡。長大以後,情況有改觀嗎?好像沒差多少。在生活周遭、職場上,壓抑個人言行的事情還是屢見不鮮。好比說,曾聽一位朋友提起,有一次他穿件 polo 衫去上班,衣服上面印有某家公司的標誌,主管見狀,便質問他為什麼要穿這種衣服來上班,好像在幫別家公司宣傳。

對比竹林七賢的嵇康,夏天穿著厚棉衣在柳樹下燒火打鐵,被指為違背社會禮俗,最後壓至刑場砍頭。這樣看來,古今皆然--特立獨行就是不對,跟別人不一樣就是不合群、該打。即使是現代,選擇展現自己的獨立意志、表達自己的立場,還是得面對不少衝突。更何況,穿著印有特定字樣的衣服,根本還稱不上特立獨行或標新立異哩。

生命的意義

偶爾還會想起一件事,就是小時候(讀小學的年紀)站在自家後門口,獨自望著後院的天空,憂慮:「如果我死了怎麼辦?我會去哪裡?我對這個世界發生的事都一無所知了,好恐怖。」我也不知道同年齡的小孩會不會也這樣想,但我到現在都還會有這些想法。阿佳小姐說:「你死了,世界還是繼續運轉啊,根本沒影響,你以為世界繞著你轉嗎?」我知道我不是世界的中心,只是那股莫名的恐懼感偶爾還是會浮上心頭。也許是自己對死亡、生命的意義不了解所產生的恐懼吧。

蔣先生認為,生命的意義或許就是在尋找意義的過程,「當你開始尋找時,那個狀態才是意義。」又說:「不管生命的意義為何,如果強迫把自己的意義加在別人身上,那是非常恐怖的事。我相信,意義一定要自己去尋找。」誠哉斯言!

想到另一位蔣先生--故總統蔣中正先生--說的話:「生命的意義在創造宇宙繼起之生命。」小時候老師並沒有解釋這句話是什麼意思,我們只是把它背熟用來考試而已,原來翻譯成白話,就是繁殖下一代。嗯,這也是一種看法,簡單明瞭,只是用詞太拐彎抹角,偉大不掉。

業餘與專業之間

7/30/2008
看書時的一點聯想,順手抄上來。

在《Object-Oriented Analysis and Design with Applications 3rd Edition》的第六章〈Process〉一開始提到:

業餘的軟體工程師總是在尋找神奇、萬能的方法或工具,冀望它們能把軟體開發工作變得極為簡單。專業的軟體工程師就不同了,他們知道那種東西並不存在。業餘人士經常只想要像煮菜那樣照著食譜一步一步做,專家則知道這種開發方法會設計出呆板的產品,並導致開發人員用一連串的謊言來掩蓋一開始就做了錯誤決策的事實。業餘的軟體工程師往往毫不重視文件,要不然就是太過以文件為中心,只擔心客戶不滿意這些文件,對於文件真正應該呈現的重點反而不那麼在意。專家了解文件的重要性,但是他們絕不會為了寫文件而犧牲掉改進軟體架構的時間。

這是業餘和專業開發人員的差別。

我又想到薩依德在《知識分子論》當中的論述:

....所謂的業餘性就是,不為利益或獎賞所動,只是為了喜愛和不可抹煞的興趣....今天在教育體系中爬得愈高,愈受限於相當狹隘的知識領域。當然,沒有人會反對專業能力,但如果它使人昧於個人直接領域--比方說,早期維多利亞時代的情詩--之外的事情,並為了一套權威和經典的觀念而犧牲一己廣泛的文化時,那種能力就得不償失。....專業化也戕害了興奮感和發現感....陷入專業化就是怠惰,到頭來照別人的吩咐行事。因為聽命於人終究成為你的專長。

以上摘錄的兩段內容都提到了專業和業餘的不同,看起來兩邊的觀點是彼此衝突,不過,由於取自完全不同領域的書籍,直接拿來比較會有點奇怪。

第一段的業餘和專業,講的是對自己吃飯謀生的東西熟不熟,厲不厲害的程度差別。第二段的層次則高一些,談的是忠於自己還是服從他人、是否願意犧牲自由的心靈和廣泛的興趣,以換取特定領域的專業形象(訓練有素的狗?)。

我想,專業的軟體工程師也可以有業餘人士的生活態度和價值觀。而不那麼專業(業餘)的軟體工程師當然也可以透過取得高學歷、考證照等方式,為自己塑造出專業的形象(但能維持多久還是得看後面的努力了)。

我好像喜歡業餘多一些 :)

[.NET Code] 如何在指定的逾時時間內偵測伺服器能否連接

7/06/2008
問題

應用程式使用 .NET WebClient 類別執行偵測新版本以及線上自動更新的功能,可是如果伺服器因為某些原因無法連線(如網路斷線、伺服器關機等),應用程式在檢查是否有更新版時,就得等 20 秒左右才會發生連線失敗。
想要使用 Socket 或 TcpClient 類別事先偵測伺服器的 80 port 能否連線,可是結果還是一樣,因為它們並沒有提供連線逾時的屬性。

解決方法

可使用非同步的方式解決。以下是利用 TcpClient 類別偵測伺服器 80 port 能否連接的工具函式:

/// <summary>

/// 偵測指定的伺服器的特定 port 是否可以連接。

/// </summary>

/// <param name="host">伺服器名稱或 IP 位址。</param>

/// <param name="port">Port 號。</param>

/// <param name="timeOut">連線逾時時間,單位:秒。</param>

/// <returns></returns>

private bool IsServerConnectable(string host, int port, double timeOut)

{

  TcpClient tcp = new TcpClient();

  DateTime t = DateTime.Now;

 

  try

  {

    IAsyncResult ar = tcp.BeginConnect(host, port, null, null);

    while (!ar.IsCompleted)

    {

      if (DateTime.Now > t.AddSeconds(timeOut))

      {

        throw new Exception("Connection timeout!");

      }

      System.Threading.Thread.Sleep(100);

    }

 

    tcp.EndConnect(ar);

    tcp.Close();

    return true;

  }

  catch

  {

    return false;

  }

}


呼叫 IsServerConnectable 時,可將參數 timeOutSeconds 指定為 1,如此一來,函式最多只等待 1 秒便會立即傳回結果。

2008-11-22 增加:偵測某個網頁是否能正常回應

    private void PingWebPage(string url, int timeout)

    {

      HttpWebRequest req = (HttpWebRequest) WebRequest.Create(url);

      req.Method = "GET";

      req.Credentials = CredentialCache.DefaultCredentials;

      req.ContentType = "text/xml";

      req.Timeout = timeout;

      WebResponse rsp = req.GetResponse();

      StreamReader sr = new StreamReader(rsp.GetResponseStream(), Encoding.UTF8);

      try

      {

        sr.ReadLine();

      }

      finally

      {

        sr.Close();

        rsp.Close();       

      }

    }


p.s. 以上程式碼是由 CopySourceAsHtml 產生。選項:Wrap words + Embed styles + Overwrite tab size + Overwrite font list (Fixedsys) + Strip line breaks。
技術提供:Blogger.
回頂端⬆️