PR

VB.NET(VB2022)でTCPサーバー(タスク処理)を実装

VB.NET
本記事はプロモーションが含まれています。

こんにちは、ENGかぴです。

VB.NET(Visual Basic 2022)のデスクトップアプリでTCPクライアントの接続要求を受け入れるTCPサーバーをタスクを使用して行う方法を紹介します。タスクで接続待機中にアプリが固まることなく他の処理を行うことができます。

TCPサーバーを非同期処理で行う方法を下記記事で紹介していますが、本記事は同期処理を非同期のタスクを生成して行う方法をまとめています。またTCPクライアントは下記記事で動作確認したESP32-WROOM-32E(以下ESP32)を使用します。

VB.NET(VB2022)でTCPサーバー(非同期)を実装

VB.NET(VB2022)のデスクトップアプリで動作確認したことを下記リンクにまとめています。

VB.NET(VB2022)のデスクトップアプリ開発でできること

以下ではTCPサーバーをサーバ、TCPクライアントをクライアントとします。以下はVisual Basic 2022をVB2022とします。

TCPサーバーの動作を確認するアプリ

アプリのコントロールの配置
コントロールの追加

本記事は参考記事とコントロールの配置やイベントは冒頭の参考記事と同様です。

サーバーは自局のIPアドレスとポートを指定して「開始」ボタンをクリックするとクライアントからの接続要求を待機します。

クライアントからの接続要求を受け入れると接続中のクライアントのリストボックスにクライアントのIPアドレスとポートを表示します。

接続中のクライアントがある場合、リストボックスのIPアドレスとポートを選択して「送信」ボタンを押すとTextBox6に入力した文字列をクライアントに送信します。

スポンサーリンク

変数の宣言

アプリで共通する変数を宣言する場合はフォームクラス内で変数を宣言する必要があります。VBの場合はDimでメモリに変数を割り当てます。

Imports System.Net.Sockets

Public Class Form1
    Dim ServerTCP As TcpListener
    Dim cts As Threading.CancellationTokenSource
End Class
//使用例
cts = New Threading.CancellationTokenSource()

Form1クラス直前でインポート(Imports)しているものはネームスペースを使用する意味です。4行目のServerTCPはSystem.Net.SocketsネームスペースのTcpListenerクラスでインスタンス化する宣言になります。インポートしない場合はSystem.Net.Socketsと記述する必要がありますが、名前の簡素化に従って省略するとネームスペースが自動で追加されます。

例ではアプリ全体で使用するServerTCPをインスタンス化しています。クラス内で宣言した場合はアプリを終了しない限りメモリを割り当てた状態になります。ServerTCPはクライアント接続待ちや接続受け入れ後のパケットの送受信に使用します。

5行目のctsはSystem.ThreadingネームスペースのCancellationTokenSourceクラスでインスタンス化する宣言になります。ctsはTCP接続や受信待機のタスクをキャンセルする場合に使用します。ctsはThreading.CancellationTokenSource()メソッドでインスタンス化して使用します。

PR:RUNTEQ(ランテック )- マイベスト3年連続1位を獲得した実績を持つWebエンジニア養成プログラミングスクール

TCPサーバーの開始と停止

サーバー動作確認の全体構成
サーバー動作確認の全体構成

WiFiはノートパソコンであれば標準搭載されていますが、動作確認に使用しているデスクトップパソコンにはWiFiを搭載していないためルータを介してESP32と通信を行います。

ESP32と接続するルータ(アクセスポイント)にはWZR-HP-G300NH(バッファロー:生産中止)を使用します。パソコンとアクセスポイントをLANケーブルで接続します。

ESP32を2台準備し、サーバーに接続要求をおこないます。サーバーはクライアントの接続を受け入れると「接続中のクライアント」にクライアントのIPアドレスとポートを表示します。

接続中のクライアントがあれば接続しているソケットを維持しますが、接続後一定時間データの受信がない場合は、ソケットを破棄してサーバーの負担を減らすようにします。

サーバーを開始する

System.Net.Socketsのネームスペースを使用します。自局のIPアドレスとポートを使用してサーバーを開始します。Button1のクリックイベントに処理を追加します。

Private Sub TaskAccept()
    '//クライアント接続要求の受付開始
    If ServerTCP Is Nothing Then
        ServerTCP = New TcpListener(localIp, localPort)
        ServerTCP.Start()
    End If
    'タスクを生成する
    Task.Run(
        Sub()
            Try
                TcpServer(no).client = ServerTCP.AcceptTcpClient()
                TaskRecive(no)'受信待機
            Catch ex As Exception

            End Try
       End Sub
    )
