2022年1月アーカイブ

前回、ソケット通信ならSocketクラスを用いて作るのが普遍的。の様な書き方をしたと思いますが、

比較的単純なアプリケーションを作成していて、最大のパフォーマンスを必要としない場合は、TcpClient、TcpListener、およびUdpClientを使用することを検討してください。これらのクラスは、Socket通信のためのより簡単でわかりやすいインターフェイスを提供します。

なのですから、パフォーマンスなんて要らない、非常に複雑なんだから(ホンマかいな?)。という事で。


<注意点>

・動作は同期ブロッキングモードです。

・エラー処理は手抜きです。


お試し環境
  Windows7 64bit Edition
  Visual Basic 2008 AnyCPU対象


/*---- サーバー側 -------------------- お試し結果 --------------------------*/

Waiting for a connection...
Connected!
Received: This is it!<EOF>
Sent: THIS IS IT!<EOF>
Waiting for a connection...

/*----------------------------------------------------------------------------*/

/*---- クライアント側 ---------------- お試し結果 --------------------------*/

Sent: This is it!<EOF>
Received: THIS IS IT!<EOF>

/*----------------------------------------------------------------------------*/


/*---- サーバー側 ------------------  お試しソース -------------------------*/

'Imports System.Net
'Imports System.Net.Sockets
'Imports System.Text

Public Class Form1

    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click

        Dim server As Net.Sockets.TcpListener ' リスナー側ソケット
        Dim client As Net.Sockets.TcpClient ' クライアント側ソケット
        Dim port As Integer
        Dim i As Integer

        ' バイト列送受信バッファ
        Dim sndBytes(1024) As Byte
        Dim rcvBytes(1024) As Byte
        Dim msg As String

        Dim localAddr As Net.IPAddress ' ローカルIPアドレス
        Dim stream As Net.Sockets.NetworkStream ' クライアント側との送受信ストリーム

        server = Nothing
        Try
            ' リスナーソケット作成
            port = 22000
            localAddr = Net.IPAddress.Parse("127.0.0.1")
            server = New Net.Sockets.TcpListener(localAddr, port)

            ' クライアントからの受信接続要求リッスン開始
            server.Start()

            ' 接続要求受け取り開始
            While True
                Debug.Print("Waiting for a connection... ")

                ' 接続要求許可待ちの間、プログラムは一時停止
                client = server.AcceptTcpClient()
                Debug.Print("Connected!")

                ' クライアントとの送受信ストリーム取得
                stream = client.GetStream()

                ' データを全て受信するまでループ
                i = stream.Read(rcvBytes, 0, rcvBytes.Length)
                While (i <> 0)
                    ' 受信バイト列を文字列に変換
                    msg = System.Text.Encoding.ASCII.GetString(rcvBytes, 0, i)
                    Debug.Print("Received: {0}", msg)

                    ' 受信データ処理
                    msg = msg.ToUpper()
                    sndBytes = System.Text.Encoding.ASCII.GetBytes(msg)

                    ' 応答送信
                    stream.Write(sndBytes, 0, sndBytes.Length)
                    Debug.Print("Sent: {0}", msg)

                    i = stream.Read(rcvBytes, 0, rcvBytes.Length)
                End While

                ' クライアント側ソケット解放
                stream.Close()
                client.Close()
            End While

        Catch ex As Net.Sockets.SocketException
            Debug.Print("SocketException : {0}", ex)
        Finally
            server.Stop()
        End Try

    End Sub

End Class

/*----------------------------------------------------------------------------*/

/*---- クライアント側 --------------  お試しソース -------------------------*/

'Imports System.Net
'Imports System.Net.Sockets
'Imports System.Text

