忍者ブログ

VB.NET-TIPS などプログラミングについて

VB.NETのTIPS(小技集)を中心に、Javascript その他のプログラミングについて少し役に立つ情報を発信します。いわゆる個人的な忘備録ですが、みなさんのお役に立てれば幸いです。

名前付きパイプを使ったプロセス間通信についてその3(クライアントとの双方向通信)

名前付きパイプを使った複数クライアントプログラムとの通信の説明は以下の記事で行いましたが、 今回はこれにクライアントとの双方向通信が出来る様に変更してみました。
名前付きパイプを使ったプロセス間通信についてその2(複数クライアントとの通信)

双方向と言っても、クライアント側からサーバー側に送信されてきた文字列に、応答の為に少しの文字列を付加してクライアント側に送信する処理を考えます。
サーバー側の変更個所は以下の通りです。

  • 通信スレッドの中でストリーム読込を生成を行った後で、ストリーム書込を生成します。
  • 処理ループの中でストリーム読込を行った後で、受信した文字列に「...OK」の文字列を付加してストリーム書込を行います。

サーバー側プログラム

Imports System.Threading
Imports System.IO
Imports System.IO.Pipes

Public Class frmNameServer

    '別スレッドからメッセージを処理するためデリゲートを利用
    Delegate Sub SetRichTextBox1Delegate(ByVal Value As String)
    Private RichTextBox1Delegate As New SetRichTextBox1Delegate(AddressOf _
                                                                AppendTextRichTextBox1)
    'リッチテキストボックスにメッセージを表示する
    Private Sub AppendTextRichTextBox1(ByVal message As String)
        RichTextBox1.AppendText(message)
        RichTextBox1.SelectionStart = RichTextBox1.TextLength
    End Sub

    '別スレッドからボタンの許可設定処理するためデリゲートを利用
    Delegate Sub SetButton1EnabledDelegate(ByVal bln As Boolean)
    Private SetButton1Delegate As New SetButton1EnabledDelegate(AddressOf _
                                                                SetButton1Enabled)
    '[Buttin1]のEnabled設定
    Private Sub SetButton1Enabled(ByVal blnEnabled As Boolean)
        Me.Button1.Enabled = blnEnabled
    End Sub

    'サーバスレッドの数
    Private Const numThreads As Integer = 3
    'サーバスレッド配列
    Private arrServer(numThreads - 1) As Thread
    'サーバスレッド終了フラグ件数
    Private intServerFinish As Integer = 0

    '[Start NamePipe]ボタンクリックイベント
    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) _
                                                     Handles Button1.Click
        intServerFinish = 0
        '名前付きパイプによる通信スレッド開始
        For i = 0 To UBound(arrServer)
            arrServer(i) = New Thread(AddressOf ServerThread)
            arrServer(i).Start()
        Next
        AppendTextRichTextBox1("名前付きパイプ通信待機中・・・" & vbNewLine)

        '[Start NamePipe]ボタン不可設定
        Me.Button1.Enabled = False
    End Sub

    '名前付きパイプによる通信スレッド
    Private Sub ServerThread()
        'サーバー用のパイプを双方向でクライアント数は指定値で生成
        Dim pipeServer As New NamedPipeServerStream("PIPE_TEST", PipeDirection.InOut, numThreads)
        Try
            'パイプスレッド取得
            Dim threadId As Integer = Thread.CurrentThread.ManagedThreadId
            Me.Invoke(RichTextBox1Delegate, _
                      New Object() {"パイプ生成完了(スレッドID:" & threadId & ")" & vbNewLine})

            'パイプの接続を待つ
            pipeServer.WaitForConnection()
            Me.Invoke(RichTextBox1Delegate, _
                      New Object() {"パイプ接続完了(スレッドID:" & threadId & ")" & vbNewLine})

            'ストリーム読込を生成
            Dim pipeStreamReader As New StreamReader(pipeServer)
            'ストリーム書込を生成
            Dim pipeStreamWriter As New StreamWriter(pipeServer)
            pipeStreamWriter.AutoFlush = True
            Try
                While True
                    'パイプからの文字列受信(入力)
                    Dim strRead As String = pipeStreamReader.ReadLine()
                    'ストリームの末尾に到達した場合はNullなので、通信を終了する
                    If strRead = "" Then
                        Exit While
                    End If
                    '受信文字列の表示
                    Me.Invoke(RichTextBox1Delegate, _
                              New Object() {"受信文字列(スレッドID:" & threadId & "):" & strRead & vbNewLine})

                    '受信文字列に「...OK」を追加して返す
                    Dim strTX As String = strRead & "...OK"
                    pipeStreamWriter.WriteLine(strTX)
                    Me.Invoke(RichTextBox1Delegate, _
                              New Object() {"受信文字列(スレッドID:" & threadId & "):" & strTX & vbNewLine})
                End While

            Catch ex As IOException
                MsgBox(ex.Message)
            Finally
                pipeStreamReader.Close()    '基になるストリームも閉じられるので
                'pipeStreamWriter.Close()   'この close はエラーになるので実行しない!!
            End Try

            Me.Invoke(RichTextBox1Delegate, New Object() {"パイプ通信の終了" & vbNewLine})

            intServerFinish += 1
            If intServerFinish >= numThreads Then
                '[Button1]の許可設定
                Me.Invoke(SetButton1Delegate, New Object() {True})
            End If

        Catch ex As Exception
            MsgBox(ex.Message)
        Finally
            pipeServer.Close()
        End Try
    End Sub

