2008年5月21日 星期三

.NET實做即時通知功能(P2P)

背景:因工作上使用者提出一個需求,當手術室開始動刀的同時,希望能將訊息自動即時通知護理站之人員,並撥放音效。

Note:此功能只有在XP系統以上方可支援,不支援Win2000以下作業系統。

概念:

P2P概念:本身是伺服器(監聽者),可以傾聽其他電腦對自己的呼叫並作出回應的能力,同時也是用戶端可以對其他電腦作出請求的動作。

前置作業:

1.建立一個table進行IP 之Mapping

IP_Mapping

2.在畫面上佈制WebBrower元件作為顯示接收訊息用

3.利用BackgroundWorker元件建立非同步作業

4.利用Timmer之Tick事件在接收訊後將訊息顯示於WebBrower上

事件撰寫:

Server功能:

1.在Form_load利用BackgroundWorker元件建立非同步執行緒啟動監聽功能,並讓Timmer開始運作

2.在BackgroundWorker 之DoWork內撰寫監聽功能程式

3.在Timer之Tick市件內撰寫接收訊息處理程式

以上即為Server部分之功能,可對其他電腦所發出之請求進行監聽,並在接收訊息後將訊息顯示於WebBrower物件上。

Client功能:

1.在Button之Click事件觸發SendMessage(目的地之IP位置由Mapping Table取得)

其他相關事件:

1.form Close時需將執行緒取消

 

程式碼:

Server部分:

Dim sp As New System.Media.SoundPlayer '撥放通知音效
Dim db As dbAccess
Dim IP_ADDRESS As String

Private Sub FrmMain01_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
        Me.WebBrowser1.Navigate("about:blank")
        If My.Computer.Network.IsAvailable = False Then
            MsgBox("您沒有連上網路,本程式無法執行", MsgBoxStyle.Critical)
            Me.Close()
        End If

        '取得欲傳送訊息之OR護理站IP位置
        Dim sql As String
        Dim dtQry As New DataTable
        sql = "select * from Surgery where SurgeryId='01'"
        dtQry = db.GetData(sql)

        IP_ADDRESS = IIf(IsDBNull(dtQry.Rows(0)("ORIP")), "", dtQry.Rows(0)("ORIP").ToString)
        If IP_ADDRESS = "" Then
            MsgBox("尚未設定對應之OR護理站之IP位置!")
        End If

        '開始傾聽(啟動多執行緒)
        If Not BackgroundWorker.IsBusy Then
            BackgroundWorker.RunWorkerAsync()
        End If

        'Timer開始運作
        Me.Timer1.Enabled = True
End Sub

Public Function Decrypt(ByVal inputString As String, ByVal decryptKey As String) As String
    Dim byKey As Byte() = Nothing
    Dim IV As Byte() = {1, 2, 3, 4, 5, 6, 7, 8}
    Dim inputByteArray(inputString.Length) As Byte
    Try
        byKey = System.Text.Encoding.UTF8.GetBytes(Right(decryptKey.PadRight(8), 8))
        Dim des As DESCryptoServiceProvider = New DESCryptoServiceProvider
        inputByteArray = Convert.FromBase64String(inputString)
        Dim ms As MemoryStream = New MemoryStream
        Dim cs As CryptoStream = New CryptoStream(ms, des.CreateDecryptor(byKey, IV), CryptoStreamMode.Write)
        cs.Write(inputByteArray, 0, inputByteArray.Length)
        cs.FlushFinalBlock()
        Dim encoding As System.Text.Encoding = New System.Text.UTF8Encoding
        Return encoding.GetString(ms.ToArray)
    Catch er As System.Exception
        Throw New Exception(Decrypt = er.Message)
        Return Nothing
    End Try
End Function

