こんにちは、ENGかぴです。
VB.NET(Visual Basic 2022)でTCPクライアントの接続要求を受け入れるTCPサーバーを非同期処理を使って実装する方法をまとめました。非同期なので接続待機中にアプリが固まることなく他の処理を行うことができます。
Windowsフォームアプリケーション(.NETFramework)のデスクトップアプリを対象としています。以下はVisual Basic 2022をVB2022とします。
TCPクライアントは下記記事のESP32-WROOM-32E(以下ESP32)を使用しています。
ESP32-WROOM-32EでTCPクライアントを実装する
アプリはESP32からの接続要求を受け入れて双方向通信ができるようにします。同様の動作をタスクを生成して処理する方法を下記記事まとめています。
VB.NET(VB2022)でTCPサーバー(タスク処理)を実装
VB.NET(VB2022)のデスクトップアプリで動作確認したことを下記リンクにまとめています。
VB.NET(VB2022)のデスクトップアプリ開発でできること
以下ではTCPサーバーをサーバ、TCPクライアントをクライアントとします。
TCPサーバーの動作を確認するアプリ
サーバーは自局のIPアドレスとポートを指定して「開始」ボタンをクリックするとクライアントからの接続要求を待機します。
クライアントからの接続要求を受け入れると接続中のクライアントのリストボックスにクライアントのIPアドレスとポートを表示します。
接続中のクライアントがある場合、リストボックスのIPアドレスとポートを選択して「送信」ボタンを押すと横のテキストボックスに入力した文字列をクライアントに送信します。
クライアントから受信したパケットのデータは「テキストクリア」ボタンの上のテキストボックスに表示して確認できるようにします。
コントロールの実装
ツールボックスからコントロールを追加し、一部のプロパティを変更します。コントロールの名称はイラストの通りの番号の配置とします。コントロールのサイズとロケーションは任意で問題ありませんが入力する文字が確認できる最低限のサイズを指定することを推奨します。
GroupBoxは複数のコントロールをまとめて管理する場合に使用します。GroupBox3を移動するとGroupBox3内のすべてのコントロールが同時に移動します。
GroupBoxに内部のコントロールはグループ化しているGroupBoxの左上端がロケーションの基準になります。例えばGroupBox1はGroupBox3の内部に配置しているためロケーションはGroupBox3の左上端になります。
GroupBox1のTextプロパティを「接続中のクライアント」、GroupBox3のTextプロパティを「操作盤」、GroupBox5のTextプロパティを「ポート」、GroupBox6のTextプロパティを「接続元」に変更します。
Button1はサーバーの開始に使用します。Textプロパティを「開始」に変更し、BackColorプロパティをWebタブからLightGreenに変更します。Button2はサーバーの停止に使用します。Textプロパティを「停止」に変更し、BackColorプロパティをWebタブからLightBlueに変更します。
Button3はTextBox7のテキストのクリアに使用します。Textプロパティを「テキストクリア」に変更し、BackColorプロパティをWebタブからLightGreenに変更します。
パケットの受信とTCP接続を管理するためTimer1及びTimer2を追加します。タイマの使用方法は下記記事にまとめています。
VB.NET(VB2022)のタイマーの実装とイベントの追加方法
Timer1は受信パケットの表示の応答性を良くするためインターバルを100msにしています。Timer2はTCP接続確認とソケットの破棄を判断に使用し、インターバルを1000ms(1秒)しています。
変数の宣言
アプリで共通する変数を宣言する場合はフォームクラス内で変数を宣言する必要があります。VBの場合はDimでメモリに変数を割り当てます。
Imports System.Net.Sockets
Public Class Form1
Dim ServerTCP As TcpListener
End Class
Form1クラス直前でインポート(Imports)しているものはネームスペースを使用する意味です。4行目のServerTCPはSystem.Net.SocketsネームスペースのTcpListenerクラスでインスタンス化する宣言になります。インポートしない場合はSystem.Net.Socketsと記述する必要がありますが、名前の簡素化に従って省略するとネームスペースが自動で追加されます。
例ではアプリ全体で使用するServerTCPをインスタンス化しています。クラス内で宣言した場合はアプリを終了しない限りメモリを割り当てた状態になります。ServerTCPはクライアント接続待ちや接続受け入れ後のパケットの送受信に使用します。
PR: わからないを放置せず、あなたにあったスキルを身に着けるコツを教える テックジムPython入門講座の申込
イベントの追加
コントロールに対してイベントを追加します。エディターを選択している状態でF7を押すか、右クリックで表示されるメニュー「コードの表示(C)」を選択するとコードの編集画面(Form1.vb)に遷移します。次のイベントを追加します。
- フォームを呼び出したときに一度だけ処理するLoadイベント
- Button1~3をクリックしたときに呼び出すClickイベント(サーバーの開始と停止はTCPサーバーの開始と停止参照)
- ComboBox1で選択したインデックス番号を取得するSelectedIndexChangedイベント
- ComboBox1をクリックしたときに呼び出すMouseClickイベント
- ListBox1をクリックしたときに呼び出すSelectedIndexChangedイベント
- Timer1及びTimer2のTick(インターバル)イベント
1~3、5のイベントはフォームをダブルクリックすると追加されます。6のイベントはエディターのタイマのコンポーネント(タイマのアイコン)をダブルクリックすると追加されます。4のイベントはコードエディターで追加します。イベントの処理の内容はソースコード全体を確認してください。
コントロール選択でComboBox1を選択します。コントロール選択の横にあるイベント一覧からMouseClickを選択するとイベントが追加できます。イベントを選択すると自動でイベントの追加されます。
Private Sub ComboBox1_MouseClick(sender As Object, e As EventArgs) Handles ComboBox1.MouseClick
ComboBoxInit()
End Sub
自動生成されたサブルーチンの後方にHandles ComboBox1.MouseClickのように記述されている部分がイベントの発生要因になります。生成したイベントのサブルーチンに処理を追加します。例ではComboBoxを初期化する処理を追加しています。
TCPサーバーの開始と停止
WiFiはノートパソコンであれば標準搭載されていますが、動作確認に使用しているデスクトップパソコンにはWiFiを搭載していないためルータを介してESP32と通信を行います。
ESP32と接続するルータ(アクセスポイント)にはWZR-HP-G300NH(バッファロー:生産中止)を使用します。パソコンとアクセスポイントをLANケーブルで接続します。
ESP32を2台準備し、サーバーに接続要求をおこないます。サーバーはクライアントの接続を受け入れると「接続中のクライアント」にクライアントのIPアドレスとポートを表示します。
接続中のクライアントがあれば接続しているソケットを維持しますが、接続後一定時間データの受信がない場合は、ソケットを破棄してサーバーの負担を減らすようにします。
サーバーを開始する
System.Net.Socketsのネームスペースを使用します。自局のIPアドレスとポートを使用してサーバーを開始します。Button1のクリックイベントに処理を追加します。
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
If Button1.BackColor = Color.LightGreen Then
localPort = TextBox8.Text
Try
ServerTCP = New TcpListener(localIp, localPort)
'//クライアント接続要求の受付開始
ServerTCP.Start()
ServerTCP.BeginAcceptTcpClient(AddressOf TcpClientListen, ServerTCP)
Catch ex As Exception
End Try
End If
End Sub
localIPは存在するイーサーネットを確認して取得したIPを格納した変数です。自局のIPを指定する場合IPAddressクラスのLoopbackフィールドで指定することもできます。localPortはサーバーを開始するポートを指定するための変数です。
TcpListenerクラスを第1引数に自局のIPを指定します。第2引数は自局のポートを指定してServerTCPでインスタンス化して初期化します。以降はServerTCPオブジェクトを使用してTCP通信の管理を行います。
ServerTCPオブジェクト(TcpListenerクラス)のStart()メソッドでサーバーを開始します。処理が失敗した時、アプリが強制終了しないようにTry Catch文で処理を行います。
BeginAcceptTcpClient()メソッドで非同期でクライアントからの接続要求を受け入れる待機を行います。
第1引数にコールバック関数を指定します。AddressOfで非同期でコールバックするAsyncCallback型のプロシージャ(サブルーチンを呼び出し可能にしたもの)を生成してサブルーチンを指定しています。第2引数にTCP接続を管理しているオブジェクトを指定します。
非同期で接続要求を受け入れる待機を行うため待機中にアプリが固まることなく処理を行うことができます。
Private Sub TcpClientListen(AR As IAsyncResult)
Try
TcpServer(conectcnt).client = ServerTCP.EndAcceptTcpClient(AR)
TimClose(conectcnt) = TIME_CLOSE
If conectcnt < CONECT_CLIENT_MAX - 1 Then
conectcnt += 1
Else
conectcnt = 0
End If
ServerTCP.BeginAcceptTcpClient(AddressOf TcpClientListen, ServerTCP)
Catch ex As Exception
End Try
End Sub
接続を受け入れるとBeginAcceptTcpClient()メソッドで指定したコールバック関数に遷移するのでTcpListenerクラスのEndAcceptTcpClient()メソッドで接続要求を受け入れます。
クライアント情報が引数(AR)に格納されているため引数で指定し、新しいクライアントとしてインスタンス化した結果をTcpServer()に格納します。TcpServer()はサーバーが受け入れたクライアント情報を格納する変数です。
TimCloseはクライアントからデータを受信するまでを管理するタイマです。一定時間(30秒)経過するとクライアントから受信するデータが無いと判断してクライアントを破棄するために使用します。
続けて他のクライアントからの接続の受け入れるため、再度BeginAcceptTcpClient()メソッドdで待機します。
サーバーを停止する
Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
ServerTCP.Stop()
End Sub
Button2をクリックするとサーバーを停止します。TcpListenerクラスのStop()メソッドでクライアントの接続待機を停止してサーバーを停止します。
パケットを受信する
TCP接続するとパケットの双方向通信ができるようになります。パケットのデータを取得はタイマのインターバルの100ms毎にGetStream()のBeginRead()メソッドを使用して行います。
Private Sub Timer1_Tick(sender As Object, e As EventArgs) Handles Timer1.Tick
For i = 0 To TcpServer.Length - 1
If TcpServer(i).name <> "" Then
If TcpServer(i).client.Connected Then
If TcpServer(i).client.Available Then
ReDim getByte(i)(TcpServer(i).client.Available)
TcpServer(i).client.GetStream().BeginRead(getByte(i), 0, TcpServer(i).client.Available, AddressOf ReceiveCallback, i)
End If
End If
End If
Next
End Sub
サーバーが管理するクライアント数(本記事では5個)に対してクライアントが存在する場合に受信待ちのメソッドを管理します。
TCP接続中のクライアントが存在し、読み取り可能なパケットが存在するかをAvailableプロパティで確認します。パケットが存在する場合、GetStream()のBeginRead()メソッドでデータを読み取ります。
BeginRead()メソッドは第1引数に読み取ったデータを格納する配列を指定します。第2引数は読み取り開始の位置を指定します。例では全てのデータを取得するため0を指定しています。
第3引数は読み取るデータのサイズを指定します。例ではAvailableで取得した読み取り可能なデータ数を指定しています。
第4引数はコールバック関数を指定します。AddressOfで非同期でコールバックするAsyncCallback型のプロシージャ(サブルーチンを呼び出し可能にしたもの)を生成してサブルーチンを指定しています。
第5引数はユーザー定義のオブジェクトを指定します。例では受信待ちするクライアントの番号を指定しています。
Private Sub ReceiveCallback(AR As IAsyncResult)
Dim i As Integer
' ソケット受信
For i = 0 To getByte(AR.AsyncState).Length - 1
RxRingSet(AR.AsyncState, getByte(AR.AsyncState)(i))
Next
End Sub
パケットを受信するとBeginRead()メソッドで指定したコールバック関数に遷移します。BeginRead()メソッドで指定したgetByte[]配列にパケットのデータが格納されているため表示用のデータに移し替えて格納しています。
getByte[]配列のサイズはAvailableでパケットのサイズを確認してRedimでリサイズしているため受信したデータのみを移し替えることができます。
ARはユーザー定義のオブジェクトになりますが、クライアント番号を指定しているためクライアントが格納している番号と一致したgetByte[]配列にデータを移すことができます。
PR:スキマ時間を有効に!スマホで学べる人気のオンライン資格講座【スタディング】まずは気になる講座を無料で体験しよう!
パケットを送信する
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 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
Button3をクリックするとTCP接続中のクライアントが存在している場合、TextBox6に入力している文字列を「UTF-8」でエンコーディングしてGetStream()のWrite()メソッドで送信します。
第1引数に送信するデータのアドレス(配列)、第2引数に送信するデータの先頭のオフセット値を指定します。例では配列をすべて送信するため0を指定しています。第3引数に送信するデータのサイズを指定します。
クライアントが存在しない場合、アプリが強制終了するため、クライアントが存在を確認してから切断処理を行います。
動作確認(デバッグ)
クライアントはESP32を2台使用して動作確認を行います。ESP32を起動したタイミングでピン21がGNDであればクライアント2として動作し、何も接続しない場合はクライアント1として動作開始します。
ESP32はアクセスポイントに接続します。接続に成功するとサーバーに対して接続要求を行います。サーバーが接続要求が受け入れると双方向通信可能になります。下記記事のソースコードを一部修正して文字列を送信します。
ESP32-WROOM-32EでTCPクライアントを実装する
if( difilt.di[DI1] == LOW ){
if( conflg == false){
conflg = true;
//client.println("\n");//crlfを送信(コメントアウト)
/*以下を追加 */
if( digitalRead(PIN_DI1)){
client.println("TcpClient-1");
}
else{
client.println("TcpClient-2");
}
/* ここまで */
}
}
コメントアウトの部分を削除して/*以下を追加*/から/*ここまで*/のソースを追加します。
追加したソースコードによりピン5を操作するとクライアント1は「TcpClient-1」、クライアント2は「TcpClient-2」の文字列を送信するようになります。
動作確認のためデバッグを行います。デバッグの開始はエディタ上部の「▶開始」をクリックすると開始します。
アプリのComboBox1で使用可能なイーサーネットを選択します。次にサーバーを開始するポートを指定します。ESP32はポート80に対して接続要求を出すためポートを80にします。
「開始」ボタンをクリックするとサーバーが開始し、クライアントからの接続待機になります。
サーバーが接続要求を受け入れるとリストボックスにクライアントのIPアドレスとポートを表示します。この状態でESP32のピン5を操作するとクライアント1であれば「TcpClient-1」、クライアント2であれば「TcpClient-2」の文字列を送信します。
サーバーが文字列を受信するとTextBox7に文字列を表示するためTCP通信がうまくできていることが確認できました。
リストボックスのクライアントを選択し「送信」ボタンをクリックするとTextBox6に入力した文字列のパケットをクライアントに送信できます。
サーバーからクライアントにデータが送信できているかを確認するためにESP32のシリアルモニターで確認します。
TextBox6に「testあいうえお」を入力して「送信」ボタンをクリックするとシリアルモニターに「testあいうえお」が表示されており、サーバーから文字列が受信できていることが確認できます。
「停止」ボタンをクリックするとサーバーを停止します。クライアントからの接続要求や受信が途絶えるため一定時間後にソケットを破棄してリストボックスからクライアントが削除されたことで停止できていることが確認できます。
コードのデバッグを行う場合は、コードエディタでブレークポイントを置くことで一時的にプログラムを停止させることができます。
ブレークポイントはコードエディターの最左部分をクリックして●マークが表示されれば設置できています。例では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
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
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
Try
ServerTCP = New TcpListener(localIp, localPort)
'//クライアント接続要求の受付開始
ServerTCP.Start()
ServerTCP.BeginAcceptTcpClient(AddressOf TcpClientListen, ServerTCP)
Button1.BackColor = Color.LightBlue
Button2.BackColor = Color.LightGreen
Catch ex As Exception
End Try
End If
End Sub
'非同期接続後コールバック
Private Sub TcpClientListen(AR As IAsyncResult)
Dim remote_ep As IPEndPoint
Dim ip() As Byte
Dim ipstr As String
Try
If TcpServer(conectcnt).client IsNot Nothing Then
TcpServer(conectcnt).client.Close()
End If
TcpServer(conectcnt).client = ServerTCP.EndAcceptTcpClient(AR)
remote_ep = TcpServer(conectcnt).client.Client.RemoteEndPoint
ip = remote_ep.Address.GetAddressBytes()
ipstr = ip(0) & "." & ip(1) & "." & ip(2) & "." & ip(3) &
":" & remote_ep.Port
TcpServer(conectcnt).name = ipstr
TimClose(conectcnt) = TIME_CLOSE
If conectcnt < CONECT_CLIENT_MAX - 1 Then
conectcnt += 1
Else
conectcnt = 0
End If
ServerTCP.BeginAcceptTcpClient(AddressOf TcpClientListen, ServerTCP)
Catch ex As Exception
End Try
End Sub
'非同期受信コールバック
Private Sub ReceiveCallback(AR As IAsyncResult)
Dim i As Integer
' ソケット受信
For i = 0 To getByte(AR.AsyncState).Length - 1
RxRingSet(AR.AsyncState, getByte(AR.AsyncState)(i))
Next
TimClose(AR.AsyncState) = TIME_CLOSE
End Sub
Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
If Button2.BackColor = Color.LightGreen Then
ServerTCP.Stop() 'TCPサーバー停止
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
For i = 0 To TcpServer.Length - 1
If TcpServer(i).name <> "" Then
If TcpServer(i).client.Connected Then
If TcpServer(i).client.Available Then
ReDim getByte(i)(TcpServer(i).client.Available)
TcpServer(i).client.GetStream().BeginRead(getByte(i), 0, TcpServer(i).client.Available, AddressOf ReceiveCallback,i)
End If
End If
End If
Next
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
TimClose(i) = TIME_OFF
TcpServer(i).name = ""
TcpServer(i).client.Close()
End If
Next
End Sub
End Class
TextBox8は数値以外の文字列を受け付けるため数値以外の入力を許可しないようにKeyPressイベントを追加実装しても良いと思います。KeyPressイベントについては下記記事を参考にしてください。
VB.NET(VB2022)のTextBoxの実装とイベントの追加方法
コントロールの番号やイベントハンドラーなどのプロパティ名などを変更している場合はソースコードのイベントハンドラーをお使いのイベントハンドラーに置き換えてください。
関連リンク
VB.NET(VB2022)のデスクトップアプリで動作確認したことを下記リンクにまとめています。
VB.NET(VB2022)のデスクトップアプリ開発でできること
マイクロソフトはVisual Studio以外にもVSCodeという便利なエディターを提供しています。下記リンクではVSCodeのインストールの仕方からC言語開発環境の作り方までをまとめています。
PR:無料トライアル実施中【PC専用】AIスライド資料作成ツールの利用:イルシル
最後まで、読んでいただきありがとうございました。