End Sub

TaskAccept()関数はButton1をクリックすると遷移し、サーバーの開始とパケットの受信処理のタスクを生成する自作の関数です。

localIPは存在するイーサーネットを確認して取得したIPを格納した変数です。自局のIPを指定する場合IPAddressクラスのLoopbackフィールドで指定することもできます。localPortはサーバーを開始するポートを指定するための変数です。

TcpListenerクラスを第1引数に自局のIPを指定します。第2引数は自局のポートを指定してServerTCPでインスタンス化して初期化します。以降はServerTCPオブジェクトを使用してTCP通信の管理を行います。

ServerTCPオブジェクト(TcpListenerクラス)のStart()メソッドでサーバーを開始します。

サーバーを開始するとクライアントからの接続要求及び受信待機のタスクを生成します。Run()メソッド内に接続要求の受付と受信待機のタスク処理を追加します。

TCP接続とパケットの受信はタスクを生成して別のスレッドで行います。タスクはSystem.Threading.tasks.taskクラスのRun()メソッドの引数に処理を追加して生成します。

ServerTCPオブジェクトのAcceptTcpClient()メソッドでクライアントからの接続要求を待機します。接続要求を受け入れるとClientクラスのTcpServer(no).clientオブジェクト(TcpClientクラス)にクライアント情報を格納し、以降の処理に進みます。

PR: わからないを放置せず、あなたにあったスキルを身に着けるコツを教える テックジムPython入門講座の申込

TCPパケットを受信する

Private Sub TaskRecive(cnt As Integer)

    TaskAccept()'次の接続要求待ちのタスクを生成する

    While cts.Token.IsCancellationRequested = False
        If TcpServer(cnt).client IsNot Nothing Then
            Try
                Dim intCount As Integer = TcpServer(cnt).client.GetStream().Read(getByte(cnt), 0, getByte(cnt).Length)
              
                If intCount <> 0 Then
                    '//受信部分だけ切り出す
                    For i = 0 To intCount - 1
                        RxRingSet(cnt, getByte(cnt)(i))
                    Next
                Else
                    Exit While
                End If
            Catch ex As Exception

            End Try
        Else
            Exit While
        End If
    End While
End Sub

TaskRecive()関数はクライアントからのパケット受信待ちを行う自作の関数です。接続しているクライアントの受信処理をループで行うため、ループに入る前に次の接続要求を待機するためTaskAccept()関数を呼び出してタスクを生成しています。

パケットの受信はタスクがキャンセルされない限りWhileによるループで行います。タスクのキャンセルやソケットの消失でアプリが強制終了しないようにTry catch文を使用して同期受信を行います。

タスクをキャンセルせずに次の接続を行うとタスクが残ってしまい不要なタスクが生成されたままになってしまいます。

TcpServer(cnt).clientオブジェクト(TcpClientクラス)のGetStream()のRead()メソッドを使用してクライアントからパケットを受信します。

第1引数に読み込んだデータを格納する配列を指定します。第2引数にパケットの読み出しを開始位置を指定します。例ではパケットのデータをすべて取得するために0を指定しています。第3引数に読み出すデータ数を指定します。

戻り値にRead()メソッドで読み出したデータの数が格納されるため、表示用のデータに移し替えて格納します。表示用のデータに移し替えて格納しています。

Read()メソッドは同期受信するためメイン処理で使用するとアプリが占有されフリーズしたように固まってしまいますが、非同期のタスクで処理するためメイン処理に影響を与えることなくパケットを受信することができます。

サーバーを停止する

Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
        
    If Button2.BackColor = Color.LightGreen Then
        ServerTCP.Stop()
        ServerTCP = Nothing
        cts.Cancel() 'タスクをキャンセル
        Button2.BackColor = Color.LightBlue
        Button1.BackColor = Color.LightGreen
    End If
End Sub

Button2をクリックするとサーバーを停止します。ServerTCPオブジェクト(TcpListenerクラス)のStop()メソッドでサーバーを停止します。次にNothingでServerTCPオブジェクトを破棄していますが、破棄せずインスタンス化したままでも問題ありません。

サーバーを停止はタスクのキャンセルを行ってから行います。タスクのキャンセルはインスタンス化したctsのCancel()メソッドを使用します。