Public Class Form1

    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click

        Dim client As Net.Sockets.TcpClient ' クライアントソケット
        Dim port As Integer
        Dim stream As Net.Sockets.NetworkStream ' 送受信ストリーム

        Dim sndData(1024) As Byte ' バイト列送信バッファ
        Dim rcvData(1024) As Byte ' バイト列受信バッファ
        Dim rcvLen As Integer
        Dim msg As String

        Dim server As String = "127.0.0.1"
        Dim message As String = "This is it!<EOF>"

        Try
            ' クライアントソケット作成
            port = 22000
            client = New Net.Sockets.TcpClient(server, port)

            ' 送信文字列をバイト配列に変換
            sndData = System.Text.Encoding.ASCII.GetBytes(message)

            ' 送受信ストリームを取得
            stream = client.GetStream()

            ' サーバーにメッセージ送信
            stream.Write(sndData, 0, sndData.Length)
            Debug.Print("Sent: {0}", message)

            ' 応答データ受信
            rcvLen = stream.Read(rcvData, 0, rcvData.Length)
            msg = System.Text.Encoding.ASCII.GetString(rcvData, 0, rcvLen)
            Debug.Print("Received: {0}", msg)

            ' ソケット解放
            stream.Close()
            client.Close()

        Catch ex As ArgumentNullException
            Debug.Print("ArgumentNullException: {0}", ex)
        Catch ex As Net.Sockets.SocketException
            Debug.Print("SocketException: {0}", ex)
        End Try

    End Sub

End Class

/*----------------------------------------------------------------------------*/

/*----------------------------------------------------------------------------*/
/*============================================================================*/

TCP/IPのソケット通信と言ったら、C言語だろうとVisualBasicだろうとソケットを作成するのが基本。と言うか一般的ですよね。

マイクロソフトのドキュメントによれば、

If you are writing a relatively simple application and do not require maximum performance, consider using TcpClient, TcpListener, and UdpClient. These classes provide a simpler and more user-friendly interface to Socket communications.

だそうですから、VBでも尚更、Socketクラスを使わない訳には行きません(ホンマかいな?)。

<注意点>

・動作は同期ブロッキングモードです。

・Listenメソッドの引数は接続要求を保留する上限値を指定します。

・エラー処理は手抜きです。


お試し環境
  Windows7 64bit Edition
  Visual Basic 2008 AnyCPU対象


/*---- サーバー側 -------------------- お試し結果 --------------------------*/

Waiting for a connection...
Text received : This is a test<EOF>
Waiting for a connection...

/*----------------------------------------------------------------------------*/

/*---- クライアント側 ---------------- お試し結果 --------------------------*/

Socket connected to ::1:22000
Echoed test = This is a test<EOF>

/*----------------------------------------------------------------------------*/


/*---- サーバー側 ------------------  お試しソース -------------------------*/

'Imports System.Net
'Imports System.Net.Sockets
'Imports System.Text

Public Class Form1

    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click

        Dim srvSkt As Net.Sockets.Socket ' サーバー側ソケット
        Dim datSkt As Net.Sockets.Socket ' クライアント側との送受信ソケット
        Dim port As Integer
        Dim rcvLen As Integer
        Dim sndBytes(1024) As Byte ' バイト列の送信バッファ
        Dim rcvBytes(1024) As Byte ' バイト列の受信バッファ
        Dim msg As String

        Dim ipHostInfo As Net.IPHostEntry
        Dim ipAddress As Net.IPAddress ' ローカルIPアドレス
        Dim localEP As Net.IPEndPoint

        port = 22000
        ipHostInfo = Net.Dns.GetHostEntry("localhost")
        ipAddress = ipHostInfo.AddressList(0)
        localEP = New Net.IPEndPoint(ipAddress, port)

        ' ソケット作成
        srvSkt = New Net.Sockets.Socket(ipAddress.AddressFamily, Net.Sockets.SocketType.Stream, Net.Sockets.ProtocolType.Tcp)

        Try
            ' ソケットをローカルエンドポイントに関連付け
            srvSkt.Bind(localEP)
            ' クライアントからの接続要求をリッスン
            srvSkt.Listen(1)

            ' 接続要求受け取り開始
            While True
                Debug.Print("Waiting for a connection...")
                ' 接続要求許可待ちの間、プログラムは一時停止されます
                datSkt = srvSkt.Accept()
                msg = Nothing

                ' クライアントからデータ受信
                While True
                    rcvLen = datSkt.Receive(rcvBytes)
                    msg += System.Text.Encoding.ASCII.GetString(rcvBytes, 0, rcvLen)
                    If msg.IndexOf("<EOF>") > -1 Then
                        Exit While
                    End If
                End While
                ' 受信データを表示
                Debug.Print("Text received : {0}", msg)

                ' クライアントにデータをエコーバック
                sndBytes = System.Text.Encoding.ASCII.GetBytes(msg)
                datSkt.Send(sndBytes)

                ' ソケット解放
                datSkt.Shutdown(Net.Sockets.SocketShutdown.Both)
                datSkt.Close()
            End While

        Catch ex As ArgumentException
            Debug.Print("ArgumentException : {0}", ex.ToString())
        Catch ex As Net.Sockets.SocketException
            Debug.Print("SocketException : {0}", ex.ToString())
        Catch ex As ObjectDisposedException
            Debug.Print("ObjectDisposedException : {0}", ex.ToString())
        Catch ex As Security.SecurityException
            Debug.Print("SecurityException : {0}", ex.ToString())
        Catch ex As InvalidOperationException
            Debug.Print("InvalidOperationException : {0}", ex.ToString())
        Catch ex As Exception
            Debug.Print("Unexpected exception : {0}", ex.ToString())
        End Try

    End Sub

