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

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

新規登録して質問してみよう
ただいま回答率
85.48%
TCP

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

Unity

Unityは、Unity Technologiesが開発・販売している、IDEを内蔵するゲームエンジンです。主にC#を用いたプログラミングでコンテンツの開発が可能です。

Q&A

解決済

2回答

11077閲覧

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

peter_kes

総合スコア14

TCP

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

Unity

Unityは、Unity Technologiesが開発・販売している、IDEを内蔵するゲームエンジンです。主にC#を用いたプログラミングでコンテンツの開発が可能です。

0グッド

0クリップ

投稿2018/04/18 03:40

編集2018/04/19 02:51

前提・実現したいこと

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

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

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

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

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答2

0

自己解決

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(); }

}

投稿2018/04/19 02:46

peter_kes

総合スコア14

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

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

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

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

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

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

C#

1private TcpListener server = null; 2// 3public void ServerStart() 4{ 5 int port = 2222; 6 this.server = new TcpListener(System.Net.IPAddress.Any, port); 7 this.server.Start(); 8 this.server.BeginAcceptTcpClient(new AsyncCallback(this.DoAcceptTcpClientCallback), this.server); 9} 10 11public void DoAcceptTcpClientCallback(IAsyncResult ar) 12{ 13 var listener = res.AsyncState as TcpListener; 14 // EndAcceptTcpClient呼び出し時にObjectDisposedExceptionが発生する可能性があるので、例外をキャッチする必要があります。 15 var client = listener.EndAcceptTcpClient(res); 16 // 次のAcceptを非同期で呼び出し。 17 listener.BeginAcceptTcpClient(new AsyncCallback(this.DoAcceptTcpClientCallback), listener); 18 19 Task.Factory.StartNew(() => 20 { 21   //これ以降は各クライアントで行いたい処理。 22 var stream = client.GetStream(); 23 }); 24} 25

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

投稿2018/04/18 04:38

編集2018/04/18 04:55
umyu

総合スコア5846

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

peter_kes

2018/04/18 05: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側が 対象のコンピューターによって拒否されたため、接続できませんでした。を吐きます。 よろしければ、ご教授いただけませんでしょうか?
umyu

2018/04/18 05:22 編集

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

2018/04/18 05:41

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

2018/04/18 05:44

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

2018/04/18 05:45

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

2018/04/18 05:50

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

2018/04/18 05:51

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問