ctsのTokenプロパティはキャンセルを受け付けるとIsCancellationRequestedがTrueになります。タスクの同期受信はタスクのキャンセルされない限り繰り返すようにしていますが、キャンセル要求によりTrueになるとループから抜け出してタスクを終了します。

Private Sub Timer2_Tick(sender As Object, e As EventArgs) Handles Timer2.Tick
    Dim i As Integer

    For i = 0 To CONECT_CLIENT_MAX - 1
        If TimClose(i) = TIME_UP Then
            If TcpServer(i).client IsNot Nothing Then
                TcpServer(i).client.Close()'サーバーに接続中のクライアントを破棄
                TimClose(i) = TIME_OFF
            End If
        End If
    Next

    If cts IsNot Nothing Then
        If cts.Token.IsCancellationRequested Then
            If taskstop = False Then
                taskstop = True
                For i = 0 To CONECT_CLIENT_MAX - 1
                    TimClose(i) = TIME_UP
                Next
            End If
        End If
    End If
End Sub

ctsのCancel()メソッドを受けるとタスクが終了するためサーバーに接続しているクライアントの情報を破棄します。

クライアントの破棄の判断はTimer2のインターバルイベントで生成したタイマを使用します。タスクのキャンセルを受け入れたタイミングでタイマをTimCloseをタイムアップ(TIME_UP)にしてクライアントを破棄します。

TCPパケットを送信する

Private Sub Button3_Click(sender As Object, e As EventArgs) Handles Button3.Click
    Dim data() As Byte = Encoding.GetEncoding("utf-8").GetBytes(TextBox6.Text)
    Dim buf(data.Length + 1) As Byte

    For i = 0 To data.Length - 1
        buf(i) = data(i)
    Next
    'CRLFを付加する場合に追加する
    buf(data.Length) = &HD
    buf(data.Length + 1) = &HA

    If ClientTCP IsNot Nothing Then
        Try
            ClientTCP.GetStream().Write(buf, 0, buf.Length)
        Catch ex As Exception

        End Try
    End If
End Sub

Button3をクリックした時にリストボックスから選択したクライアントが存在している場合、TextBox6に入力している文字列を「UTF-8」でエンコーディングしてGetStream()Write()メソッドで送信します。

第1引数に送信するデータのアドレス(配列)、第2引数に送信するデータの先頭のオフセット値を指定します。例では配列をすべて送信するため0を指定しています。第3引数に送信するデータのサイズを指定します。

クライアントが存在しない場合、アプリが強制終了するため、クライアントが存在を確認してから切断処理を行います。

スポンサーリンク

動作確認(デバッグ)

動作確認の全体構成
動作確認の全体構成

クライアントとしてESP32を2台使用して動作確認を行います。ESP32はアクセスポイントに接続します。接続に成功するとサーバーに対して接続要求を行います。サーバーが接続要求が受け入れると双方向通信可能になります。

動作確認のためデバッグを行います。デバッグの開始はエディタ上部の「▶開始」をクリックすると開始します。

アプリの動作確認の結果
アプリの動作確認の結果

アプリのComboBox1で使用可能なイーサーネットを選択します。次にサーバーを開始するポートを指定します。ESP32はポート80に対して接続要求を出すためポートを80にします。

「開始」ボタンをクリックするとサーバーが開始し、クライアントからの接続待機になります。

サーバーが接続要求を受け入れるとリストボックスにクライアントのIPアドレスとポートを表示します。この状態でESP32のピン5を操作するとクライアント1であれば「TcpClient-1」、クライアント2であれば「TcpClient-2」の文字列を送信します。

サーバーが文字列を受信するとTextBox7に文字列を表示するためTCP通信がうまくできていることが確認することができます。

リストボックスのクライアントを選択し「送信」ボタンをクリックするとTextBox6に入力した文字列のパケットをクライアントに送信できます。

サーバーからクライアントにデータが送信できているかを確認するためにESP32のシリアルモニターで確認します。

ESP32のシリアルモニターの結果
ESP32のシリアルモニターの結果

TextBox6に「サーバーテスト」を入力して「送信」ボタンをクリックするとシリアルモニターに「サーバーテスト」が表示されており、サーバーから送信した文字列が受信できていることが確認できます。

タスクの生成の確認はアプリを一時停止して行います。エディターのタスクの欄に生成中のタスクが表示されます。

タスクの生成を確認
タスクの生成を確認

