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

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

ただいまの
回答率

89.71%

unity(C#)とpythonでのソケット通信ができずに困っています。

解決済

回答 2

投稿

  • 評価
  • クリップ 0
  • VIEW 3,117

meJ15

score 42

 前提・実現したいこと

一つのパソコンを使ってpythonでリアルタイムに計算した結果をC#(unity)で受け取りたい。

そこでパイプ処理というものに行きつき、さっそくデモ用にpythonで送信し、それをほかのpythonのプログラムで受信することはできました。参考URL

このpythonの受信プログラムをunityでも使える形にしようとしています。

(pythonで送信して、C#で受信)送信側のpythonのプログラムは変えなくてよいはず?

しかし受信用のプログラムをC#にどう書き直せばよいのかわからず、とりあえず、ほかのサイトを見て実装だけしようと思いました。
このサイトをみて、SocketServer、Serverというスクリプトを作り、ポートを50007としても、デバックログには何も出力されずに挫折しました。

上の方法をあきらめ、
他のサイトのC#クライアントのページをみてunityで使えるように書きましたが下のようすぐ接続が切れてしまい、データが受け取れません。
TCPC#クライアント

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

pythonから送られてくるデータをunityで逐次とりたいが、切断される。

 該当のソースコード

これがpythonの受信プログラムです。
2秒おきに、データを受け取っているつもりです。
これならpythonで送信される値をとれることがわかりました。
これをC#出かければ最高なのですが、、、

import socket
import time 
import datetime

HOST = '127.0.0.1'    # The remote host
PORT = 50007 # The same port as used by the server
for i in range(10):
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.connect((HOST, PORT))

    s.send("Hallo".encode('utf-8'))
    data = s.recv(1024)
    s.close()
    print ('Received', (data).decode('utf-8'))
    time.sleep(2.0) //2秒おきにデータをもらうために


これを自分なりにunityで動くように考えたものが↓です。
(2秒おきとかは考えずに来たデータを蓄積する)

using UnityEngine;
using System;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Collections.Generic;

public class Cserver : MonoBehaviour {

    // Use this for initialization
    void Start () {

        var HOST = "127.0.0.1";
        var PORT = 50007;
        var data = 0;
        System.Net.Sockets.TcpClient tcp =
            new System.Net.Sockets.TcpClient(HOST, PORT);
        Debug.Log("サーバと接続した");

        //NetworkStreamを取得する
        System.Net.Sockets.NetworkStream ns = tcp.GetStream();


        //読み取り、書き込みのタイムアウトを10秒にする
        //デフォルトはInfiniteで、タイムアウトしない
        //(.NET Framework 2.0以上が必要)
        ns.ReadTimeout = 100000;
        ns.WriteTimeout = 100000;

        var sendMsg = "hallo";
        //サーバーにデータを送信する
        //文字列をByte型配列に変換
        System.Text.Encoding enc = System.Text.Encoding.UTF8;
        byte[] sendBytes = enc.GetBytes(sendMsg + '\n');
        //データを送信する
        ns.Write(sendBytes, 0, sendBytes.Length);
        Debug.Log(sendMsg);


        //サーバーから送られたデータを受信する
        System.IO.MemoryStream ms = new System.IO.MemoryStream();
        byte[] resBytes = new byte[256];
        int resSize = 0;
        do
        {
            //データの一部を受信する
            resSize = ns.Read(resBytes, 0, resBytes.Length);
            //Readが0を返した時はサーバーが切断したと判断
            if (resSize == 1)
            {
                Debug.Log("サーバーが切断しました。");
                break;
            }
            //受信したデータを蓄積する
            ms.Write(resBytes, 0, resSize);
            //まだ読み取れるデータがあるか、データの最後が\nでない時は、
            // 受信を続ける
        } while (ns.DataAvailable || resBytes[resSize - 1] != '\n');
        //受信したデータを文字列に変換
        string resMsg = enc.GetString(ms.GetBuffer(), 0, (int)ms.Length);
        ms.Close();
        //末尾の\nを削除
        resMsg = resMsg.TrimEnd('\n');
        Debug.Log(resMsg);

        //閉じる
        ns.Close();
        tcp.Close();
        Debug.Log("切断しました。");

        Console.ReadLine();
        Debug.Log(data);

    }

    // Update is called once per frame
    void Update () {


    }
}


このC#のプログラムをunityで動かすと下のようになります。
unity

またpython側は下のようになります
!python側]

これがpythonの送信プログラムです。このプログラムは変える必要はないと思います。
pythonの送信側は将来的に0か1か2かを2秒おきぐらいに送信するだけなので今は1を連続して送信しています。

import socket
import threading
import time
import datetime

HOST = '127.0.0.1'
PORT = 50007
INTERVAL = 1 # 測定間隔
status = { "result" : "" } # 結果保存用

# 測定実行用スレッドのクラス
class MyThread(threading.Thread):

    def __init__(self):
        super(MyThread, self).__init__()
        self.setDaemon(True)

    def run(self):
        for i in range(50):           
            result = str(1)
            print (result)
            status["result"] = result
            time.sleep(INTERVAL)


 # サーバを作成して動かす関数
def socket_work():

    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.bind((HOST, PORT))
    s.listen(1)

    while True:
        conn, addr = s.accept()
        print ('Connected by', addr)
        data = conn.recv(1024)
        print (data.decode('utf-8'))
        conn.send(status["result"].encode('utf-8'))
        conn.close()

if __name__ == '__main__':

    # スレッドの作成と開始
    mythread = MyThread()
    mythread.start()

    # サーバを作成して動かす
    socket_work()

このC#のプログラムがなぜ意図した動きをしないのか、
pythonの受信用のプログラムをC#に書き換えたいですが、うまくいきません。

C#では2秒おきぐらいにpythonから来るデータを2秒おきに確認してそれによってアニメーションさせようと考えています。いま、そのデータの受信ができずに手が止まってしまっています。

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

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

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

    クリップを取り消します

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

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

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

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

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

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

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

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

    質問の評価を下げる

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

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

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

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

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

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

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

    詳細な説明はこちら

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

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

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

回答 2

checkベストアンサー

+1

pythonの送信側ですが

    while True:
        conn, addr = s.accept()
        print ('Connected by', addr)
        data = conn.recv(1024)
        print (data.decode('utf-8'))
        conn.send(status["result"].encode('utf-8'))
        conn.close()


このブロックがwhileとしてループするんですよね?
とすれば、送信側のTCPコネクションの動作としては
1.受信側プログラムからの接続を受け付け
2.受信側プログラムからのデータを受信
3.データの送信
4.コネクションの切断
この処理がループすることになります。

Unity側は
1.送信側へコネクションの接続要求
2.接続したら、データの送信
3.送信側からのデータを受信
の流れが1回実行されるような形になるかと思います。
(このスクリプトがどのようにアタッチされているのか分からないので1回だけ呼ばれると考えています)
このスクリプトを定期的に動作させるようにするか、一連の流れをループして動作し続けるようにする必要があるかと思います。

ただ、この通信であれば、TCPを使うよりUDPを使った方がすっきりしそうな気がします。
UDPであれば、コネクションレスなので、
・送信側は受信側が実行されているされていないに関わらずデータを投げ続ける。
・受信側は受信待ちして、データが来たら処理をする
だけになりそうです。

送信側

import socket

    HOST = '127.0.0.1'
    PORT = 50007

    client = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    while True:
        client.sendto(status["result"].encode('utf-8'),(HOST,PORT))

受信側

    int LOCA_LPORT = 50007;
    udp = new UdpClient(LOCA_LPORT);
    udp.Client.ReceiveTimeout = 1000;

    while(true)
    {
        IPEndPoint remoteEP = null;
        byte[] data = udp.Receive(ref remoteEP);
        string text = Encoding.UTF8.GetString(data);
        Debug.Log(text);
    }


肉付けは必要ですが、通信部分だけであれば、このような感じになるかと思います。

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2018/11/11 12:58

    回答ありがとうございます。UDP通信を使ったほうが良いという提案ありがとうございます。
    pythonのコードを
    while True:
    a = random.randrange(3)
    result = str(a)
    print(a)
    client.sendto(result.encode('utf-8'),(HOST,PORT))
    time.sleep(2.0)
    として、2秒おきぐらいに0,1,2を送るようにしました。

    C#(unity側)
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    using System.Net;
    using System.Net.Sockets;
    using System.Text;
    using System.Threading;

    public class UDP : MonoBehaviour {

    static UdpClient udp;
    IPEndPoint remoteEP = null;
    int i = 0;
    // Use this for initialization
    void Start () {
    int LOCA_LPORT = 50007;

    udp = new UdpClient(LOCA_LPORT);
    udp.Client.ReceiveTimeout = 2000;
    }

    // Update is called once per frame
    void Update ()
    {
    IPEndPoint remoteEP = null;
    byte[] data = udp.Receive(ref remoteEP);
    string text = Encoding.UTF8.GetString(data);
    Debug.Log(text);
    Thread.Sleep(1000);
    }
    }
    として、おくられてくるデータをunity側でみることができました。
    通信部分だけを書いていただいたのがすごく助かりました。
    これから自分が望む動作をするように頑張りたいと思います。

    キャンセル

  • 2018/11/11 15:22

    UnityではUpdateの中で時間をかけるべきではなかったかと思います。
    受信部分は別スレッドなりで実行しておき、その値をUpdate内でみるという形にするほうがいいのだとは思いますが、Unityに適した実装方法はちょっと分かりません。

    キャンセル

  • 2018/11/11 16:20

    C#
    public calss UDPの内部のupdate関数を
    void Update ()
    {
    IPEndPoint remoteEP = null;
    byte[] data = udp.Receive(ref remoteEP);
    text = Encoding.UTF8.GetString(data);
    Debug.Log(text);
    }
    }
    として、
    他のunityプログラムのupdate関数で
    a = int.Parse(UDP.text);
    として値を確保することにしました。

    キャンセル

0

おそらく両方のソースともクライアントのソースになっているので、両方ともサーバに接続しようとしているのが原因だと思われます。ソースを見る限り、サーバを立てる部分がありません。
送信側か受信側のどちらかをサーバとして実行しないと接続できません。参考にしたURLのサーバを作成して動かす関数を組み合わせたらうまくいくと思います。

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2018/11/10 23:17

    回答ありがとうございます。
    今私は一番下のpythonのコード(サーバ用?)と一番上のpython(クライアント用)のコードで上手くやり取りできましたが、一番上のpythonコードをC#に書き換えがうまくできてないと考えています。
    そのC#のプログラムが2番目です。
    今やっているのはこの2番目のC#と一番下のpythonのプログラムの通信です。

    一番下のpythonのコードのdef socket_work():という関数でサーバを作成しているつもりなのですが、
    このソースはサーバーとして機能していないのでしょうか?

    キャンセル

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

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