0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Visual Studio | WPF > TCPサーバ > echo server > 1対N通信 > ThreadPool.QueueUserWorkItem()使用 v0.1, v0.2

Last updated at Posted at 2017-12-04
動作環境
Windows 8.1 Pro (64bit)
Microsoft Visual Studio 2017 Community
Sublime Text 2

Visual Studio | WPF > TCPサーバ > echo server > 1対1通信 >Thread処理 | link: 自分のIPアドレスを取得する

上記では一つのクライアント接続時、別のクライアント応答ができなかった。

以下を見つけた。
how do i get TcpListener to accept multiple connections and work with each one individually?

answered Mar 17 '11 at 13:46
Oleg Tarasov

ThreadPool.QueueUserWorkItem()を使った例が紹介されている。

code v0.1

実装してみた。

MainWindow.xaml
<Window x:Class="_171204_t1710_TCPechoServer.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:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:_171204_t1710_TCPechoServer"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <Button Name="B_connect" Content="Connect" Height="30" Click="B_connect_Click"/>
    </Grid>
</Window>
MainWindow.xaml.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
// 以下を追加した
using System.Threading;
using System.Net;
using System.Net.Sockets;
using System.IO;

namespace _171204_t1710_TCPechoServer
{
    /// <summary>
    /// MainWindow.xaml の相互作用ロジック
    /// </summary>
    public partial class MainWindow : Window
    {

        private Thread rcvThr;
        TcpListener tcpListener;
        private readonly int kPort_recv = 7000;
        private bool stopThr = false;

        public MainWindow()
        {
            InitializeComponent();
        }

        private void B_connect_Click(object sender, RoutedEventArgs e)
        {
            this.Title = "Connected";

            tcpListener = new TcpListener(IPAddress.Any, kPort_recv);
            tcpListener.Start();

            rcvThr = new Thread(new ThreadStart(FuncRcvData));
            rcvThr.Start();
        }

        private void FuncRcvData()
        {
            while (stopThr == false)
            {
                TcpClient client = tcpListener.AcceptTcpClient();
                ThreadPool.QueueUserWorkItem(ThreadProc, client);
            }
        }

        private static void ThreadProc(object obj)
        {
            var client = (TcpClient)obj;

            if (client.Connected == false)
            {
                return;
            }
            using (var netStream = client.GetStream())
            {
                using (var sReader = new StreamReader(netStream, Encoding.UTF8))
                {
                    var sWriter = new StreamWriter(netStream, Encoding.UTF8);

                    do
                    {
                        string str = sReader.ReadLine();
                        if (str == null)
                        {
                            break;
                        }
                        sWriter.WriteLine(str);
                        sWriter.Flush();
                        //
                        Console.WriteLine(str);
                    } while (true);
                    sWriter.Close();
                }
            }
            client.Close();

        }
    }
}

複数のクライアントから同時接続されても応答できるようになった。

関連

ThreadPool.QueueUserWorkItem メソッド

code v0.2

  • 受信処理でtry, catchを追加した。
    • これがない場合、処理によってはstring str = sReader.ReadLine();の部分で「xxxは動作を停止しました」になる。
  • スレッドが動いている場合の終了処理を追加
    • これをしないとListenボタン押下後に終了するとtcpListener.AcceptTcpClient();で処理が止まり、ソフトがゾンビ化する
    • Window_ClosingをXAMLで追加
    • Window_Closingを実装
    • tcpListerner.Stop()で処理を停止
MainWindow.xaml
<Window x:Class="_171205_t0900_TCPechoBackUI.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:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:_171205_t0900_TCPechoBackUI"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="525"
        Closing="Window_Closing">
    <Grid>
        <StackPanel>
            <Button Name="B_listen" Content="Listen" Height="30" Click="B_listen_Click"/>
            <TextBox Name="T_recv" Height="300" VerticalScrollBarVisibility="Visible"
                     Text="{Binding RecvString}"/>
        </StackPanel>
    </Grid>
</Window>
MainWindow.xaml.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
// 以下を追加した
using System.ComponentModel;
using System.Threading;
using System.Net;
using System.Net.Sockets;
using System.IO;

namespace _171205_t0900_TCPechoBackUI
{
    /// <summary>
    /// MainWindow.xaml の相互作用ロジック
    /// </summary>
    public partial class MainWindow : Window, INotifyPropertyChanged
    {
        private string _recvString = string.Empty;
        private Thread rcvThr;
        TcpListener tcpListener = null;
        private readonly int kPort_recv = 7000;
        private bool stopThr = false;

        public MainWindow()
        {
            InitializeComponent();

            this.DataContext = this;
        }

        public string RecvString
        {
            get { return _recvString; }
            set
            {
                _recvString = value;
                OnPropertyChanged("RecvString");
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;
        protected virtual void OnPropertyChanged(string propertyName = null)
        {
            if (PropertyChanged != null)
                PropertyChanged.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }

        private void B_listen_Click(object sender, RoutedEventArgs e)
        {
            if (tcpListener != null)
            {
                return; // error
            }

            this.Title = "Connected";

            tcpListener = new TcpListener(IPAddress.Any, kPort_recv);
            tcpListener.Start();

            rcvThr = new Thread(new ThreadStart(FuncRcvData));
            rcvThr.Start();
        }

        private void FuncRcvData()
        {
            while (stopThr == false)
            {
                try
                {
                    TcpClient client = tcpListener.AcceptTcpClient();
                    ThreadPool.QueueUserWorkItem(ThreadProc, client);
                }
                catch(Exception err)
                {

                }
            }
        }

        private void ThreadProc(object obj)
        {
            var client = (TcpClient)obj;

            if(client.Connected == false)
            {
                return;
            }
            using (var netStream = client.GetStream())
            {
                using (var sReader = new StreamReader(netStream, Encoding.ASCII))
                {
                    var sWriter = new StreamWriter(netStream, Encoding.ASCII);

                    while(stopThr == false)
                    {
                        try
                        {
                            Console.WriteLine("read start");
                            string str = sReader.ReadLine();
                            Console.WriteLine("read end");
                            if (str == null)
                            {
                                break;
                            }
                            sWriter.WriteLine(str);
                            sWriter.Flush();

                            string log = RecvString;
                            if (log.Length > 0)
                            {
                                log += System.Environment.NewLine;
                            }
                            RecvString = log + str;
                        }
                        catch (Exception err)
                        {
                            break;
                        }
                    }
                    sWriter.Close();
                }
            }
            client.Close();
        }

        private void Window_Closing(object sender, CancelEventArgs e)
        {
            if (tcpListener != null)
            {
                tcpListener.Stop();
            }
            stopThr = true;
        }
    }
}

エラー処理はもう少し勉強が必要だろう。

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?