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

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

ただいまの
回答率

89.65%

NIOを用いたTCPクライアントからのデータ送信が、呼び出すタイミングによって失敗してしまう。

解決済

回答 1

投稿

  • 評価
  • クリップ 0
  • VIEW 945

inca1987

score 18

 実装しようとしている流れ

  1. bluetoothの接続先デバイスをAndroid内で検出
  2. 接続先リストを一つづつAndroid上のUnityにTCPで送信

 クラス

TCPClientForUnity : NIOを用いたTCPクライアントクラス
BTManager : Bluetoothの接続先を検出しUnityに送るクラス
ConnectionService : 上記2クラスを持つServiceクラス

 問題・質問

下記のTCPClientForUnityが状況に依って動作しない場合があります。
TCPの送信は、TCPClientForUnityクラスのSendToUnity関数を呼んで行っているのですが、
・BTManagerクラスのStartConnect内から呼んだ場合動作せず、
・BTManagerクラスが持つBroadcastReceiverオブジェクト内から呼んだ場合動作します。

また、TCPクライアント↔サーバ間のソケットの接続が確立されたあとに、データを送信する関数を呼び出しているので、ソケットの通信自体には問題が無いものと思われます。

大変混みあった状況で申し訳ないのですが、このような状況では何の原因を疑うべきか、誤りは何か等ご教授いただけると幸いです。

 TCPClientForUnity

public class TCPClientForUnity{

    private SocketChannel mSocketChannel;
    private Selector mSelector;
    private static int mBufferLength = 1024;
    private ByteBuffer mWriteBuffer;
    private ByteBuffer mReadBuffer;
    private String mAddress;
    private int mPort;

    public TCPClientForUnity(final String adr, final int PORT){
        mAddress = adr;
        mPort = PORT;
        mWriteBuffer = ByteBuffer.allocate(mBufferLength);
        mReadBuffer = ByteBuffer.allocate(mBufferLength);
    }

    public void CreateSocketChannel(final String adr, final int PORT){
        try {
            mSelector = Selector.open();
            mSocketChannel = SocketChannel.open();
            mSocketChannel.configureBlocking(false);
            mSocketChannel.connect(new InetSocketAddress(adr, PORT));
            mSocketChannel.register(mSelector, SelectionKey.OP_CONNECT);
        }catch(IOException e){
            e.printStackTrace();
        }
    }

    private final Runnable mAcceptTask = new Runnable() {
        public void run() {
            System.out.println("start runnnable");

            try { //IOException

                //while (!Thread.interrupted()) { // try to connect
                while (true) { // try to connect
                    try { //ConnectException

                        CreateSocketChannel(mAddress, mPort);
                        System.out.println("connect loop ater mSelector.select()");
                        System.out.println("!Thread.interrupted(): " + !Thread.interrupted() + " mSocketChannel.isOpen(): " + mSocketChannel.isOpen());
                        //while (!Thread.interrupted() & mSocketChannel.isOpen() & mSelector.select()>0) {
                        while (mSocketChannel.isOpen()) {
                            int select_len = mSelector.selectNow();
                            //System.out.println("select length: " + select_len);
                            if (select_len > 0){
                                //System.out.println("select loop");
                                Iterator<SelectionKey> keys = mSelector.selectedKeys().iterator();

                                while (keys.hasNext()) {
                                    //System.out.println("keys.hasNext");
                                    SelectionKey key = keys.next();
                                    keys.remove();

                                    if (key.isConnectable()) {
                                        System.out.println("connecting...");
                                        mSocketChannel.finishConnect(); // if connection fail, throw ConnectException
                                        System.out.println("CLIENT: Connected");
                                        mSocketChannel.register(mSelector, SelectionKey.OP_READ, mReadBuffer);
                                    }

                                    if (key.isWritable()) {
                                        System.out.println("is writable");
                                        if (mWriteBuffer.hasRemaining()) {
                                            mSocketChannel.write(mWriteBuffer);
                                            System.out.println("CLIENT: Write " + ByteBuffer2String(mWriteBuffer));
                                            mWriteBuffer.clear();
                                        }
                                        mSocketChannel.register(mSelector, SelectionKey.OP_READ, mReadBuffer);
                                    }

                                    if (key.isReadable()) {
                                        System.out.println("is readable");
                                        ByteBuffer readBuf = (ByteBuffer) key.attachment();
                                        if (readBuf.hasRemaining()) {
                                            mSocketChannel.read(readBuf);
                                            String msg = ByteBuffer2String(readBuf);
                                            System.out.println("CLIENT: Read  " + msg);
                                            ClassifyMsg(msg);
                                            readBuf.clear();
                                        }
                                        mSocketChannel.register(mSelector, SelectionKey.OP_READ, readBuf);
                                        //mSocketChannel.close();
                                        //break loop;
                                    }
                                }
                            }
                        }
                    } catch (ConnectException e) {
                        e.getStackTrace();
                        try{
                            Thread.sleep(100);
                        }catch (InterruptedException  e2){
                            e2.getStackTrace();
                        }
                    }
                }
            }catch (IOException e) {
                e.getStackTrace();
            }
        }
    };


    public void SendToUnity(String msg){
        msg += "\n";
        System.out.println("in TCPClientForUnity, msg: " + msg);
        mWriteBuffer.clear();
        mWriteBuffer.put(msg.getBytes(Charset.forName("UTF-8")));
        mWriteBuffer.flip();

        try{
            mSocketChannel.register(mSelector, SelectionKey.OP_WRITE);
        }catch (IOException e){
            e.getStackTrace();
        }
    }
}

     

 BTManager  

文字数不足のため外部サイトへ     

 ConnectionService  

外部サイト

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

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

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

    クリップを取り消します

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

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

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

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

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

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

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

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

    質問の評価を下げる

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

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

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

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

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

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

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

    詳細な説明はこちら

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

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

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

回答 1

check解決した方法

+1

自己解決致しました。
原因はTCPClientForUnityの条件、if(key.isReadable())のデータ受信時の読み込み待ち状態に遷移するレジスタ設定のタイミングにありました。

ClassifyMsg(msg)→…→SendToUnity(String msg)で書き込み待ち状態に遷移するレジスタ設定mSocketChannel.register(mSelector, SelectionKey.OP_WRITE)を行ってすぐ、読み込み待ち状態に遷移するレジスタ設定mSocketChannel.register(mSelector, SelectionKey.OP_READ, readBuf);を行ってしまっていたため、

ClassifyMsg(msg)→…→SendToUnity(String msg)間のディレイが極端に少ない場合に、「書き込み待ち状態に遷移しない」という問題が発生していたようです。

上記問題を解消したコードを示します。

if (key.isReadable()) {
    ByteBuffer readBuf = (ByteBuffer) key.attachment();
    if (readBuf.hasRemaining()) {
        mSocketChannel.read(readBuf);
        String msg = ByteBuffer2String(readBuf);
        readBuf.clear();
        mSocketChannel.register(mSelector, SelectionKey.OP_READ, readBuf);
        ClassifyMsg(msg);
    }else{
        mSocketChannel.register(mSelector, SelectionKey.OP_READ, readBuf);
    }
}

自分でも混乱したまま質問を投げた所為か、伝わらない質問になってしまった事をお詫びいたします。
ありがとうございます。

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

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

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

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

  • トップ
  • Javaに関する質問
  • NIOを用いたTCPクライアントからのデータ送信が、呼び出すタイミングによって失敗してしまう。