End Class

/*----------------------------------------------------------------------------*/

/*---- クライアント側 --------------  お試しソース -------------------------*/

'Imports System.Net
'Imports System.Net.Sockets
'Imports System.Text

Public Class Form1

    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click

        Dim clntSkt As Net.Sockets.Socket ' クライアント側ソケット
        Dim port As Integer
        Dim sndLen As Integer
        Dim rcvLen As Integer
        Dim sndBytes(1024) As Byte ' バイト列の送信バッファ
        Dim rcvBytes(1024) As Byte ' バイト列の受信バッファ

        Dim ipHostInfo As Net.IPHostEntry
        Dim ipAddress As Net.IPAddress ' リモートIPアドレス
        Dim remoteEP As Net.IPEndPoint

        port = 22000
        ipHostInfo = Net.Dns.GetHostEntry("localhost")
        ipAddress = ipHostInfo.AddressList(0)
        remoteEP = New Net.IPEndPoint(ipAddress, port)

        ' ソケット作成
        clntSkt = New Net.Sockets.Socket(ipAddress.AddressFamily, Net.Sockets.SocketType.Stream, Net.Sockets.ProtocolType.Tcp)

        Try
            ' ソケットをリモートエンドポイントに接続
            clntSkt.Connect(remoteEP)
            Debug.Print("Socket connected to {0}", clntSkt.RemoteEndPoint.ToString())

            ' 送信文字列をバイト配列に変換
            sndBytes = System.Text.Encoding.ASCII.GetBytes("This is a test<EOF>")

            ' データ送信
            sndLen = clntSkt.Send(sndBytes)

            ' リモートサーバーから応答受信
            rcvLen = clntSkt.Receive(rcvBytes)
            Debug.Print("Echoed test = {0}", System.Text.Encoding.ASCII.GetString(rcvBytes, 0, rcvLen))

            ' ソケット解放
            clntSkt.Shutdown(Net.Sockets.SocketShutdown.Both)
            clntSkt.Close()

        Catch ex As ArgumentException
            Debug.Print("ArgumentException : {0}", ex.ToString())
        Catch ex As Net.Sockets.SocketException
            Debug.Print("SocketException : {0}", ex.ToString())
        Catch ex As ObjectDisposedException
            Debug.Print("ObjectDisposedException : {0}", ex.ToString())
        Catch ex As Security.SecurityException
            Debug.Print("SecurityException : {0}", ex.ToString())
        Catch ex As InvalidOperationException
            Debug.Print("InvalidOperationException : {0}", ex.ToString())
        Catch ex As Exception
            Debug.Print("Unexpected exception : {0}", ex.ToString())
        End Try

    End Sub

End Class

/*----------------------------------------------------------------------------*/

/*----------------------------------------------------------------------------*/
/*============================================================================*/

以前、C言語でTCP/IPのソケット通信の記事「TCP/IPプログラムのいろはは、DOS窓でWinSockAPI」を書きましたので、VisualBasicでも使ってみようかと。だけどその前に、VBにとって重要そうな(私だけ?)エンドポイントについて。

VBでは、ネットワークアドレスとサービスポートの組み合わせを「エンドポイント」と呼びます。

