C#

【C#】TcpListenerでサーバーの通信処理を作成

TCP/IP通信は確実にデータの送受信ができるので通信品質が高く、現在でも使用されている通信手段の1つとして使用されています。

C#でTCP/IP通信を非同期処理でプログラムを実装する方法をこの記事にまとめていますので、参考にしてみてください。

環境

OSWindows10
IDEVisual Studio 2019
フレームワーク.NET Core3.1
UIフレームワークWPFアプリ

TCP/IPの通信イメージ

まずTCP/IP通信は、サービスや機能を提供するサーバーとサービスや機能を利用するクライアントが相互に接続しあってデータの送受信を行います。

C#ではサーバーとクライアントのプログラミングを簡単に実装できるように、Socketクラスをベースとした、TcpListenerクラスとTcpClientクラスが用意されています。

この2つのクラスを利用した場合の通信イメージを理解しておくことで、プログラミングがスムーズに出来ると思いますので、下図の通信手順をみてみましょう。

  1. サーバー側はTcpListenerクラスで接続を開始して、クライアントからの要求を待ち受けします。
  2. クライアント側はTcpClientクラスで接続要求をし、サーバーに接続します。
  3. サーバーはクライアント側からの接続要求を受け付けると、TcpClientオブジェクトを作成して通信を確立します。
  4. 通信確立後、クライアントからリクエストを送信し、サーバーはリクエストを受信します。
  5. サーバーからレスポンスを送信し、クライアントはレスポンスを受信します。
  6. 最後にサーバーとクライアントのTcpClientの接続を閉じます。

今回のサンプルコードではサーバーとクライアント間で接続をして、クライアントから「Hello」を受信したら、「OK」を送信するプログラムを作成します。通信には非同期処理を使用します。

サーバー側の処理

サーバー側の処理を行う為に2つの関数を用意します。上図のフローチャート(通信処理の流れ)と比較しながら関数の役割を確認してみてください。

関数役割
StartListening指定されたIPアドレスとポートを、TCP/IP通信のソケットのエンドポイントとしてTcpListenerを作成する。クライアントの接続要求を受け入れ、通信を確立させる。
ReceiveAsyncクライアントからのレスポンスデータを受信する。受信がすべて完了したら、クライアントへ受信が完了したことを知らせる為にレスポンスを送信する。

サンプルソースコード

「tcpserver.cs」という新しい項目(ファイル)を作成して、プロジェクトに追加します。

ソケット関連で使用する名前空間 System.Net.Sockets System.Net を参照するために、usingディレクティブを用いてコードの先頭に記述します。
using System;
using System.Net.Sockets;
using System.Net;

StartListening関数

先ほど作成したtcpserver.csに記述するコードは以下になります。
public async Task>bool> StartListening(int port)
{
    // 接続を待つエンドポイントを設定
    IPEndPoint localEndPoint = new IPEndPoint(IPAddress.Any, port);

    // TcpListenerを作成
    var tcpServer = new TcpListener(localEndPoint);

    try
    {
        // 待ち受け開始
        tcpServer.Start();

        while (true)
        {
            Debug.WriteLine("接続待機中...");

            // 非同期で接続要求を待機
            using (var tcpClient = await tcpServer.AcceptTcpClientAsync())
            {
                Debug.WriteLine("クライアントからの接続要求を受け入れ");

                // クライアントからデータを受信
                var request = await ReceiveAsync(tcpClient);
            }

            Debug.WriteLine("接続終了");
        }
    }
    catch (Exception ex)
    {
        Debug.WriteLine(ex.ToString());
    }

    return false;
}

IPアドレスとポート番号を指定して、エンドポイントを設定します。IPアドレスは、  IPAddress.Any として、どんなIPアドレスも受け付けるようにします。ポート番号は引数で渡すことで変更できるようにしました。

指定したローカルエンドポイントを使用して TcpListener のインスタンスを初期化します。

非同期処理の AcceptTcpClientAsync でクライアントからの接続要求があるまで待機します。接続要求があるまで次の処理は実行されることがありません。

今回は while で何度もクライアントと接続をし続けるようにしています。

ReceiveAsync関数

次に以下のコードをtcpserver.csに記述します。
public async Task<string> ReceiveAsync(TcpClient tcpClient)
{
    byte[] buffer = new byte[1024];
    string request = "";

    try
    {
        using (NetworkStream stream = tcpClient.GetStream())
        {
            // クライアントからリクエストを受信する
            do
            {
                int byteSize = await stream.ReadAsync(buffer, 0, buffer.Length);
                request += Encoding.UTF8.GetString(buffer, 0, byteSize);
            }
            while (stream.DataAvailable);
            Debug.WriteLine($"クライアントから「{request}」を受信");

            // クライアントへレスポンスを送信する
            var response = "OK";
            buffer = Encoding.ASCII.GetBytes(response);

            await stream.WriteAsync(buffer, 0, buffer.Length);
            Debug.WriteLine($"クライアントへ「{response}」を送信");
        }
    }
    catch (Exception ex)
    {
        Debug.WriteLine(ex.ToString());
    }

    return request;
}

