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

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

ただいまの
回答率

90.47%

  • Unity

    4206questions

    Unityは、ユニティテクノロジーが開発したゲームエンジンです。 主にモバイルやブラウザ向けのゲーム製作に利用されていましたが、3Dの重力付きゲームが簡単に作成できることから需要が増え、現在はマルチプラットフォームに対応しています。 言語はC言語/C++で書かれていますが、C#、JavaScript、Booで書かれたコードにも対応しています。

  • TCP

    167questions

    TCP(Transmission Control Protocol)とは、トランスポート層のプロトコルで、コネクション型のデータサービスです。

UnityでTCPを利用したチャットのやりとりをできるようにしたい(C#)

解決済

回答 2

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 1,180

peter_kes

score 4

 前提・実現したいこと

UnityでTCPを利用したチャットのやりとりをできるようにしたいんです。C#
AcceptSocket(); を使っても
BeginAcceptTcpClient(
どちらを使ってもフリーズしてしまいます。何が間違っているのかわかりません。

 発生している問題・エラーメッセージ

Croutineでずっとリスニングループさせたりするとフリーズします。
ループさせないでAsyncCallbackの方法をするとSocket Error 対象のコンピュータによって拒否されたため、接続できませんでした。というエラーになります。

 該当のソースコード

(フリーズするダメなコード Server側)
int port = 2222;
TcpListener server = new TcpListener(System.Net.IPAddress.Any, port);

//AcceptSocket()を使った場合
Socket client = server.AcceptSocket(); //ブロッキングするのでここでフリーズ

//AsyncCallbackを使った場合
server.BeginAcceptTcpClient(new AsyncCallback(DoAcceptTcpClientCallback), server);
...
public static void DoAcceptTcpClientCallback(IAsyncResult ar) 
{
TcpListener listener = (TcpListener) ar.AsyncState; 
client = listener.EndAcceptTcpClient(ar);//ここでおそらくフリーズ
....


サーバー側修正後、ポートとIPなどがおかしいくてエラーSocketException: 対象のコンピューターによって拒否されたため、接続できませんでした。がでる Client側のコード
bool isForceStop = false;
NetworkStream stream = null;
bool isStopReading = false;
byte[] readbuf;

private IEnumerator Start(){
Debug.Log("START START");
readbuf = new byte[1024];

while (!isForceStop) {
if(!isStopReading)    {
StartCoroutine(ReadMessage ());
}
yield return new WaitForSeconds(1f);//あんまりしょっちゅうやらないために
}
}

void Update()
{
if (Input.GetKeyDown (KeyCode.A) == true) {
isForceStop = true;
}

}

public IEnumerator SendCurrentMessage(string message){

Debug.Log ("START SendMessage:" + message);

if (stream == null) {
stream = GetNetworkStream();
}
string playerName = "[A]: ";
//サーバーにデータを送信する
Encoding enc = Encoding.UTF8;
byte[] sendBytes = enc.GetBytes(playerName + message + "\n");
//データを送信する
stream.Write(sendBytes, 0, sendBytes.Length);
yield break;
}

//常駐
private IEnumerator ReadMessage(){
stream = GetNetworkStream ();
// 非同期で待ち受けする
stream.BeginRead (readbuf, 0, readbuf.Length, new AsyncCallback (ReadCallback), null);
isStopReading = true;
yield return null;
}

public void ReadCallback(IAsyncResult ar ){
Encoding enc = Encoding.UTF8;
stream = GetNetworkStream ();
int bytes = stream.EndRead(ar);
string message = enc.GetString (readbuf, 0, bytes);
message = message.Replace("\r", "").Replace("\n", "");
isStopReading = false;
Chat.Insntace.GetMessage (message);
}   

private NetworkStream GetNetworkStream(){
if (stream != null && stream.CanRead) {
return stream;
}
string ipOrHost = "192.168.0.4";
//string ipOrHost = "127.0.0.1"; 
int port = 2222;

//TcpClientを作成し、サーバーと接続する
TcpClient tcp = new TcpClient(ipOrHost, port);
//TcpClient tcp = new TcpClient(System.Net.IPAddress.Any.ToString(), port);

Debug.Log("success conn server");

//NetworkStreamを取得する
return tcp.GetStream();
}

 試したこと

Try chatch でエラーハンドルを入れても何も解決しません。
ネットの解決方法色々試しました。たとえば、while(!server.Pending())を入れてみたり、Thread.sleep(1000)をいれてみたり...

 補足情報(FW/ツールのバージョンなど)

Unityは2017.2.0f3

  • 気になる質問をクリップする

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

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

    クリップを取り消します

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

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

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

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

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

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

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

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

    質問の評価を下げる

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

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

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

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

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

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

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

    詳細な説明はこちら

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

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

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

回答 2

check解決した方法

+1

TCP での実装は解決いたしました!!

Server側
using System;
using System.Text;
using System.Net;
using System.Net.Sockets; 
using UnityEngine;
using System.Collections.Generic;
using System.Collections;
using System.Threading;
using System.Threading.Tasks;

public class Server : MonoBehaviour {

private TcpListener server = null;

bool isStop = false;
void Awake()
{
ServerStart ();
}

void Update()
{
if (Input.GetKeyDown (KeyCode.Q) == true) {
isStop = true;
}
}

public void ServerStart()
{
int port = 2222;
this.server = new TcpListener(System.Net.IPAddress.Any, port);
this.server.Start();
StartCoroutine (ServerContinue ());
}

IEnumerator ServerContinue()
{
while (isStop == false) {
this.server.BeginAcceptTcpClient (new AsyncCallback (this.DoAcceptTcpClientCallback), this.server);
yield return new WaitForSeconds (1f);
}
}

public void DoAcceptTcpClientCallback(IAsyncResult res) 
{
print ("something came...");
var listener = res.AsyncState as TcpListener;
var client = listener.EndAcceptTcpClient(res);

// 次のAcceptを非同期で呼び出し。
listener.BeginAcceptTcpClient(new AsyncCallback(this.DoAcceptTcpClientCallback), listener);

Task.Factory.StartNew(() =>
{
//これ以降は各クライアントで行いたい処理。
//データをストリームへ取得
var stream = client.GetStream();
IPEndPoint endpoint = (IPEndPoint)client.Client.RemoteEndPoint;
IPAddress address = endpoint.Address;

//データを受け取るbyte型変数を定義(例では1バイトずつ受け取る)
byte[] getData = new byte[1];

//データの取得と同時に、取得したデータのバイト数も得る
//引数は(受け皿,格納開始位置,受け取るバイト数)
int cnt;

//どれだけもらうかわからないので一時的に格納するリストを定義
List<byte> bytelist=new List<byte>();

//cntには受け取ったデータの長さが入る
while((cnt = stream.Read(getData, 0, getData.Length)) > 0)
{
//データをリストに追加していく
foreach(byte byteData in getData)
bytelist.Add(byteData);
}

//リストに入った分だけ配列を定義
byte[] result = new byte[bytelist.Count];
for(int i = 0 ; i < result.Length ; i++){
result[i] = bytelist[i];
}

//文字列にエンコード
string data = Encoding.UTF8.GetString(result);
//データの出力
print("受信結果:" +  data);
Chat.Insntace.GetMessage(data);

});

}

}


Client側
using UnityEngine;
using System;
using System.Threading;
using System.Collections;
using System.Collections.Generic;
using System.Net.Sockets;
using System.IO;
using System.Text;
using System.Net;

public class Client : MonoBehaviour {

bool isForceStop = false;
//NetworkStream stream = null;
bool isStopReading = false;
byte[] readbuf;

private IEnumerator Start(){
Debug.Log("START START");
readbuf = new byte[1024];

while (!isForceStop) {
if(!isStopReading)    {
StartCoroutine(ReadMessage ());
}
yield return new WaitForSeconds(1f);//あんまりしょっちゅうやらないために
}
}

void Update()
{
if (Input.GetKeyDown (KeyCode.A) == true) {
isForceStop = true;
}

}

public IEnumerator SendCurrentMessage(string message){

Debug.Log ("START SendMessage:" + message);

string playerName = "[A]: ";
//サーバーにデータを送信する
Encoding enc = Encoding.UTF8;
byte[] sendBytes = enc.GetBytes(playerName + message + "\n");
//データを送信する
stream.Write(sendBytes, 0, sendBytes.Length);
yield break;
}

//常駐
private IEnumerator ReadMessage(){
NetworkStream stream = GetNetworkStream ();
// 非同期で待ち受けする
stream.BeginRead (readbuf, 0, readbuf.Length, new AsyncCallback (ReadCallback), null);
isStopReading = true;
yield return null;
}

public void ReadCallback(IAsyncResult ar ){
Encoding enc = Encoding.UTF8;
NetworkStream stream =  GetNetworkStream ();
int bytes = stream.EndRead(ar);
string message = enc.GetString (readbuf, 0, bytes);
message = message.Replace("\r", "").Replace("\n", "");
isStopReading = false;
Chat.Insntace.GetMessage (message);
}   

private NetworkStream GetNetworkStream(){

string ipOrHost = "127.0.0.1"; 
int port = 2222;

//TcpClientを作成し、サーバーと接続する
TcpClient tcp = new TcpClient(ipOrHost, port);
//TcpClient tcp = new TcpClient(System.Net.IPAddress.Any.ToString(), port);

Debug.Log("success conn server");

//NetworkStreamを取得する
return tcp.GetStream();
}
}

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

+1

Unityはサンプルを弄ったレベルですが、TcpListenerの使い方が気になったので回答します。

UnityでTCPを利用したチャットのやりとりをできるようにしたいんです。

この件はUnity WebSocketのキーワードで検索してみるのをお勧め致します。
TcpListenerを使うより楽に開発できるかと。

以下は質問文のコードについての回答です。
1,TcpListener#Startメソッドの呼び出しを行い受信ポートをListen状態にする必要があります。
2,TcpListener#EndAcceptTcpClientした直後にTcpListener#BeginAcceptTcpClientをして次のAcceptを非同期で待機する必要があります。

DoAcceptTcpClientCallbackの宣言にstaticが付いているのが気になります。
コンパイルを通してませんが、以下のような形にもできると思います。

private TcpListener server = null;
// 
public void ServerStart()
{
    int port = 2222;
    this.server = new TcpListener(System.Net.IPAddress.Any, port);
    this.server.Start();
    this.server.BeginAcceptTcpClient(new AsyncCallback(this.DoAcceptTcpClientCallback), this.server);
}

public void DoAcceptTcpClientCallback(IAsyncResult ar) 
{
    var listener = res.AsyncState as TcpListener;
    // EndAcceptTcpClient呼び出し時にObjectDisposedExceptionが発生する可能性があるので、例外をキャッチするか、
    var client = listener.EndAcceptTcpClient(res);
    // 次のAcceptを非同期で呼び出し。
    listener.BeginAcceptTcpClient(new AsyncCallback(this.DoAcceptTcpClientCallback), listener);

    Task.Factory.StartNew(() =>
    {
       //これ以降は各クライアントで行いたい処理。
        var stream = client.GetStream();
    });
}

◇参考情報
C# TCP通信の複数クライアント

投稿

編集

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2018/04/18 14:16

    早速御回答いただきありがとうございます。
    ありがたいです。
    フリーズはなくなりましたが、
    SocketException: 対象のコンピューターによって拒否されたため、接続できませんでした。
    がクライアント側で発生しています。初心者なのでIPアドレスにいったいどのIPアドレスを入れたらいいのか分かっていません。
    同じパソコンで通信テストをするので、
    Client側は
    string ipAddress = "自分のIPアドレス";
    int port = 2222; //サーバー側のポート番号

    Server側では
    int port = 2222;
    IPAddress ip = IPAddress.Parse(自分のIPアドレス);
    でいいんでしょうか?
    ネットではサーバーはSystem.Net.IPAddress.Anyにしろと書いてありますが、どちらでも
    Client側が 対象のコンピューターによって拒否されたため、接続できませんでした。を吐きます。
    よろしければ、ご教授いただけませんでしょうか?

    キャンセル

  • 2018/04/18 14:21 編集

    1,サーバー側のプログラムを動かした状態でまずコマンドプロンプトよりnetstat -anp tcpコマンドを実行して、ポート番号:2222で受信待機していることを確認してください。
    2,次に質問文はサーバー側のコードなので、クライアントのコードを質問文に追記してくださいな。
    自分のIPアドレスと接続したいなら、IPAddress localAddr = IPAddress.Parse("127.0.0.1"); みたいな形が良いと思います。
    3,回答文にも記載しましたが、WebSocketで作成されることをオススメします。

    キャンセル

  • 2018/04/18 14:41

    インデントが崩れちゃうのでコメント欄ではなく、質問文に追記してください。

    キャンセル

  • 2018/04/18 14:44

    なるほどです。NetStatコマンドで、ローカルとリモートのIPアドレスおよびポート番号、そして状態がわかるんですね。はずかしながらポートってなんでもいいのかと思っていました(本当にポンコツの初心者で)

    キャンセル

  • 2018/04/18 14:45

    TCP/UDPをあきらめてWebSocketで作成。承知しました。明日実装をかえてみます。

    キャンセル

  • 2018/04/18 14:50

    >peter_kesさんへ
    はい、お手すきの時に質問文を自己解決でクローズしてくださいな。
    ネットワークプログラムはデバックとテストが結構たいへんなので、自作せずに既存ライブラリを使うことをお勧めします。

    キャンセル

  • 2018/04/18 14:51

    はい!! 本当にスこんなに早く解答していただいて感謝しております!!! すごく勉強になりました。
    ありがとうございます。

    キャンセル

関連した質問

  • 解決済

    C#でlogを出力する方法(log4net無しで)

    デバッグとして変数をlogに出力したいのですが検索するとlog4netを使用する例が多く出てきます。 チーム制作中なのでdllを追加する等はあまりやりたくないです、、、 .txt形

  • 解決済

    【Unity JavaScript】yield WaitForSeconds(0.0)の動作

    コルーチンの中で yield WaitForSeconds(wait); のように変数で指定する間、処理を停止したいのですが この変数waitに0を入れた場合 この文はスルーさ

  • 解決済

    Unity5のテキスト表示について

    UnityでUDPデータを受信して、UIのTextに表示をしようと考えているのですが表示することができません。 言語はC#です。 11/10 Debug.Log()については表示

  • 解決済

    C言語とC#間でSocket通信をさせたい

    前提・実現したいこと C言語とC#でローカルな(同一PC内での)ソケット通信を行いたいです。 C言語側ではWinSocketを利用してサーバーを立て C#側ではSystem.

  • 受付中

    Watson の speech to text を HTTP REST で呼び出す方法

    前提・実現したいこと 【IBM Watson音声認識のAPI呼び出し】 IBM Watson の HTTP REST API で speech to text の recogn

  • 解決済

    EC2上でのシンタックスエラーについて

    EC2にSSH接続して、Node.jsを起動するために「sudo npm start」のコマンドを走らせると下記のようなエラーが出てしまい原因がわからず困っています。 間違えって

  • 解決済

    1対1のTCP通信画面を一つのソフト内で複数作成

    いつもお世話になっております。 今回はある動作でSocektExceptionが発生する為、その対処法についてお聞きしようと質問しました。 やりたい事は1対1のTCP通

  • 解決済

    C# ネットワーク間のシリアライズ

    今TCP/IPでクライアントとサーバーの通信をしています。 クライアントのClassをシリアライズし、サーバーに送りたいのですがやり方がイマイチわかりません。 クライアント

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

  • Unity

    4206questions

    Unityは、ユニティテクノロジーが開発したゲームエンジンです。 主にモバイルやブラウザ向けのゲーム製作に利用されていましたが、3Dの重力付きゲームが簡単に作成できることから需要が増え、現在はマルチプラットフォームに対応しています。 言語はC言語/C++で書かれていますが、C#、JavaScript、Booで書かれたコードにも対応しています。

  • TCP

    167questions

    TCP(Transmission Control Protocol)とは、トランスポート層のプロトコルで、コネクション型のデータサービスです。