TCP/IP通信は確実にデータの送受信ができるので通信品質が高く、現在でも使用されている通信手段の1つとして使用されています。
C#でTCP/IP通信を非同期処理でプログラムを実装する方法をこの記事にまとめていますので、参考にしてみてください。
環境
OS | Windows10 |
IDE | Visual Studio 2019 |
フレームワーク | .NET Core3.1 |
UIフレームワーク | WPFアプリ |
TCP/IPの通信イメージ
まずTCP/IP通信は、サービスや機能を利用するクライアントとサービスや機能を提供するサーバーが相互に接続しあってデータの送受信を行います。
C#ではクライアントとサーバーのプログラミングを簡単に実装できるように、Socketクラスをベースとした、TcpClientクラスとTcpListenerクラスが用意されています。
この2つのクラスを利用した場合の通信イメージを理解しておくことで、プログラミングがスムーズに出来ると思いますので、下図の通信手順をみてみましょう。
- サーバー側はTcpListenerクラスで接続を開始して、クライアントからの要求を待ち受けします。
- クライアント側はTcpClientクラスで接続要求をし、サーバーに接続します。
- サーバーはクライアント側からの接続要求を受け付けると、TcpClientオブジェクトを作成して通信を確立します。
- 通信確立後、クライアントからリクエストを送信し、サーバーはリクエストを受信します。
- サーバーからレスポンスを送信し、クライアントはレスポンスを受信します。
- 最後にサーバーとクライアントのTcpClientの接続を閉じます。
今回のサンプルコードではクライアントとサーバー間で接続をして、クライアントから「Hello」を受信したら、「OK」を送信するプログラムを作成します。通信には非同期処理を使用します。
クライアント側の処理
クライアント側の処理を行う為に2つの関数を用意します。上図のフローチャート(通信処理の流れ)と比較しながら関数の役割を確認してみてください。
関数 | 役割 |
StartClient | 接続先のエンドポイント(IPアドレスとポート)を指定する。サーバーへリクエストを送信し、サーバーからの応答を受信する。 |
サンプルソースコード
「tcpclient.cs」という新しい項目(ファイル)を作成して、プロジェクトに追加します。
using System.Net.Sockets;
StartClient関数
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;
}

サーバーとデータの送受信を行う為に、ネットワークストリームの情報を GetStream 取得します。サーバーへリクエストを送信する際はバイト型の配列にし、 WriteAsync で非同期にて送信をします。リクエストを送信すると、サーバーからレスポンスがくるのでReadAsyncで受信をします。バイト型の配列で受信データが返ってくるのでアスキーコードに変換をします。
最後にTcpClientを閉じれば処理は完了です。
サンプル使用例
上記で説明したサンプルソースコードを使用して、WPFでクライアントのアプリケーションを作成してみます。
IPアドレスとポート番号を入力して、送信開始ボタンをクリックすればサーバーと接続してリクエストを送信します。画面はマテリアルデザインを適用させてオシャレな画面に仕上げました。

<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通信について記載しました。

以上、最後まで読んでいただきありがとうございました。
コメント