End Class


クライアント側の変更個所は以下の通りです。

  • パイプ用ストリーム読込オブジェクトを静的変数で宣言し、[Start Named Pipe]ボタンクリック時イベントで生成します。
  • [Send Message]ボタンクリック時イベントでサーバーへ文字列を送信後、空文字でなければサーバーからの応答を受信し表示します。 尚、送信文字列が空文字の場合は、パイプ用ストリーム読込オブジェクトをCloseします。
  • フォーム上に受信文字列を表示するTEXTBOXを追加します。

クライアント側プログラム

Imports System.IO.Pipes
Imports System.IO
Imports System.Security.Principal

Public Class frmNameClient

    '名前付きパイプ・クライアント
    Private pipeStream As NamedPipeClientStream = Nothing
    'パイプ用ストリーム書込
    Private pipeSw As StreamWriter = Nothing
    'パイプ用ストリーム読込
    Private pipeSr As StreamReader = Nothing

    'フォームロード時イベント
    Private Sub frmNameClient_Load(sender As Object, e As EventArgs) Handles Me.Load
        '[Start Named Pipe]ボタンを許可設定
        Me.Button1.Enabled = True
        '[Send Message]ボタンを不可設定
        Me.Button2.Enabled = False
    End Sub

    '[Start Named Pipe]ボタンクリック時イベント
    Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
        Try
            '名前付きパイプ・クライアント生成
            pipeStream = New NamedPipeClientStream(".", "PIPE_TEST", _
                                                   PipeDirection.InOut, PipeOptions.None, _
                                                   TokenImpersonationLevel.Impersonation)
            '名前付きパイプ接続
            pipeStream.Connect(1000)
            'ストリーム書込生成
            pipeSw = New StreamWriter(pipeStream)
            pipeSw.AutoFlush = True
            'ストリーム読込生成
            pipeSr = New StreamReader(pipeStream)

            '[Start Named Pipe]ボタンを不可設定
            Me.Button1.Enabled = False
            '[Send Message]ボタンを許可設定
            Me.Button2.Enabled = True

        Catch ex As Exception
            MsgBox(ex.Message)
        End Try
    End Sub

    '[Send Message]ボタンクリック時イベント
    Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
        Try
            '送信文字列取得
            Dim strTX As String = Me.TextBox1.Text.Trim
            Me.TextBox1.Text = ""
            '送信
            pipeSw.WriteLine(strTX)
            If strTX = "" Then
                'ストリーム書込を閉じる
                pipeSw.Close()
                'ストリーム読込を閉じる
                pipeSr.Close()
                '名前付きパイプを閉じる
                pipeStream.Close()

                '[Start Named Pipe]ボタンを許可設定
                Me.Button1.Enabled = True
                '[Send Message]ボタンを不可設定
                Me.Button2.Enabled = False
            Else
                '送信後、サーバーからの応答を受信
                Dim strRead As String = pipeSr.ReadLine
                '受信文字列を表示
                Me.TextBox2.Text = strRead
            End If

        Catch ex As Exception
            MsgBox(ex.Message)
        End Try
    End Sub

    'フォームクローズ時イベント
    Private Sub frmNameClient_FormClosed(sender As Object, e As FormClosedEventArgs) Handles Me.FormClosed
        'ストリーム書込を閉じる
        If Not pipeSw Is Nothing Then
            pipeSw.Close()
        End If
        'ストリーム読込を閉じる
        If Not pipeSr Is Nothing Then
            pipeSr.Close()
        End If
        '名前付きパイプを閉じる
        If Not pipeStream Is Nothing Then
            pipeStream.Close()
        End If
    End Sub

End Class

サーバー側のEXEを実行した後、クライアントEXEを3個実行します。 その後、サーバー側の「Start NamePipe」ボタンを押下して、各クライアントの「Start Name Pipe」ボタンを順次押下します。 各クライアント側で送信文字列を入力して「Send Message」ボタンを押下した結果の画像です。 送信した文字列に「...OK」の文字列が追加されて送信されてくる様子が分かります。


尚、以下は各クライアント側で送信文字列をクリアして「Send Message」ボタンを押下して一連の処理を終了した結果の画像です。

関連する記事

名前付きパイプを使ったプロセス間通信について
名前付きパイプを使ったプロセス間通信についてその2(複数クライアントとの通信)
Remoting の IPC を使ったプロセス間通信について
Remoting の IPC を使ったプロセス間通信についてその2(HTTPチャネル)
PR

コメント

コメントを書く