本人一直以來都是一個實踐主義者,認為在實踐中學習,是最好的學習方式。所以在部落格中添加文章的時候,總是習慣寫一些具體實現的技術知識點。感覺這些東西可以更好的協助那些學習使用.net c#進行開發開發人員。雖然自己的文筆不好,知識的應用也很漸顯,但是本著互相學習的精神,希望在總結的同時可以加深自己的理解,考效自己對各個知識的瞭解程度,共同提高。
言歸正傳,我今天要說的就是一個windows 服務結合 Soctket、郵件發送、簡訊發送的小應用。功能不大,但是用到的.net下的知識點很多,我感覺對初步接觸這幾個相關知識的朋友還是有學習意義的。
這個功能來源於一個監看資料夾的需求:
1、 輪詢監看資料夾內檔案的狀態
2、 在發現異常後通過郵件和簡訊的方式通知管理員。
3、 在有外網環境下使用郵件方式通知。
4、 無外網情況下,通過區域網路中一台帶有的簡訊裝置電腦傳送簡訊提醒。
在經過具體的功能分析後,將功能劃分為4個小功能單元:
1、 運用c#開發windows服務來實現對檔案夾的輪詢監視。
2、 引用.net架構內的system.net.mail 命名控制項的下的郵件處理類來實現郵件的發送。
3、 區域網路內採用scoket解決區域網路內簡訊裝置電腦與監控電腦之間的通訊問題。
4、 C#對簡訊裝置進行簡單必要的二次開發。
一、windows服務實現輪詢監視
Windows 服務,以前的NT服務,都是被作為Windows NT作業系統的一部分引進來的。你需要使用NT層級的作業系統才可運行Windows服務,諸如:Windows NT、Windows 2000 Professional、windows XP或Windows 2000 Server以上作業系統。舉例而言,以Windows服務形式的產品有:Microsoft Exchange、SQL Server,還有別的如設定電腦時鐘的Windows Time服務。它隨 Windows 作業系統啟動而啟動的,在後台啟動並執行,通常不和使用者產生互動的程式。
在.net架構下建立Windows 服務非常的簡單快捷,它封裝了Windows服務程式的建立和控制過程,程式相關的命名空間涉及到以下兩個:System.ServiceProcess和System.Diagnostics。
首先是建立windows服務,在.net架構中直接建立windows服務項目即可,在服務啟動事件中添加必要的邏輯處理。〔實現見下代碼〕
protected override void OnStart(string[] args)
{
// TODO: 在此處添加代碼以啟動服務。
//讀取配置資訊
ReadConfig();
timer1.Elapsed+=new System.Timers.ElapsedEventHandler(timer1_Elapsed);
timer1.Interval = GlobalInfo.TimeInterval;
timer1.Start();
}
void timer1_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
DirectoryInfo dirs = new DirectoryInfo(GlobalInfo.DirName);
int count=0;
switch (GlobalInfo.Type)
{
case 0://延遲時間
foreach (FileInfo var in dirs.GetFiles())
{
DateTime createTime = var.CreationTime;
if (System.DateTime.Now > createTime.AddHours(GlobalInfo.Size))
count++;
}
if (count > 0)
MessageSender.Send(GlobalInfo.Norm > 1 ? false : true, string.Format("{0}個資料檔案延遲超過{1}小時,請關注。[報告時間:{2}]", count.ToString(), GlobalInfo.Size.ToString(), System.DateTime.Now.ToString()));
break;
case 1://延時資料包個數
count = dirs.GetFiles().Length;
if (count > GlobalInfo.Count)
MessageSender.Send(GlobalInfo.Norm > 1 ? false : true, string.Format("同時有{0}個資料檔案滯留,請關注。[報告時間:{1}]", count.ToString(), System.DateTime.Now.ToString()));
break;
default:
break;
}
}
二、郵件發送功能
在以前的一篇文章中提到過郵件類的使用,所以就不在作詳細說明了。〔附代碼:見下〕
static void Send(string titel,string megHtml,string subject) { string meg = ""; //寄件者 string smtpAuthUsername = GlobalInfo.SenderAddr; //寄件者密碼 string smtpAuthPassword = GlobalInfo.SenderPwd; //發送伺服器 string smtpServer = GlobalInfo.SmtpServer; string objEmail = GlobalInfo.ObjAddr; //定義傳輸協議 System.Net.Mail.SmtpClient smtp = new System.Net.Mail.SmtpClient(smtpServer); //設定認證寄件者 smtp.Credentials = new System.Net.NetworkCredential(smtpAuthUsername, smtpAuthPassword); //非同步發送完成擷取發送狀態 smtp.SendCompleted += new System.Net.Mail.SendCompletedEventHandler(SendCompletedCallback); try { System.Net.Mail.MailMessage mail = new System.Net.Mail.MailMessage(); mail.From = new System.Net.Mail.MailAddress(smtpAuthUsername, smtpAuthUsername); //回複人,回複人名 mail.ReplyTo = new System.Net.Mail.MailAddress(smtpAuthUsername, smtpAuthUsername); //收件者 mail.To.Add(objEmail); //郵件優先順序 mail.Priority = System.Net.Mail.MailPriority.Normal; //設定html郵件 mail.IsBodyHtml = true; //標題 mail.Subject = titel; //內容 mail.Body = megHtml; smtp.Send(mail); meg = string.Format("{0} {1}郵件發送成功。", System.DateTime.Now.ToString(), objEmail); WriteLog(meg); } catch { meg = string.Format("{0} {1}郵件發送失敗。", System.DateTime.Now.ToString(), objEmail); WriteLog(meg); } }
三、區域網路內Socket通訊
Socket原意是“插座”。應用程式層通過傳輸層進行資料通訊時,TCP和UDP會遇到同時為多個應用程式進程提供並發服務的問題。多個TCP串連或多個應用程式進程可能需要通過同一個TCP協議連接埠傳輸資料。為了區別不同的應用程式進程和串連,許多電腦作業系統為應用程式與TCP/IP協議互動提供了稱為通訊端(Socket)的介面,區分不同應用程式進程間的網路通訊和串連。 產生通訊端,主要有3個參數:通訊的目的IP地址、使用的傳輸層協議(TCP或UDP)和使用的連接埠號碼。通過將這3個參數結合起來,與一個“插座”Socket綁定,應用程式層就可以和傳輸層通過通訊端介面,區分來自不同應用程式進程或網路連接的通訊,實現資料轉送的並發服務。 Socket可以看成在兩個程式進行通訊串連中的一個端點,一個程式將一段資訊寫入Socket中,該Socket將這段資訊發送給另外一個Socket中,使這段資訊能傳送到其他程式中。
因為現有的簡訊裝置位與區域網路內的另外一台伺服器上,所以需要在簡訊通知狀態下,需要將資訊發送到區域網路內另一台電腦。用socket通訊的話需要一個接收資訊的用戶端。並且用戶端需要一些參數的設定。比如:接收電話號碼、開機啟動、通訊電腦IP、等。〔資訊接收代碼見下〕
Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, 1); try { socket.Bind(new IPEndPoint(IPAddress.Parse(Info.IP), int.Parse(Info.PORT))); socket.Listen((int)SocketOptionName.MaxConnections); while (true) { Socket a = socket.Accept(); if (a.Connected) { byte[] stream = new byte[80]; a.Receive(stream); string message = System.Text.Encoding.UTF8.GetString(stream); InsertRechText ins = new InsertRechText(Insert); Invoke(ins, new object[] { message }); } if (isover) return; } } catch (Exception ex) { WriteLog(string.Format("接收資訊失敗。[{0}]", ex.Message)); throw ex; } finally { socket.Close(); }
四、簡訊裝置二次開發
簡訊裝置用的是人大金倉的DG-C1A 簡訊貓。該硬體有對應的二次開發類庫,開發很簡單。直接將引用方法封裝成一個類的靜態方法。用的時候直接調用就可以了。(直接貼出代碼)
[DllImport("GSMMultiPort.dll", EntryPoint = "GSMModemInit", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)] public static extern bool GSMModemInit( string device, string baudrate, string initstring, string charset, bool swHandshake, string sn);//傳送簡訊息 [DllImport("GSMMultiPort.dll", EntryPoint = "GSMModemSMSsend", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)] public static extern bool GSMModemSMSsend( string device, string serviceCenterAddress, int encodeval, string text, int textlen, string phonenumber, bool requestStatusReport); //取得錯誤資訊 [DllImport("GSMMultiPort.dll", EntryPoint = "GSMModemGetErrorMsg", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)] public static extern string GSMModemGetErrorMsg(string device);
在完成功能後,部署,運行效果不錯。感覺這個功能雖然很小。但是用到的知識可不少,感覺還是有所收穫的。畢竟術業有專攻,每個人在從事的行業開發中專註的技術也是有限的。多學點各個方面的知識對自己的成長還是很有好處的。哈哈.〔囉嗦了〕。共同學習吧。