「停止」ボタンをクリックするとサーバーを停止します。停止するとタスクのキャンセルと同時にソケット破棄の判断を行うタイマがタイムアップするためクライアントが直ちに破棄されます。またサーバーが停止しているのでクライアントからの接続要求の受け入れを行わないことが確認できます。

クライアントを受け入れた後、任意の時間(デフォルトは30秒)送受信がない場合はパケットの送受信が完了したと見なしてクライアントを破棄するためリストボックスからクライアントが削除されることが確認できます。この場合はサーバーが停止していないためクライアントからの接続要求があれば受け入れてリストボックスからクライアントが追加されます。

コードのデバッグを行う場合は、コードエディタでブレークポイントを置くことで一時的にプログラムを停止させることができます。

ブレークポイントでデバッグする方法
ブレークポイントでデバッグする方法

ブレークポイントはコードエディターの最左部分をクリックして●マークが表示されれば設置できています。例ではButton1がクリックされた時に発生したイベントの先頭部分にブレークポイントを置いています。この状態でButton1をクリックするとブレークポイントでプログラムが一時停止します。

F11を押すとステップ実行になるため1行ずつ内容を確認しながらデバッグを行うことができます。上部の「▶続行(C)」をクリックするとブレークポイント状態から通常の動作に復帰します。

PR:(即戦力のスキルを身に着ける:DMM WEBCAMP 学習コース(はじめてのプログラミングコース))

ソースコード全体

以下のソースコードはコンパイルして動作確認をしております。コメントなど細かな部分で間違っていたりやライブラリの更新などにより動作しなくなったりする可能性があります。参考としてお使いいただければと思います。

Imports System.Net
Imports System.Net.NetworkInformation
Imports System.Net.Sockets
Imports System.Text