'利用BackGroupdWorker元件啟動多執行緒(非同步),進行監聽服務
Private Sub BackgroundWorker_DoWork(ByVal sender As System.Object, ByVal e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker.DoWork
    MainProcess()
End Sub

Sub MainProcess()
    Dim Ports() As String = {"http://*:80/"}
    ListenerProcess(Ports)
End Sub

'啟動監聽服務(listener)
Public Sub ListenerProcess(ByVal prefixes As String())
    Static flag As Boolean = False
    If flag = True Then Exit Sub

    flag = True
    If Not HttpListener.IsSupported Then
        MsgBox("此系統需要 Windows XP 且安裝 SP2 以上版本 或 Server 2003 才可以使用.", MsgBoxStyle.Critical)
        End
    End If
    'prefixes是傳入的參數(port)
    If prefixes Is Nothing OrElse prefixes.Length = 0 Then
        Throw New ArgumentException("連接port不正常")
    End If
    '加入連接port
    Dim listener As HttpListener = New HttpListener
    For Each s As String In prefixes
        listener.Prefixes.Add(s)
    Next
    '開始
    listener.Start()
    Do While True
        Application.DoEvents()
        Dim context As HttpListenerContext = listener.GetContext
        Dim request As HttpListenerRequest = context.Request
        Dim response As HttpListenerResponse = context.Response
        Dim msg As String
        msg = Microsoft.VisualBasic.Right(request.RawUrl, Len(request.RawUrl) - 1)
        '將訊息解密
        msg = Decrypt(msg, "Key")
        '輸出字串
        TalkingMsg += msg & "<font color=gray size=2>(" & request.UserHostAddress & ")</font><br>"
        Dim responseString As String = "Get:" & request.RawUrl & "......"
        Dim buffer As Byte() = System.Text.Encoding.UTF8.GetBytes(responseString)
        response.ContentLength64 = buffer.Length
        Dim output As System.IO.Stream = response.OutputStream
        output.Write(buffer, 0, buffer.Length)
        output.Close()
        Application.DoEvents()
    Loop
    listener.Stop()
End Sub

'利用Timmer 若有接收到訊息,則將訊息顯示在WebBrowser元件,並撥放通知音效
Private Sub Timer1_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Timer1.Tick
    If TalkingMsg <> "" Then
        Me.WebBrowser1.Document.Body.InnerHtml = TalkingMsg & Me.WebBrowser1.Document.Body.InnerHtml
        TalkingMsg = ""
        '撥放通知音效
        PlaySound()
    End If
End Sub

'撥放通知音效
Public Sub PlaySound()
    '設定音效撥放路徑
    sp.SoundLocation = PlayPath
    '取得音效
    sp.LoadAsync()
    '判斷音效是否取得
    If sp.IsLoadCompleted = True Then
        '循環撥放音效
        sp.Play()
        sp.PlayLooping()
    End If
End Sub

Client部分:

Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        SendMsg("啟動下台刀,病患姓名:MARY", IP_ADDRESS) '送出通知訊息
End Sub

    '傳送訊息(Flownm:通知訊息 / IP_ADDRESS:IP位置)
   Public Sub SendMsg(ByVal Flownm As String, ByVal IP_ADDRESS As String)
        Dim URL As String

        '從輸入區取得要傳送給對方的訊息
        Dim msg As String = "通知訊息:啟動流程-" & Flownm

        '初步處理(文字處理)
        msg = Trim(msg)
        If Microsoft.VisualBasic.Left(msg, 2) = vbCrLf Then msg = Microsoft.VisualBasic.Right(msg, Len(msg) - 2)
        If Trim(Replace(msg, vbCrLf, "")) = "" Then Exit Sub

        '在訊息前加入自己的暱稱(加入通知之護理站名稱及IP)
        'msg = "<font color=Blue><b>" & Me.txb_MyName.Text & "</b></font>:" & msg
        '先顯示在自己的畫面上
        'Me.WebBrowser1.Document.Body.InnerHtml = msg & "<br>" + Me.WebBrowser1.Document.Body.InnerHtml

        '將訊息加密
        msg = Encrypt(msg, "Key")
        '透過HttpRquest方式傳送給對方(使用標準80Port)
        URL = "http://" & IP_ADDRESS & ":80/" & msg
        If GetWebPage(URL) = "" Then MsgBox("無法傳遞啟動訊息,請檢查對方IP位置,或是您的連線狀態。", MsgBoxStyle.Critical)
    End Sub

    '取得網頁HTML
    Public Function GetWebPage(ByVal URL As String, Optional ByVal proxy As String = "") As String
        Dim proxyObject As System.Net.WebProxy
        Dim proxyString As String

        On Error GoTo err
        '準備讀取資料
        Dim MyRequest As System.Net.HttpWebRequest = System.Net.WebRequest.Create(URL)

        '讀取registry看看是否需要 proxy
        proxyString = proxy
        If proxyString <> "" Then
            '設定proxy
            proxyObject = New System.Net.WebProxy(proxyString, True)
            MyRequest.Proxy = proxyObject
        End If

        '讀取遠端網頁
        Dim MyWebResponse As System.Net.WebResponse = MyRequest.GetResponse()
        Dim MyStream As System.IO.Stream
        MyStream = MyWebResponse.GetResponseStream

        '使用預設的編碼方式 --> System.Text.Encoding.Default
        Dim StreamReader As New System.IO.StreamReader(MyStream, System.Text.Encoding.Default)
        GetWebPage = StreamReader.ReadToEnd()
err:
        If Err.Number <> 0 Then GetWebPage = Nothing
        MyRequest = Nothing
        MyWebResponse = Nothing
        MyStream = Nothing
        StreamReader = Nothing
        proxyObject = Nothing
    End Function

    Public Function Encrypt(ByVal inputString As String, ByVal encryptKey As String) As String
        Dim byKey As Byte() = Nothing
        Dim IV As Byte() = {1, 2, 3, 4, 5, 6, 7, 8}
        Try
            byKey = System.Text.Encoding.UTF8.GetBytes(Right(encryptKey.PadRight(8), 8))
            Dim des As DESCryptoServiceProvider = New DESCryptoServiceProvider
            Dim inputByteArray As Byte() = System.Text.Encoding.UTF8.GetBytes(inputString)
            Dim ms As MemoryStream = New MemoryStream
            Dim cs As CryptoStream = New CryptoStream(ms, des.CreateEncryptor(byKey, IV), CryptoStreamMode.Write)
            cs.Write(inputByteArray, 0, inputByteArray.Length)
            cs.FlushFinalBlock()
            Return Convert.ToBase64String(ms.ToArray)
        Catch er As System.Exception
            Throw New Exception(er.Message)
            Return Nothing
        End Try
    End Function

其他相關事件:

'form close時記得將執行緒取消
Private Sub FrmMain01_FormClosing(ByVal sender As System.Object, ByVal e As System.Windows.Forms.FormClosingEventArgs) Handles MyBase.FormClosing
    If BackgroundWorker.IsBusy Then
        BackgroundWorker.CancelAsync()
    End If
End Sub

Private Sub Button10_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button10.Click
    StopPlaySound()
End Sub

'停止撥放音效
Public Sub StopPlaySound()
    sp.Stop()
End Sub

參考資源:Visual Basic 2005 程式設計與案例剖析

沒有留言: