VB.NET でTCP を使うには

投稿日 2015/11/29

[Home] 

概要

VB.NET で TCP を使って通信を行うには、System.Net.Sockets の TcpClient や TcpListener を使用します。TcpClient が名前通りクライアント側で、TcpListener がサーバー側です。使用する場合は、これらのクラスを継承してカスタマイズするとよいです。


TcpClient の使用例

次の MyTcpClient は System.Net.Sockets.TcpClient から派生したクラスでテキストの送受信を行うためのメソッド SendText と ReceiveText を持ちます。

Imports System.Text
Imports System.Net.Sockets

Public Class MyTcpClient : Inherits System.Net.Sockets.TcpClient
#Region "メンバー変数と定数"
    Public Const BUFSIZE As Integer = 16384  ' 16 KB
    Private stream As NetworkStream
    Private buffer(BUFSIZE) As Byte
#End Region

#Region "プロパティ"
    ''' <summary>
    ''' エンコーディング
    ''' </summary>
    ''' <returns></returns>
    Public Property MyEncoding As Encoding
#End Region

    ''' <summary>
    ''' コンストラクタ
    ''' </summary>
    ''' <param name="host">ホストのURL</param>
    ''' <param name="port">ホストのポート番号</param>
    Public Sub New(ByVal host As String, ByVal port As Integer)
        MyBase.New(host, port)
        MyEncoding = Encoding.ASCII
        stream = GetStream()
    End Sub
    
    ''' <summary>
    ''' テキストを送る。
    ''' </summary>
    ''' <param name="text">送信するテキスト</param>
    Public Sub SendText(ByVal text As String)
        Dim byteArray() As Byte = MyEncoding.GetBytes(text)
        stream.Write(byteArray, 0, byteArray.Length)
    End Sub
    
    ''' <summary>
    ''' テキストを受信する。
    ''' </summary>
    ''' <returns>受信したテキスト</returns>
    ''' <remarks>最大バイト長は BUFSIZE による。</remarks>
    Public Function ReceiveText() As String
        Dim len As Integer = stream.Read(buffer, 0, buffer.Length)
        Dim text As String = MyEncoding.GetString(buffer, 0, len)
        Return text
    End Function
End Class

次のコードは、MyTcpClient クラスを使う例です。

Module TestClient
    ''' <summary>
    ''' MyTcpClient のテスト
    ''' </summary>
    Sub Main()
        Const IPAddr = "127.0.0.1"
        Const Port = 1144
        Dim text As String
        Console.WriteLine("MyTcpClient のテスト")
        
        Try
            ' MyTcpClient をインスタンス化する。
            Dim client = New MyTcpClient(IPAddr, Port)
            ' 無限ループ
            While True
                ' プロンプトを表示
                Console.Write("> ")
                text = Console.ReadLine()
                ' キー入力 :quit で終了する。
                If text.ToLower() = ":quit" Then
                    client.SendText(text)
                    Exit While
                End If
                ' テキストを送信する。
                client.SendText(text + vbCrLf)
                ' テキストを受信する。
                text = client.ReceiveText()
                ' 受信したテキストを表示する。
                Console.WriteLine(text)
            End While
            Console.WriteLine("終了しました。")
        Catch ex As Exception
            Console.WriteLine(ex.Message, "例外")
        End Try
        
#If DEBUG Then
        Console.ReadKey()
#End If
    End Sub
    
End Module

TcpListener の例

Point to Point の通信を行う場合は、同期型メソッドを使ったほうが簡単です。下の例は同期型メソッドを使う例です。

Imports System.Net
Imports System.Net.Sockets
Imports System.IO
Imports System.Text

''' <summary>
''' TCP サーバ
''' </summary>
Public Class MyTcpServer : Inherits TcpListener
#Region "メンバー変数"
    Public Const BUFSIZE = 16384  ' 16 KB
    Private buffer(BUFSIZE) As Byte
    Private stream As NetworkStream
    Private client As TcpClient
#End Region

#Region "プロパティ"
    ''' <summary>
    ''' エンコーディング
    ''' </summary>
    ''' <returns></returns>
    Public Property MyEncoding As Encoding
#End Region

    ''' <summary>
    ''' コンストラクタ
    ''' </summary>
    ''' <param name="host">このホスト(リスナ)のIPAddress</param>
    ''' <param name="port">このホスト(リスナ)のポート番号</param>
    Public Sub New(ByVal host As IPAddress, ByVal port As Integer)
        MyBase.New(host, port)
        MyEncoding = Encoding.ASCII
    End Sub
    
    ''' <summary>
    ''' クライアントから受信したテキストを返す。
    ''' </summary>
    ''' <returns>ライアントから受信したテキスト</returns>
    Public Function Read() As String
        Dim n As Integer = stream.Read(buffer, 0, buffer.Length)
        Dim text = MyEncoding.GetString(buffer, 0, n)
        Return text
    End Function
    
    ''' <summary>
    ''' クライアントへテキストを送信する。
    ''' </summary>
    ''' <param name="text">送信するテキスト</param>
    Public Sub Write(ByVal text As String)
        Dim data() As Byte = MyEncoding.GetBytes(text)
        stream.Write(data, 0, data.Length)
    End Sub
    
    ''' <summary>
    ''' クライアントの接続を待機する。
    ''' </summary>
    Public Sub Open()
        ' 待機を開始する。
        Start()
        
        ' クライアントオブジェクトがない場合はクライアントを受け付けてストリームを得る。   
        If client Is Nothing Then
            client = AcceptTcpClient()
            stream = client.GetStream()
        End If
    End Sub
    
    ''' <summary>
    ''' 接続を閉じる。
    ''' </summary>
    Public Sub Close()
        If stream IsNot Nothing Then
            stream.Close()
        End If
    End Sub
End Class

下のコードは MyTcpServer クラスの使用例です。

Imports System.Net

Module TestServer
    Const HostAddress = "127.0.0.1"
    Const Port = 1144
    
    ''' <summary>
    ''' MyTcpServer のテスト
    ''' </summary>
    Sub Main()
        Dim host As IPAddress = IPAddress.Parse(HostAddress)
        ' MyTcpServer をインスタンス化
        Dim server = New MyTcpServer(host, Port)
        Dim req As String
        
        ' 接続を待機する。
        Console.WriteLine("接続を待機しています。")
        server.Open()
        
        ' 無限ループ
        While True
            ' クライアントのリクエストを文字列を読む。
            req = server.Read()
            
            ' リクエストが ":quit" なら終了。
            If req.ToLower().Equals(":quit") Then
                server.Close()
                Exit While
            End If
            
            ' リクエストを表示
            Console.WriteLine(req)
            
            ' クライアントへ文字列を送る。
            Dim str = "Received: " & req
            server.Write(str)
            
            ' 送信した文字列を表示
            Console.WriteLine(str)
            
        End While
        
        Console.WriteLine("終了しました。")
    End Sub
    
End Module

複数のクライアントからの接続をサポートするには、スレッドを使ってクライアントからの接続を保持してサービスを提供する必要があります。

Imports System.IO
Imports System.Text
Imports System.Net
Imports System.Net.Sockets

