質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

89.72%

TcpClient接続

解決済

回答 4

投稿 編集

  • 評価
  • クリップ 2
  • VIEW 6,701
退会済みユーザー

退会済みユーザー

C#の勉強でサーバーとクライアント、2つのアプリを作りクライアントからtextBoxに入力した文字をサーバーに送信するというものです。
一通り完成し少しづつ改良しているのですが、行き詰ったので質問させていただきます。
今まで正常に動いていたクライアントのVer.はtextBoxに文字を入力し、送信ボタンを押す度に

 public void Send()
{
TcpClient client = new TcpClient();
 client.Connect("127.0.0.1", 9000);
・・・省略・・・
client.Close()
}


のようにTcpClientをnewしてIPアドレスの指定をしてソケットをClose()していました。
サーバー側は、接続を受け付けて、クライアントから文字の受信が終わればソケットをClose()としていました。
その後、無限ループでまた接続を受け付け→処理→Close()の繰り返しです。
これでは何度も同じ作業をすることになるので、クライアントのTcpClientのnewとIPアドレスの指定を一度だけして2回目以降は文字の送受信だけを行いたいと思い、下記のようにしてみました。

//メンバ変数
 TcpClient client = new TcpClient();

 public void Send(byte num, string text)
        {
            if(client.Connected != true)
            {
                client.Connect("127.0.0.1", 9000);                   //TCP/IP接続を行う
            }

            //通信ストリームの取得
            NetworkStream stream = client.GetStream();
            byte[] SendBuffer = Encoding.Unicode.GetBytes(num + text);
            stream.Write(SendBuffer, 0, SendBuffer.Length);

引数などは気にしないでください。
変更箇所は、メンバ変数としてTcpClientをnewして、IPアドレスの指定はif文で分岐、処理の最後にClose()をしない、という3か所です。
サーバー側の変更箇所はClose()をしない、という1か所だけです。

そしていざ送信をすると、1回目は普通に送信出来ますが、2回目以降が反応してくれません。
デバックしながら見てみると、クライアント側はちゃんと送信までできています。今回のソースコードでいうSendBufferにはちゃんとコードが収まっています。
しかし、サーバー側は受信した際に処理が行われる場所にデバックポイントを貼っているにもかかわらず、まったく反応していません。

送信はできているハズなのですが、どうにもわかりません。
調べてみると使い捨て???みたいな事が書いてありましたが私にはよくわかりませんでした。

以上、どうすれば2回目以降も送受信ができるのか教えてください。
よろしくお願いいたします。

サーバーのコードです。(今回必要と思う箇所だけ抜粋)

 //// 接続待ち開始ボタンのクリックイベント
        private void button1_Click(object sender, EventArgs e)
        {
            if (SLTAlive == false)
            {
                // スレッド終了指示フラグを未終了に設定
                SLTAlive = true;

                // 接続待ち用スレッドを作成
                ListeningCallbackThread =new Thread(Start);

                // 接続待ち用スレッドを開始
                ListeningCallbackThread.Start();

            }
        }


        //=========================================

//接続要求受け入れ開始
        public void Start()
        {

            // 接続要求受け入れ開始
            server.Start();
            form.label1.Text = "サーバー開始";

            try
            {
                // 受信の受付を行なうための無限ループ

                while (form.SLTAlive == true)     // スレッド終了指示フラグでの終了指示がある場合はループ終了
                {
                    // 受信接続キュー内で、接続待ちがあるか判断
                    if (server.Pending() == true)
                    {
                        // クライアントからの接続を受け付ける
                        TcpClient ClientSocket = server.AcceptTcpClient();

                        // 通信ストリームの取得
                        NetworkStream stream = ClientSocket.GetStream();

                        //Receiveメソッドの呼び出し
                        Receive(stream);

                    }

                    // 短時間だけ待機

                    Thread.Sleep(100);

}
            }
            catch (Exception ex)
            {
                form.label1.Text = "サーバー終了";
            }

        }

        //=========================================

//クライアントからの受信
        public void Receive(NetworkStream stream)
        {
            // クライアントからの電文の受信
            byte[] ReceiveDate = new byte[2000];
            stream.Read(ReceiveDate, 0, ReceiveDate.Length);

            string str = new string(Encoding.Unicode.GetString(ReceiveDate, 0, ReceiveDate.Length).ToCharArray());          //オブジェクトの生成
            str = str.TrimEnd("\0".ToCharArray());

・・・以下省略・・・
        }
  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • YAmaGNZ

    2017/06/06 11:24

    送受信なのでクライアント側だけではなくサーバ側のソースもないと判断できません。

    キャンセル

  • 退会済みユーザー

    退会済みユーザー

    2017/06/06 11:43

    サーバーのソースを追加しました。よろしくお願いいたします。

    キャンセル

回答 4

checkベストアンサー

+1

サーバ側ですが

    // 受信接続キュー内で、接続待ちがあるか判断
    if (server.Pending() == true)
    {
        // クライアントからの接続を受け付ける
        TcpClient ClientSocket = server.AcceptTcpClient();

        // 通信ストリームの取得
        NetworkStream stream = ClientSocket.GetStream();

        //Receiveメソッドの呼び出し
         Receive(stream);

    }


とありますが、この状態では1回目のReceiveが呼び出された後、接続要求が来ない限り次のReceiveが
呼び出されません。
今回のクライアント側がCloseしないよう修正されたことで、接続要求は最初の1回目しか来ないことになります。

今回のようなコネクションを維持するような修正を行う場合、現状の
コネクション接続要求受付→受信→送信
というような順次処理を行うようなロジックはやめたほうがいいと思います。
イベントやコールバックで状態遷移をしっかり把握し接続要求や受信等を処理するようにしたほうがいいと思います。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/06/06 15:26

    つまりこのソースではできないということですよね・・
    クライアント側で送信する度にnewする→接続要求がある→AcceptTcpClientが返ってくる。
    クライアント側で送信する度にnewをしない→接続要求がない→AcceptTcpClientが返ってこない。

    BeginAcceptTcpClientを使えばこの問題は解決できますでしょうか?

    キャンセル

  • 2017/06/06 15:32 編集

    接続要求がある時のみAcceptTcpClientを実行すればいいのです。
    先ほど私が書いたように

    // 受信接続キュー内で、接続待ちがあるか判断
    if (server.Pending() == true)
    {
    // クライアントからの接続を受け付ける
    TcpClient ClientSocket = server.AcceptTcpClient();
    }

    とすればいいはずです。
    ただ、これだけだと、接続要求が来ていない時に受信ロジックに入ってエラーとなってしまう為、
    先ほど書いたように接続した時にフラグを立てて、そのフラグで受信処理を行うようにすれば
    いいはずです。


    途中での切断等を考慮しないのであれば
     
      // 接続要求受け入れ開始
      server.Start();
      form.label1.Text = "サーバー開始";

      // クライアントからの接続を受け付ける
      TcpClient ClientSocket = server.AcceptTcpClient();

      try
      {
        // 受信の受付を行なうための無限ループ
        while (form.SLTAlive == true) // スレッド終了指示フラグでの終了指示がある場合はループ終了
        {
    // 通信ストリームの取得
    NetworkStream stream = ClientSocket.GetStream();

    とAcceptTcpClientをループの外に出してしまうのも手ではあります。

    キャンセル

  • 2017/06/06 16:02

    できました!!
    何度も私の依頼にご対応していただき、ありがとうございました。
    これを基盤にして改良していきます。

    キャンセル

0

常にnewする処理を書くのが手間なら、newする処理を共通メソッド化してみたらどうでしょうか。
そうすれば、共通メソッドをcallするだけで開通できるので、コードもスッキリし、開発量も減ると思います。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

0

Closeはした方がいいのでは…
単純に1回しか待ち受けてないので1回目受け取ったら処理が終わってるだけのような気がしますが。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/06/06 11:18

    記載不足でした。すみません。
    サーバー側は無限ループをしていますのでクライアントからの受付を終了し処理が終われば、また受信するようにしています。

    キャンセル

  • 2017/06/06 11:21

    Closeをしてしまうと使い捨てになってしまうようなのでCloseをわざとしないようにしているのですが、できないのでしょうか?

    キャンセル

  • 2017/06/06 11:30 編集

    すみません、回答ではないのに回答の所に書いてしまった上に
    ちゃんと読めていなかったです、申し訳ない。

    TcpClient自体は closeしない で合ってるかと思います。
    NetworkStreamのことと勘違いしました。

    キャンセル

0

TCPの遷移のいい画像が無かったので、適当に貼っておきますが
コネクションの状態遷移
通信をする前に、通信を始めるシーケンスがあるのですが、そのTcpClientでは、newする時にその処理をするのではないでしょうか?

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 89.72%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

同じタグがついた質問を見る