ネットワークアドレスは、ネットワーク上の特定のデバイス(例えば、PCなど)を識別します。ポート番号は、そのデバイス上の特定のサービスの接続先(例えば、メール)を識別します。

TCP/IPは、ネットワークアドレスとサービスポート番号を使用して、一意にサービスを識別し、通信しています。

インターネット検索すると、エンドポイント(EndPoint)の決め方が色々ある様ですので、目に付いたものの内容を確認してみる事に。


<注意点>

・EndPoint確認のネットワーク環境
 (1)無し、自PC内動作 ⇒ 外部接続無し、単独動作
 (2)LAN、LAN内動作 ⇒ 外部接続無し、DHCPが192.168.x.yを配布
 (3)インターネット、外部動作 ⇒ 外部接続有(デュアルスタック)

・どんな相手でも受け入れてしまうAnyアドレス(0.0.0.0)は、利用しない方が宜しいかと。

・エラー処理は手抜きです。


お試し環境
  Windows7 64bit Edition
  Visual Basic 2008 AnyCPU対象


/*-------------------------------- お試し結果 ------------------------------*/

・(1)ネットワーク接続は無し、自PC内動作

①IPv6、ループバック
エンドポイント確認1、接続無し

②IPv6、ループバック
エンドポイント確認2、接続無し

③IPv6、ループバック
エンドポイント確認3、接続無し

④IPv4、ループバック
エンドポイント確認4、接続無し

⑤IPv4、未指定アドレス
エンドポイント確認5、接続無し

⑥IPv4、ループバック
エンドポイント確認6、接続無し

⑦IPv4、ループバック
エンドポイント確認7、接続無し

⑧IPv4、未指定アドレス
エンドポイント確認8、接続無し

⑨例外発生(そのようなホストは不明です)


・(2)ネットワーク接続はLAN、LAN内動作

①IPv6、リンクローカルアドレス
エンドポイント確認1、LAN接続

②IPv6、ループバック
エンドポイント確認2、LAN接続

③IPv6、リンクローカルアドレス
エンドポイント確認3、LAN接続

④IPv4、ループバック
エンドポイント確認4、LAN接続

⑤IPv4、未指定アドレス
エンドポイント確認5、LAN接続

⑥IPv4、プライベートアドレス
エンドポイント確認6、LAN接続

⑦IPv4、ループバック
エンドポイント確認7、LAN接続

⑧IPv4、未指定アドレス
エンドポイント確認8、LAN接続

⑨例外発生(そのようなホストは不明です)


・(3)ネットワーク接続はインターネット、外部動作

①IPv6、リンクローカルアドレス
エンドポイント確認1、インターネット接続

②IPv6、ループバック
エンドポイント確認2、インターネット接続

③IPv6、リンクローカルアドレス
エンドポイント確認3、インターネット接続

④IPv4、ループバック
エンドポイント確認4、インターネット接続

⑤IPv4、未指定アドレス
エンドポイント確認5、インターネット接続

⑥IPv4、グローバルアドレス
エンドポイント確認6、インターネット接続

⑦IPv4、ループバック
エンドポイント確認7、インターネット接続

⑧IPv4、未指定アドレス
エンドポイント確認8、インターネット接続

⑨IPv4、リモートアドレス
エンドポイント確認9、インターネット接続


/*----------------------------------------------------------------------------*/


/*-------------------------------- お試しソース ----------------------------*/