TcpClientでデータの送受信をする際はNetworkStreamクラスを使います。このクラスはネットワークを読み書きの対象とするストリーム(ストリームとは順番に流れてくるデータのこと)です。

サンプル使用例

上記で説明したサンプルソースコードを使用して、WPFでサーバーのアプリケーションを作成してみます。

ポート番号を入力して、受信開始ボタンをクリックすればクライアントと接続されるまで待機します。画面はマテリアルデザインを適用させてオシャレな画面に仕上げました。

マテリアルデザインの適用方法については、以下の記事で詳しく記載しています。

【WPF】Material Designでオシャレな画面デザインに変更する方法WPFでオシャレなUI画面にするならMaterial Design In XAML Toolkitをオススメします。Material DesignとはGoogleが提唱したデザインシステムで、自作のUI画面に簡単に適用できます。Nugetからパッケージをインストールして適用するまでの手順とデモアプリのインストール方法を紹介します。...
XAMLのソースは以下の記述になります。
<Window
    x:Class="TcpServer.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="320"
    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通信のサーバー側のサンプル画面です。" />

        <!--  ポート番号入力欄  -->
        <Label
            x:Name="lblPort"
            Margin="15,98,0,0"
            HorizontalAlignment="Left"
            VerticalAlignment="Top"
            Content="ポート番号:" />
        <TextBox
            x:Name="txtPort"
            Width="185"
            Margin="90,85,0,0"
            HorizontalAlignment="Left"
            VerticalAlignment="Top"
            materialDesign:HintAssist.Hint="ポート番号を入力してください。"
            Style="{StaticResource MaterialDesignFloatingHintTextBox}" />

        <!--  受信開始ボタン  -->
        <Button
            x:Name="btnStart"
            Width="90"
            Height="30"
            Margin="0,92,10,0"
            HorizontalAlignment="Right"
            VerticalAlignment="Top"
            Click="BtnStart_Click"
            Content="受信開始"
            Foreground="Black"
            Style="{StaticResource MaterialDesignOutlinedButton}" />

        <!--  ログ出力欄  -->
        <TextBox
            x:Name="txtLog"
            Margin="10,140,10,10"
            AcceptsReturn="True"
            IsEnabled="{Binding ElementName=MaterialDesignOutlinedTextBoxEnabledComboBox}"
            Style="{StaticResource MaterialDesignOutlinedTextBox}"
            TextWrapping="Wrap"
            VerticalScrollBarVisibility="Auto" />
    </Grid>
</Window>
コードは以下の記述になります。
using System.Windows;

namespace TcpServer
{
    public partial class MainWindow : Window
    {
        tcpserver tcp = new tcpserver();

        public MainWindow()
        {
            InitializeComponent();
        }

        private async void BtnStart_Click(object sender, RoutedEventArgs e)
        {
            btnStart.IsEnabled = false;
            if (!await tcp.StartListening(int.Parse(txtPort.Text)))
            {
                btnStart.IsEnabled = true;
            }
        }
    }
}

動作確認

サーバー側とクライアント側のアプリケーションをそれぞれ起動します。

【C#】ソケット通信(非同期)でクライアントの処理を記述する方法非同期でクライアント側のソケット通信を実装する方法について記載しています。本記事で紹介しているプログラム言語はC#です。ソケット通信について詳しく解説していきますので、参考にして下さい。...

サーバーアプリにポート番号を入力してから受信開始状態にし、クライアントアプリからサーバーへ接続要求をしてみましょう。

TCP/IP通信でデータの送受信ができていることが確認できました。

まとめ

サーバー側のTCP/IP通信について記載しました。この記事ではTcpListenerクラスで説明しましたが、Soketクラスを使うことでTCP/IP通信することもできます。

【C#】 TCP通信を非同期ソケットで使う方法(サーバー編)C#でTCP/IP通信を非同期でプログラムを実装する方法について記載しています。今回はサーバーの通信サンプルを解説していますので、参考にしてみてください。ソケットやポート番号について詳しく説明しています。...

IoTが進む中、ありとあらゆる物が今後ネットワークに接続していくことが想像されます。ネットワークを用いた通信は今後も扱う機会が増えてくると思いますので、TCP/IP通信以外の通信方法も学んでいこうと思います。

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

プログラミングを学習したいなら…

プログラミングスキルを身に付けるなら、プログラミングを効率良く学べる
プログラミングスクール」がオススメです。

特にこんな方にオススメ!!
これからエンジニアを目指したい
プログラミングの専門性を高めたい
プログラミングを学んで副業をしたい
エンジニアに転職して年収をアップさせたい

プログラミングを触ったことがない未経験からでも、プログラミングスクールで学習すれば、エンジニアへ就職・転職することも可能です。

あなたの「行動力」と「やる気」で、あなたの人生を大きく変えるチャンスになることでしょう。

プログラミングスクールに興味がある方は是非チェックしてみてください。

> プログラミングを学ぶ <

COMMENT

メールアドレスが公開されることはありません。

CAPTCHA