Public Class Form1

    Const TIME_UP As Byte = 0
    Const TIME_OFF As Int16 = -1
    Const TIME_CLOSE As Byte = 30
    Const CONECT_CLIENT_MAX = 5 '接続可能クライアント数
    Const RXRGSZ As Integer = 2048

    Structure NET_INTERFACE
        Dim name As String
        Dim localip As IPAddress
    End Structure

    Structure TCP_SERVER
        Dim client As TcpClient
        Dim name As String
        Dim tsk As Task
    End Structure

    Structure TYP_COMRING
        Dim rp As Integer
        Dim wp As Integer
        Dim dat() As Byte
    End Structure

    Dim ServerTCP As TcpListener
    Dim TcpServer(CONECT_CLIENT_MAX - 1) As TCP_SERVER
    Dim LocalNet() As NET_INTERFACE
    Dim localIp As IPAddress
    Dim localPort As Integer
    Dim RxRing(CONECT_CLIENT_MAX - 1) As TYP_COMRING '受信したデータを管理
    Dim getByte(CONECT_CLIENT_MAX - 1)() As Byte
    Dim conectcnt As Byte
    Dim TcpServerName(CONECT_CLIENT_MAX - 1) As String
    Dim tcpServerNo As Byte
    Dim TimClose(CONECT_CLIENT_MAX - 1) As Integer
    Dim no As Integer
    Dim cts As Threading.CancellationTokenSource
    Dim taskstop As Boolean = False

    Private Sub GetNetworkList()
        Dim netinterface = NetworkInterface.GetAllNetworkInterfaces
        Dim wk(netinterface.Length - 1) As NET_INTERFACE
        Dim i As Integer
        Dim j As Integer = 0
        Dim cnt As Integer = 0

        For Each netin In netinterface
            If netin.OperationalStatus = OperationalStatus.Up AndAlso
               netin.NetworkInterfaceType = NetworkInterfaceType.Ethernet Then

                wk(i).name = netin.Name
                cnt += 1

                Dim ipprperties As IPInterfaceProperties = netin.GetIPProperties

                If ipprperties IsNot Nothing Then
                    For Each ipadrs In ipprperties.UnicastAddresses
                        wk(i).localip = ipadrs.Address
                    Next
                End If
            End If

            i += 1
        Next

        ReDim LocalNet(cnt - 1)

        For i = 0 To wk.Length - 1
            If wk(i).name <> "" Then
                LocalNet(j) = wk(i)
                j += 1
            End If
        Next

    End Sub

    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        ComboBoxInit()
        RxRingInit()
        Timer1.Enabled = True
        Timer2.Enabled = True
    End Sub

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

        If Button1.BackColor = Color.LightGreen Then
            localPort = TextBox8.Text
            taskstop = False

            For i = 0 To TimClose.Length - 1
                TimClose(i) = TIME_OFF
            Next

            cts = New Threading.CancellationTokenSource()
            TaskAccept()

        End If
    End Sub

    Private Sub TaskAccept()
        Dim remote_ep As IPEndPoint
        Dim ip() As Byte
        Dim ipstr As String
        'Dim no As Integer

        '//クライアント接続要求の受付開始
        If ServerTCP Is Nothing Then
            ServerTCP = New TcpListener(localIp, localPort)
            ServerTCP.Start()
            Button1.BackColor = Color.LightBlue
            Button2.BackColor = Color.LightGreen
        End If

        For no = 0 To TcpServer.Length - 1
            If TcpServer(no).client Is Nothing Then
                Exit For
            End If
        Next

        If no = TcpServer.Length Then
            TextBox7.Text = "接続クライアント数の最大です。"
        End If

        'タスクを生成する
        Task.Run(
            Sub()
                Try
                    TcpServer(no).client = ServerTCP.AcceptTcpClient()
                    TimClose(no) = TIME_CLOSE
                    remote_ep = TcpServer(no).client.Client.RemoteEndPoint
                    ip = remote_ep.Address.GetAddressBytes()
                    ipstr = ip(0) & "." & ip(1) & "." & ip(2) & "." & ip(3) &
                                    ":" & remote_ep.Port
                    TcpServer(no).name = ipstr
                    TaskRecive(no)
                Catch ex As Exception

                End Try
            End Sub
            )
    End Sub

    Private Sub TaskRecive(cnt As Integer)

        TaskAccept()

        While cts.Token.IsCancellationRequested = False
            If TcpServer(cnt).client IsNot Nothing Then
                Try
                    Dim intCount As Integer = TcpServer(cnt).client.GetStream().Read(getByte(cnt), 0, getByte(cnt).Length)

                    If intCount <> 0 Then
                        '//受信部分だけ切り出す
                        For i = 0 To intCount - 1
                            RxRingSet(cnt, getByte(cnt)(i))
                        Next
                    Else
                        TimClose(cnt) = TIME_UP
                        Exit While
                    End If
                Catch ex As Exception

                End Try
            Else
                Exit While
            End If

        End While

        TimClose(cnt) = TIME_UP
    End Sub

    Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
        If Button2.BackColor = Color.LightGreen Then
            'button1でインスタンス化しているためColorプロパティの判断のみでオブジェクトの破棄を行う
            ServerTCP.Stop()
            ServerTCP = Nothing
            cts.Cancel() 'タスクをキャンセル
            Button2.BackColor = Color.LightBlue
            Button1.BackColor = Color.LightGreen
        End If
    End Sub

    Private Sub Button3_Click(sender As Object, e As EventArgs) Handles Button3.Click
        Dim data() As Byte = Encoding.GetEncoding("utf-8").GetBytes(TextBox6.Text)
        Dim buf(data.Length + 1) As Byte

        For i = 0 To data.Length - 1
            buf(i) = data(i)
        Next
        buf(data.Length) = &HD
        buf(data.Length + 1) = &HA

        If TcpServer(tcpServerNo).client IsNot Nothing Then
            Try
                TcpServer(tcpServerNo).client.GetStream().Write(buf, 0, buf.Length)
            Catch ex As Exception

            End Try
        End If
    End Sub

    Private Sub ComboBox1_MouseClick(sender As Object, e As MouseEventArgs) Handles ComboBox1.MouseClick
        ComboBoxInit()
    End Sub

    Private Sub ComboBox1_SelectedIndexChanged(sender As Object, e As EventArgs) Handles ComboBox1.SelectedIndexChanged
        localIp = LocalNet(ComboBox1.SelectedIndex).localip
    End Sub

    Private Sub ComboBoxInit()
        Dim i As Integer
        Dim str As String

        GetNetworkList()
        ComboBox1.Items.Clear()

        For i = 0 To LocalNet.Length - 1
            str = LocalNet(i).name & " " & "IP:" & LocalNet(i).localip.ToString
            ComboBox1.Items.Add(str)
        Next

    End Sub

    Private Sub Timer1_Tick(sender As Object, e As EventArgs) Handles Timer1.Tick
        Dim sz As Integer
        Dim i, j As Integer
        Dim chg As Boolean

        For i = 0 To CONECT_CLIENT_MAX - 1
            sz = RxRing(i).wp - RxRing(i).rp

            If sz < 0 Then
                sz += RxRing(i).dat.Length
            End If

            If sz > 0 Then
                For j = 0 To sz - 1
                    TextBox7.Text &= Chr(RxRing(i).dat(RxRing(i).rp))
                    RingRpAdd(i)
                Next
            End If
        Next

        For i = 0 To TcpServer.Length - 1
            If TcpServerName(i) <> TcpServer(i).name Then
                TcpServerName(i) = TcpServer(i).name
                chg = True
            End If
        Next

        For i = 0 To TcpServer.Length - 1
            If TcpServerName(i) <> TcpServer(i).name Then
                TcpServerName(i) = TcpServer(i).name
                chg = True
            End If
        Next

        If chg = True Then
            ListBox1.Items.Clear()

            For i = 0 To TcpServer.Length - 1
                If TcpServer(i).name <> "" Then
                    ListBox1.Items.Add(TcpServer(i).name)
                End If
            Next
        End If

    End Sub

    Private Sub ListBox1_SelectedIndexChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles ListBox1.SelectedIndexChanged
        Dim i As Integer

        If ListBox1.SelectedIndex <> -1 Then
            For i = 0 To TcpServer.Length - 1
                If TcpServer(i).name = ListBox1.Items(ListBox1.SelectedIndex) Then
                    TimClose(i) = TIME_CLOSE
                    tcpServerNo = i
                    Exit For
                End If
            Next
        End If

    End Sub

    'RxRingの初期化を行う
    Private Sub RxRingInit()
        Dim i As Integer

        For i = 0 To CONECT_CLIENT_MAX - 1
            RxRing(i).wp = 0
            RxRing(i).rp = 0
            ReDim RxRing(i).dat(RXRGSZ - 1)
            ReDim getByte(i)(0)
            TimClose(i) = TIME_OFF
        Next
    End Sub

    'RxRingのwpを更新する
    Private Sub RxRingSet(no As Byte, dat As Byte)

        RxRing(no).dat(RxRing(no).wp) = dat

        RxRing(no).wp += 1
        If RxRing(no).wp = RxRing(no).dat.Length Then
            RxRing(no).wp = 0
        End If
    End Sub

    'RxRingのrpを更新する
    Private Sub RingRpAdd(no As Integer)

        RxRing(no).rp += 1
        If RxRing(no).rp >= RxRing(no).dat.Length Then
            RxRing(no).rp = 0
        End If

    End Sub

    Private Sub Button4_Click(sender As Object, e As EventArgs) Handles Button4.Click
        TextBox7.Clear()
    End Sub

    Private Sub Timer2_Tick(sender As Object, e As EventArgs) Handles Timer2.Tick
        Dim i As Integer

        For i = 0 To CONECT_CLIENT_MAX - 1
            If TimClose(i) > TIME_UP Then
                TimClose(i) -= 1
            End If

            If TimClose(i) = TIME_UP Then
                If TcpServer(i).client IsNot Nothing Then
                    TcpServer(i).client.Close()
                    TcpServer(i).client = Nothing
                    TcpServer(i).name = ""
                    TimClose(i) = TIME_OFF
                End If
            End If
        Next

        If cts IsNot Nothing Then
            If cts.Token.IsCancellationRequested Then
                If taskstop = False Then
                    taskstop = True
                    For i = 0 To CONECT_CLIENT_MAX - 1
                        TimClose(i) = TIME_UP
                    Next
                End If
            End If
        End If

    End Sub

End Class

TextBox8は数値以外の文字列を受け付けるため数値以外の入力を許可しないようにKeyPressイベントを追加実装しても良いと思います。KeyPressイベントについては下記記事を参考にしてください。

VB.NET(VB2022)のTextBoxの実装とイベントの追加方法

コントロールの番号やイベントハンドラーなどのプロパティ名などを変更している場合はソースコードのイベントハンドラーをお使いのイベントハンドラーに置き換えてください。

関連リンク

VB.NET(VB2022)のデスクトップアプリで動作確認したことを下記リンクにまとめています。

VB.NET(VB2022)のデスクトップアプリ開発でできること

マイクロソフトはVisual Studio以外にもVSCodeという便利なエディターを提供しています。下記リンクではVSCodeのインストールの仕方からC言語開発環境の作り方までをまとめています。

VSCodeをインストールしてC/C++の開発環境を作る

PR:無料トライアル実施中【PC専用】AIスライド資料作成ツールの利用:イルシル

最後まで、読んでいただきありがとうございました。

タイトルとURLをコピーしました