Public Class Form1

    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click

        Dim remoteEndPoint As Net.IPEndPoint
        Dim msg As String

        Dim port As Integer = 22000

        ' ① EndPoint check1
        Dim ipHostInfo As Net.IPHostEntry = Net.Dns.GetHostEntry(Net.Dns.GetHostName())
        Dim ipAddress As Net.IPAddress = ipHostInfo.AddressList(0)
        Dim localEndPoint As New Net.IPEndPoint(ipAddress, port)

        msg = "ipHostInfo : " & ipHostInfo.ToString & vbCrLf & _
              "ipAddress : " & ipAddress.ToString & vbCrLf & _
              "localEndPoint : " & localEndPoint.ToString
        MsgBox(msg, MsgBoxStyle.Information, "Check local EndPoint1")

        ' ② EndPoint check2
        ipHostInfo = Net.Dns.GetHostEntry("localhost")
        ipAddress = ipHostInfo.AddressList(0)
        localEndPoint = New Net.IPEndPoint(ipAddress, port)

        msg = "ipHostInfo : " & ipHostInfo.ToString & vbCrLf & _
              "ipAddress : " & ipAddress.ToString & vbCrLf & _
              "localEndPoint : " & localEndPoint.ToString
        MsgBox(msg, MsgBoxStyle.Information, "Check local EndPoint2")

        ' ③ EndPoint check3
        ipHostInfo = Net.Dns.GetHostEntry("127.0.0.1")
        ipAddress = ipHostInfo.AddressList(0)
        localEndPoint = New Net.IPEndPoint(ipAddress, port)

        msg = "ipHostInfo : " & ipHostInfo.ToString & vbCrLf & _
              "ipAddress : " & ipAddress.ToString & vbCrLf & _
              "localEndPoint : " & localEndPoint.ToString
        MsgBox(msg, MsgBoxStyle.Information, "Check local EndPoint3")

        ' ④ EndPoint check4
        ipAddress = Net.IPAddress.Parse("127.0.0.1")
        localEndPoint = New Net.IPEndPoint(ipAddress, port)

        msg = "ipAddress : " & ipAddress.ToString & vbCrLf & _
              "localEndPoint : " & localEndPoint.ToString
        MsgBox(msg, MsgBoxStyle.Information, "Check local EndPoint4")

        ' ⑤ EndPoint check5
        localEndPoint = New Net.IPEndPoint(Net.IPAddress.Any, port)

        msg = "localEndPoint : " & localEndPoint.ToString
        MsgBox(msg, MsgBoxStyle.Information, "Check local EndPoint5")

        ' ⑥ EndPoint check6
        ipHostInfo = Net.Dns.Resolve(Net.Dns.GetHostName())
        ipAddress = ipHostInfo.AddressList(0)
        localEndPoint = New Net.IPEndPoint(ipAddress, port)

        msg = "ipHostInfo : " & ipHostInfo.ToString & vbCrLf & _
              "ipAddress : " & ipAddress.ToString & vbCrLf & _
              "localEndPoint : " & localEndPoint.ToString
        MsgBox(msg, MsgBoxStyle.Information, "Check local EndPoint6")

        ' ⑦ EndPoint check7
        ipHostInfo = Net.Dns.Resolve("localhost")
        ipAddress = ipHostInfo.AddressList(0)
        localEndPoint = New Net.IPEndPoint(ipAddress, port)

        msg = "ipHostInfo : " & ipHostInfo.ToString & vbCrLf & _
              "ipAddress : " & ipAddress.ToString & vbCrLf & _
              "localEndPoint : " & localEndPoint.ToString
        MsgBox(msg, MsgBoxStyle.Information, "Check local EndPoint7")

        ' ⑧ EndPoint check8
        ipAddress = Net.Dns.Resolve(Net.IPAddress.Any.ToString()).AddressList(0)
        localEndPoint = New Net.IPEndPoint(ipAddress, port)

        msg = "ipAddress : " & ipAddress.ToString & vbCrLf & _
              "localEndPoint : " & localEndPoint.ToString
        MsgBox(msg, MsgBoxStyle.Information, "Check local EndPoint8")

        ' ⑨ EndPoint check9
        Try
            ipHostInfo = Net.Dns.GetHostEntry("www.nifty.com")
            ipAddress = ipHostInfo.AddressList(0)
            remoteEndPoint = New Net.IPEndPoint(ipAddress, port)

            msg = "ipHostInfo : " & ipHostInfo.ToString & vbCrLf & _
                  "ipAddress : " & ipAddress.ToString & vbCrLf & _
                  "remoteEndPoint : " & remoteEndPoint.ToString
            MsgBox(msg, MsgBoxStyle.Information, "Check remote EndPoint9")

        Catch ex As Net.Sockets.SocketException
            Debug.Print("SocketException : {0}", ex.ToString())
        Catch ex As Exception
            Debug.Print("Unexpected exception : {0}", ex.ToString())
        End Try

    End Sub

End Class

/*----------------------------------------------------------------------------*/

/*----------------------------------------------------------------------------*/
/*============================================================================*/