気になった事を自由に書いてます。
      
TCP/IP タイムアウトを設定して受信すると・・・①
今日はTCP/IPの通信でミスをしたのでその内容を…。
作りたいプログラムの概要はクライアントから電文(要求)を送信し、 サーバーから電文(応答)を受信する単純な通信プログラムです。

特に意識せず作ったので←間違った考え><。
クライアントは電文を送信した後に受信するという簡単なロジックを実装し テストしてみると期待どおりに動いているように見えた。

AAA→
   ←AAA

次に、サーバーから電文が返ってこないときのテストを行いました。
期待する動きは一定時間経過してもサーバーから電文が返ってこないので
タイムアウトと判断し、クライアントは同じ電文を再送。
でも、いくら待っても再送せず・・・。あれ・・というか画面の応答がない…。

TCPServer
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;

using System.Net;
using System.Net.Sockets;
using System.Threading;

namespace TCPServer
{
    public partial class Form1 : Form
    {
        private TcpListener _listener = null;

        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            Thread thread = new Thread(ReceiveProc);
            thread.Start();
        }

        private void ReceiveProc()
        {
            try
            {
                // リスナーを作成する
                _listener = new TcpListener(IPAddress.Parse("192.168.24.52"), 4567);

                // リスナーを開始
                _listener.Start();
                Console.WriteLine("サーバーを開始しました");

                // クライアントからの接続待ち
                TcpClient client = _listener.AcceptTcpClient();

                NetworkStream ns = client.GetStream();

                while (true)
                {
                    // 受信データの読み出し
                    byte[] buf = new byte[100];
                    int len = ns.Read(buf, 0, buf.Length);
                    string s = System.Text.Encoding.UTF8.GetString(buf, 0, len);
                    Console.WriteLine("受信:" + s);

                    // データを送信します。
                    if (s == "AAA")
                    {
                        ns.Write(buf, 0, len);
                    }

                }
                client.Close();
            }
            catch (Exception ex)
            {
                Console.WriteLine("サーバーを終了しました");
            }
        }
    }
}

TCPClient
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;

using System.Net;
using System.Net.Sockets;
using System.Threading;

namespace TCPClient
{
    public partial class Form1 : Form
    {
        private TcpClient _tcp = null;
        private NetworkStream _ns = null;

        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            try
            {
                // TCP/IP接続
                _tcp = new TcpClient("192.168.24.52", 4567);

                // ストリーム取得
                _ns = _tcp.GetStream();
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
        }

        private void button2_Click(object sender, EventArgs e)
        {
            // 送信データ
            string sendText = "AAA";

            // 送信データをByte型配列に変換
            byte[] sendByte = System.Text.Encoding.UTF8.GetBytes(sendText);

            // データ送信
            _ns.Write(sendByte, 0, sendByte.Length);

            // 受信開始
            timer1.Start();
        }
        
        private void button3_Click(object sender, EventArgs e)
        {
            // 送信データ
            string sendText = "BBB";

            // 送信データをByte型配列に変換
            byte[] sendByte = System.Text.Encoding.UTF8.GetBytes(sendText);

            // データ送信
            _ns.Write(sendByte, 0, sendByte.Length);

            // 受信開始
            timer1.Start();
        }

        private void timer1_Tick(object sender, EventArgs e)
        {
            // 受信停止
            timer1.Stop();

            // 受信データの読み出し
            byte[] buf = new byte[100];
            int len = _ns.Read(buf, 0, buf.Length);
            string s = System.Text.Encoding.UTF8.GetString(buf, 0, len);
            Console.WriteLine("受信:" + s);

        }
    }
}

※サンプルなので再送の仕組みは入ってません。

問題の箇所は
78行目の int len = _ns.Read(buf, 0, buf.Length); でした。

どうやら、サーバーからデータが来るまでずっと待ってるみたいで
メインスレッドで処理していると画面が固まってしまいます。
だとすると、Read()にタイムアウトを値を持たせればクリアするのでは?
という考えから、以下のように変更しました。
テストコード
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

private void timer1_Tick(object sender, EventArgs e)
{
    try
    {
        // 受信停止
        timer1.Stop();

        // 受信データの読み出し
        byte[] buf = new byte[100];
        _ns.ReadTimeout = 2000;
        int len = _ns.Read(buf, 0, buf.Length);
        string s = System.Text.Encoding.UTF8.GetString(buf, 0, len);
        Console.WriteLine("受信:" + s);
    }
    catch (Exception ex)
    {
        Console.WriteLine(ex.Message);
    }
}

として動かすと
転送接続からデータを読み取れません: 接続済みの呼び出し先が一定の時間を過ぎても正しく応答しなかったため、接続できませんでした。または接続済みのホストが応答しなかったため、確立された接続は失敗しました。。
という例外をキャッチするように・・・。どうやらタイムアウトで発生するようです。
"確立された接続は失敗しました"とでてますが、再接続しなくても次の電文を送信できます。

次は例外を発生させないようになんとか上手いことできないか考えて見ます。
      




Effective C# 4.0
ビル・ワグナー
¥ 3,780



Copyright (C) 2011 - 2017 猫の気ままなC#日記