''' <summary>
''' 複数のクライアントからの接続可能な TCP サーバー
''' </summary>
''' <remarks>.NET 4.5 以上が必要</remarks>
Public Class MyTcpServerMulti : Inherits TcpListener
    ' 非同期メソッドを使用するかどうか
    Private _async As Boolean
    
    ''' <summary>
    ''' エンコーディング
    ''' </summary>
    ''' <returns></returns>
    Public Property MyEncoding As Encoding
    
    ''' <summary>
    ''' コンストラクタ
    ''' </summary>
    ''' <param name="host">このホストのIPAddressオブジェクト</param>
    ''' <param name="port">ポート番号</param>
    ''' <param name="async">True なら非同期メソッドを使用する。</param>
    Public Sub New(ByVal host As IPAddress, ByVal port As Integer, _
      Optional ByVal [async] As Boolean = False)
        MyBase.New(host, port)
        MyEncoding = Encoding.ASCII
        _async = [async]
    End Sub
    
    ''' <summary>
    ''' クライアントからの接続を待機する。
    ''' </summary>
    ''' <returns>TcpClientオブジェクト</returns>
    Public Function WaitConnection() As TcpClient
        Dim client As TcpClient
        Me.Start()
        If _async Then
            Dim task As Task(Of Socket) = AcceptSocketAsync()
            task.Wait()
            client = New TcpClient()
            client.Client = task.Result
            Return client
        Else
            client = AcceptTcpClient()
            Return client
        End If
    End Function
    
    ''' <summary>
    ''' クライアントから受信した文字列を読む。
    ''' </summary>
    ''' <param name="client">クライアント</param>
    ''' <returns>受信した文字列</returns>
    Public Function Read(ByVal client As TcpClient) As String
        Dim stream As NetworkStream = client.GetStream()
        Dim reader As StreamReader = New StreamReader(stream, MyEncoding)
        If _async Then
            Dim task As Task(Of String) = reader.ReadLineAsync()
            task.Wait()
            Return task.Result
        Else
            Dim text As String = reader.ReadLine()
            Return text
        End If
    End Function
    
    ''' <summary>
    ''' クライアントへ文字列を送信する。
    ''' </summary>
    ''' <param name="client"></param>
    ''' <param name="text"></param>
    Public Sub Write(ByVal client As TcpClient, ByVal text As String)
        Dim stream As NetworkStream = client.GetStream()
        Dim writer As StreamWriter = New StreamWriter(stream, MyEncoding)
        If _async Then
            Dim task As Task = writer.WriteLineAsync(text)
            task.Wait()
        Else
            writer.WriteLine(text)
        End If
        writer.Flush()
    End Sub
    
    ''' <summary>
    ''' 接続を閉じる。
    ''' </summary>
    Public Sub Close(ByVal client As TcpClient)
        Dim stream As NetworkStream = client.GetStream()
        If stream IsNot Nothing Then
            stream.Close()
        End If
    End Sub
End Class

次のコードは TcpServerMulti クラスで使用しているワーカースレッドクラスです。

Imports System.Net
Imports System.Net.Sockets

''' <summary>
'''  バックグランドワーカー
''' </summary>
Public Class Worker : Inherits AbstractWorker
    ' 終了通知イベント
    Public Event Completed As Action
    ' サーバーオブジェクト
    Private server As MyTcpServerMulti
    ' クライアントオブジェクト
    Private client As TcpClient
    
    ''' <summary>
    ''' コンストラクタ
    ''' </summary>
    ''' <param name="server">サーバーオブジェクト</param>
    ''' <param name="client">クライアントオブジェクト</param>
    Public Sub New(ByVal server As MyTcpServerMulti, ByVal client As TcpClient)
        Me.server = server
        Me.client = client
    End Sub
    
    ''' <summary>
    ''' バックグランド動作メソッド
    ''' </summary>
    Protected Overrides Sub Run()
        Dim req As String
        ' 無限ループ
        While True
            ' クライアントのリクエストを文字列を読む。
            req = server.Read(client)
            
            ' リクエストが ":quit" なら終了。
            If req.ToLower().Equals(":quit") Then
                server.Close(client)
                Exit While
            End If
            
            ' リクエストを表示
            Console.WriteLine(req)
            
            ' クライアントへ文字列を送る。
            Dim str = "Received: " & req & vbCrLf
            server.Write(client, str)
            
            ' 送信した文字列を表示
            Console.WriteLine(str)
            
        End While
    End Sub
End Class

下のコードは TcpServerMulti クラスの使用例です。

Imports System.Net
Imports System.Net.Sockets

Module TestServer
    Const HostAddress = "127.0.0.1"
    Const Port = 1144
    
    Sub Main()
        Dim host As IPAddress = IPAddress.Parse(HostAddress)
        ' MyTcpServer をインスタンス化
        Dim server = New MyTcpServerMulti(host, Port, True)
        ' 接続を待機する。
        Console.WriteLine("サーバー: 接続を待機しています。")
        
        Dim i = 0
        Do While True
            Dim client = server.WaitConnection()
            Console.WriteLine("サーバー: 接続しました。" & i.ToString())
            Dim worker As New Worker(server, client)
            Dim hash = worker.GetHashCode()
            AddHandler worker.Completed, Sub()
                  Console.WriteLine("接続 " & hash & " が終了しました。")
            End Sub
            worker.Start()
            Console.WriteLine("接続 " & hash & " が開始しました。")
            i += 1
            If i > 100 Then
                Exit Do
            End If
        Loop
        
        Console.WriteLine("終了しました。")
#If DEBUG Then
        Console.ReadKey()
#End If
    End Sub
    
End Module

 

 


 

このページの先頭へ