TCP/IP 通信は確実にデータの送受信ができるので通信品質が高く、現在でも使用されている通信手段の1つとして使用されています。
C# で TCP/IP 通信を非同期処理でプログラムを実装する方法をこの記事にまとめていますので、参考にしてみてください。
環境
項目 | 内容 |
---|---|
OS | Windows10 |
IDE | Visual Studio 2019 |
フレームワーク | .NET Core3.1 |
UIフレームワーク | WPFアプリ |
オススメの参考書
C#の使い方が丁寧に解説しており、「基礎からしっかりと学びたい」という初心者の方にオススメの一冊です。サンプルコードも記載してあり、各章の最後に復習問題があるので理解度を確認しながら読み進めることができます。新しい C# のバージョンにも対応している書籍です。
TCP/IPの通信イメージ
まず TCP/IP 通信は、サービスや機能を利用するクライアントとサービスや機能を提供するサーバーが相互に接続しあってデータの送受信を行います。
C# ではクライアントとサーバーのプログラミングを簡単に実装できるように、Socket クラスをベースとした、TcpClient クラスと TcpListener クラスが用意されています。
この2つのクラスを利用した場合の通信イメージを理解しておくことで、プログラミングがスムーズに出来ると思いますので、下図の通信手順をみてみましょう。
- サーバー側はTcpListenerクラスで接続を開始して、クライアントからの要求を待ち受けします。
- クライアント側はTcpClientクラスで接続要求をし、サーバーに接続します。
- サーバーはクライアント側からの接続要求を受け付けると、TcpClientオブジェクトを作成して通信を確立します。
- 通信確立後、クライアントからリクエストを送信し、サーバーはリクエストを受信します。
- サーバーからレスポンスを送信し、クライアントはレスポンスを受信します。
- 最後にサーバーとクライアントのTcpClientの接続を閉じます。
今回のサンプルコードではクライアントとサーバー間で接続をして、クライアントから「Hello」を受信したら、「OK」を送信するプログラムを作成します。通信には非同期処理を使用します。
クライアント側の処理
クライアント側の処理を行う為に2つの関数を用意します。上図のフローチャート(通信処理の流れ)と比較しながら関数の役割を確認してみてください。
関数 | 役割 |
---|---|
StartClient | 接続先のエンドポイント(IPアドレスとポート)を指定する。サーバーへリクエストを送信し、サーバーからの応答を受信する。 |
サンプルソースコード
「tcpclient.cs」という新しい項目(ファイル)を作成して、プロジェクトに追加します。
ソケット関連で使用する名前空間System.Net.Sockets
を参照するために、using ディレクティブを用いてコードの先頭に記述します。
using System.Net.Sockets;
StartClient関数
先ほど作成した tcpclient.cs に記述するコードは以下になります。
public async Task<string> StartClient(string ipaddress, int port, string request)
{
var buffer = new byte[1024];
var response = "";
// サーバーへ接続
try
{
// クライアント作成
using (var tcpclient = new TcpClient())
{
// 送受信タイムアウト設定
tcpclient.SendTimeout = 1000;
tcpclient.ReceiveTimeout = 1000;
// サーバーへ接続開始
await tcpclient.ConnectAsync(ipaddress, port);
Debug.WriteLine("サーバーと通信確立");
using (var stream = tcpclient.GetStream())
{
// サーバーへリクエストを送信
buffer = System.Text.Encoding.ASCII.GetBytes(request);
await stream.WriteAsync(buffer);
Debug.WriteLine($"サーバーへ[{request}]を送信");
// サーバーからレスポンスを受信
var length = await stream.ReadAsync(buffer, 0, buffer.Length);
response = System.Text.Encoding.ASCII.GetString(buffer, 0, length);
Debug.WriteLine($"サーバーから[{response}]を受信");
}
}
}
catch (Exception ex)
{
Debug.WriteLine(ex.ToString());
}
return response;
}
new演算子を使用してTcpClient
のインスタンスを作成します。ConnectAsync
でアドレスとポート番号を指定して、非同期でサーバーへ接続します。
接続が確立されるまで次の処理は実行されず待機されます。実際に使用する際はタイムアウトの処理を記述することになります。
サーバーとデータの送受信を行う為に、ネットワークストリームの情報をGetStream
で取得します。サーバーへリクエストを送信する際はバイト型の配列にし、 WriteAsync
で非同期にて送信をします。リクエストを送信すると、サーバーからレスポンスがくるのでReadAsync
で受信をします。バイト型の配列で受信データが返ってくるのでアスキーコードに変換をします。
最後にTcpClient
を閉じれば処理は完了です。
サンプル使用例
上記で説明したサンプルソースコードを使用して、WPF でクライアントのアプリケーションを作成してみます。
IP アドレスとポート番号を入力して、送信開始ボタンをクリックすればサーバーと接続してリクエストを送信します。画面はマテリアルデザインを適用させてオシャレな画面に仕上げました。
マテリアルデザインの適用方法については、以下の記事で詳しく記載しています。
XAML のソースは以下の記述になります。
<Window
x:Class="TcpClinet.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
Title="TCP/IP (Server)"
Width="500"
Height="370"
WindowStyle="None"
mc:Ignorable="d">
<Grid>
<!-- ヘッダー -->
<materialDesign:ColorZone
Height="50"
Padding="12"
Mode="PrimaryMid">
<DockPanel>
<materialDesign:PopupBox DockPanel.Dock="Right" PlacementMode="BottomAndAlignRightEdges">
<ListBox>
<ListBoxItem Content="Close" />
</ListBox>
</materialDesign:PopupBox>
<StackPanel Orientation="Horizontal">
<materialDesign:PackIcon
Width="26"
Height="26"
Kind="AccessPoint" />
<TextBlock
Margin="16,0,0,0"
VerticalAlignment="Center"
Text="TCP/IP SAMPLE" />
</StackPanel>
</DockPanel>
</materialDesign:ColorZone>
<Label
Margin="15,61,0,0"
HorizontalAlignment="Left"
VerticalAlignment="Top"
Content="TCP/IP通信のクライアント側のサンプル画面です。" />
<!-- IPアドレス入力欄 -->
<Label
x:Name="lblIpaddress"
Margin="15,99,0,0"
HorizontalAlignment="Left"
VerticalAlignment="Top"
Content="IP アドレス:" />
<TextBox
x:Name="txtIpaddress"
Width="280"
Margin="90,86,0,0"
HorizontalAlignment="Left"
VerticalAlignment="Top"
materialDesign:HintAssist.Hint="IPアドレスを入力してください。"
Style="{StaticResource MaterialDesignFloatingHintTextBox}" />
<!-- ポート番号入力欄 -->
<Label
x:Name="lblPort"
Margin="15,141,0,0"
HorizontalAlignment="Left"
VerticalAlignment="Top"
Content="ポート番号:" />
<TextBox
x:Name="txtPort"
Width="280"
Margin="90,128,0,0"
HorizontalAlignment="Left"
VerticalAlignment="Top"
materialDesign:HintAssist.Hint="ポート番号を入力してください。"
Style="{StaticResource MaterialDesignFloatingHintTextBox}" />
<!-- 送信するデータ入力欄 -->
<Label
x:Name="lblSendData"
Margin="15,183,0,0"
HorizontalAlignment="Left"
VerticalAlignment="Top"
Content="送信データ:" />
<TextBox
x:Name="txtSendData"
Width="280"
Margin="90,170,0,0"
HorizontalAlignment="Left"
VerticalAlignment="Top"
Text="Hello"
materialDesign:HintAssist.Hint="送信するデータを入力してください。"
Style="{StaticResource MaterialDesignFloatingHintTextBox}" />
<!-- 送信開始ボタン -->
<Button
x:Name="btnStart"
Width="90"
Height="30"
Margin="0,177,10,0"
HorizontalAlignment="Right"
VerticalAlignment="Top"
Click="BtnStart_Click"
Content="送信開始"
Foreground="Black"
Style="{StaticResource MaterialDesignOutlinedButton}" />
<!-- ログ出力欄 -->
<TextBox
x:Name="txtLog"
Margin="10,215,10,10"
AcceptsReturn="True"
IsEnabled="{Binding ElementName=MaterialDesignOutlinedTextBoxEnabledComboBox}"
Style="{StaticResource MaterialDesignOutlinedTextBox}"
TextWrapping="Wrap"
VerticalScrollBarVisibility="Auto" />
</Grid>
</Window>
コードは以下の記述になります。
public partial class MainWindow : Window
{
tcpclient tcp = new tcpclient();
public MainWindow()
{
InitializeComponent();
}
private async void btnStart_Click(object sender, RoutedEventArgs e)
{
var responce = await tcp.StartClient(txtIpaddress.Text, int.Parse(txtPort.Text), txtSendData.Text);
MessageBox.Show(responce);
}
}
動作確認
サーバー側とクライアント側のアプリケーションをそれぞれ起動します。
クライアントアプリにIPアドレスとポート番号を入力してから送信開始ボタンをクリックして、サーバーへ接続要求をしてみましょう。
上図の結果から分かるようにTCP/IP通信でデータの送受信ができていることが確認できました。
まとめ
クライアント側のTCP/IP通信について記載しました。
この記事ではTcpClientクラスで説明しましたが、Soketクラスを使うことでTCP/IP通信することもできますので、興味がある方は以下の記事も読んでみてください。
以上、最後まで読んでいただきありがとうございました。