最近因想要嘗試 C# 這個程式語言,所以找了一個主題來自我挑戰一下,寫一個給公司內部使用的即時通訊軟體,目標很簡單,只要能雙方對話並且能使用公司帳號登入,登入後會定期的通知最新的待簽的電子簽核文件就好。
在網路上可以找到這個簡單的範本,雖然簡單但該有的都有,可以大幅度的節省很多開發的功夫,內容請自行參考以下網址。http://www.codeproject.com/Articles/429144/Simple-Instant-Messenger-with-SSL-Encryption-in-Cs
該程式分成兩段,一端是 Server 端,是一個純 Console 的軟體,要看了解程式可能需要點 網路通訊 及 Multi-Thread 的觀念,自定義與 Client 通訊的標準,因為我要增加功能,所以這一段我還有加上一些進去;但我不需要可以自動註冊變成使用者,所以關於註冊這段我是全部 Remark 掉。
另一段是 WinForm 的 Client 端,這一段相對來說比較簡單製作,我也沒有做大幅度的變動盡量維持原本的 Layout,原本的簡單地架構足以應付溝通的工作。
原始碼內定義的通訊協定
public const byte IM_OK = 0; // OK public const byte IM_Login = 1; // Login public const byte IM_Register = 2; // Register public const byte IM_TooUsername = 3; // Too long username public const byte IM_TooPassword = 4; // Too long password public const byte IM_Exists = 5; // Already exists public const byte IM_NoExists = 6; // Doesn’t exist public const byte IM_WrongPass = 7; // Wrong password public const byte IM_IsAvailable = 8; // Is user available? public const byte IM_Send = 9; // Send message public const byte IM_Received = 10; // Message received以下修改是在 Server端
修改一
在測試自定義的通訊協定時,一方面是原始碼兩邊都要改,另一方面在 Client端要新增許多新的 Handler 去對應收到這類的封包要如何去處理他,例如新增一個要求現在有哪些使用者上線的這個功能,就要去攔截收到這個封包之後的處理方式。
以下是我自定義的部分
public const byte IM_Ask_jobs = 101; // Request waiting jobs
public const byte IM_Return_jobs = 102; // Return waiting jobs
public const byte IM_Ask_OnlineList = 103; // Ask for ONLineList
public const byte IM_Return_OnlineList = 104; // Return ONLine member list
修改二
在 Server 端增加連結資料庫的查詢,因為我們的電簽是放在 Oracle DB 上面,包括使用者資料都是抓 ERP 內的員工資料,所以這些資料連線要在 Server 端處理好,但 Server 同時要負責接收新的連線,還要處理Client端相互通訊,及檢查最新的尚未簽核單據,都需要 Multi-thread 來處理;但我為了節省 Connection 數量,只要在主程式做一個連線連到資料庫,其他的 Thread 都是透過主程式的 Connection 去跑 Query,這一點在小量的測試環境都還運作正常,上線後有問題再來修改。
修改三
修改原先處理使用者資料庫的程式,原程式把使用者的資訊都存在 Users.dat 這個檔案裏面,但是我是打算抓取ERP 內的資料,所以在UserInfo class增加了一些欄位,在 Server 啟動時也一口氣地把所有的 User 都讀到記憶體內,減少每次使用者登入都要去讀取資料庫的困擾,但麻煩的是一但有使用者更新資料,就必須額外寫一段程式每60分鐘去把原先存在記憶體的資訊重新更新一次,因為我在記憶體中把各使用者的連線上來的 Connection 參數都存下來,這樣我才能知道要把訊息傳給誰,但也產生無法直接把記憶體內容直接清掉重來的麻煩。
UserInfo.cs 內調整過的使用者基本資料
public string UserName; // User Name
public string Password; // User Password
public string ADAccount; // OSUser (AD account)
public string CompanyCode; // Code of Company
public string EmpCode; // Code of Emmploye
public string DepartmentName; // name of department
//[NonSerialized] public bool LoggedIn; // Is logged in and connected?
//[NonSerialized] public Client Connection; // Connection info
public bool LoggedIn; // Is logged in and connected?
public Client Connection; // Connection info
修改四
新增一個檢查最後 5 分鐘內所需要自己簽核(包含代理別人的工作)的單據,這個就是一個全新的 Thread,但會跟主程式分享在記憶體中的使用者清單,透過資料庫查詢功能,把最新的單據清單撈近來,檢查是否有代理其他同仁的工作,然後比對已經上線人員的清單,發現對方已上線就使用 Connection 所記錄發訊息通知對方;但這邊有點 Tricky,因為所有的通訊協定都是一個封包就結束,而待辦事項可能會有多筆,所以我這邊也加了一個判斷如果沒有更多的訊息後,就發一個結束的資訊給 Client端,這樣就可以一口氣呈現有多筆待辦事項。
其實這個 Thread 是每五分鐘執行一次,由主程式呼叫之後會先休息五分鐘,之後去資料庫抓資料,同時會有個變數來計算如果自己被執行 12 次(一個小時),就會順便去資料庫撈取最新的員工資料,把[修改三]的自動更新部分並到這邊來處理。
修改五
另外是處理上線使用者清單,由於我是屬於老派的開發者,所以我喜歡把東西獨到記憶體後來處理,會比每次需要到資料庫去抓得快多了,一旦收到 Client 端發出更新清單需求,我就會去記憶體中統計有哪些人上線,然後把上線人員的資訊做成一個大字串傳給 Client,其中我用逗號(,)來區隔使用者不同的資訊,利用分號(;)來分隔不同的使用者,等到 Client 收到後再自行解開做字串分割存到不同的陣列欄位去。
以下是 Client 端的修正
修改六
首先修正如果收到其他同事送來的訊息,其實Client 端是不知道的,所以在我收到這類的訊息時,會先去檢查跟發信者是否已經開始交談視窗(檢查一個陣列aMsg),如果沒有就開一個起來,另外我也修改了TalkForm的建構,讓開啟交談視窗就會直接顯示對方送來的訊息。
void im_MessageReceived(object sender, IMReceivedEventArgs e)
{
this.BeginInvoke(new MethodInvoker(delegate
{
bool bOpened = false;
string[] sList = e.From.Split(‘,’);
textBoxSystemNote.Text = String.Format(“{0} says {1}”, e.From, e.Message);
for (int iCnt=1; iCnt < iMsg; iCnt ++)
{
if (aMsg[iCnt] == sList[0])
{
bOpened = true;
}
}
if (!bOpened)
{
for (int iCnt = 1; iCnt < iMsg; iCnt++)
{
if (aMsg[iCnt] == “”)
{
aMsg[iCnt] = sList[0];
iCnt = iMsg;
}
}
TalkForm tf = new TalkForm(im, e.From, e.Message.ToString(), aMsg, iMsg);
talks.Add(tf);
tf.Show();
}
}));
}
修改七
上線用戶清單,補強原先功能,讓顯示已經登入的使用者在Client端的頁面上,配合Server端的修改五功能,把列表顯示在ListBox 上,使用者直接雙擊該使用者就跳出對話方塊 (TalkForm),但要留意如果用戶清單變更後,可能這個ListBox 要一併跟著更新喔。
修改八
新增加一個待辦事項的 Form,處理收到最新待報事項後自動顯示著通知面板,上面條列出最新的待簽單據,並設定如果沒有任何動作的話10秒內自動關閉,以免打擾到正在工作的用戶,但設定直接點選該項目後會自動開啟網頁連結讓使用者可直接簽核。
面板實作範例如下:
修改九
最後就是一些小細節的修正,例如最小化後會縮到工具列去、聊天時按 Enter 就送出、變更 Icon、Publish 設定自動檢查新版等等…
因程式碼內部有許多公司資訊,所以不太方便直接公布,歡迎有興趣的人可以一起討論,其實還有用戶通訊內容的紀錄、Server Log File 等細節還需要再花點時間修飾,不過到目前為止已經可以上線測試,可以維持穩穩地12 小時不當正常使用喔。
感謝原作者 Teapot418 的原始碼 (http://www.codeproject.com/Members/Teapot418), Very appreciate your work.