首页 > 解决方案 > 为什么我无法从 tcp 服务器获得响应?

问题描述

我有(基于 ESP32 的)设备,其中包括实现 TCP 服务器。我可以向它发送 JSON 格式的消息以更改其中的一些控件。

我希望能够通过我的 Android 手机控制此设备。我为它写了一个应用程序,但我有一个问题 - 我的 Android 应用程序没有得到 ESP32 设备的任何响应。我为测试用 Python 编写了简单的脚本。第一个是客户端,它向 ESP32 发送消息。第二个实现 tcp 服务器,这样我就可以将我的 Android 应用程序连接到它并检查响应是否到达。两项测试都可以正常工作。

我的 Android 代码有什么问题吗?我不能确定这是否不是 ESP32 设备故障,但请先检查我的 Android 代码 - 从 ESP32 的 C 代码制作 MCVE 更难。

这是我的 Android 应用的 MCVE:

MainActivity.java:

package com.example.tcpclienttest;

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketAddress;

public class MainActivity extends AppCompatActivity {

    String TAG = "******** INFO ********";

    Socket tcpSocket;
    InetAddress IP;
    int tcpPort = 12345;
    PrintWriter mBufferOut;
    BufferedReader mBufferIn;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Button button = this.findViewById(R.id.button);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

                new Thread(new Runnable() {
                    public void run() {
                        // try twice in case of tcp error
                        if (SendSetStateCommandTaskFunction()) {
                            Log.i(TAG, "Retrying SendSetStateCommandTask function");
                            SendSetStateCommandTaskFunction();
                        }
                    }
                }).start();

            }
        });
    }

    boolean SendSetStateCommandTaskFunction() {

        boolean retry = false;



        String request_data = "'{\"cmd\":\"set_controls\", \"data\":{\"controls\":{\"name1\":5}}}'";

        try {
            IP = InetAddress.getByName("192.168.1.66");
            if (tcpSocket == null) {
                Log.i(TAG, "Creating new tcp socket");
                tcpSocket = new Socket(IP, tcpPort);
                tcpSocket.setSoTimeout(3000);
                //sends the message to the server
                mBufferOut =
                        new PrintWriter(
                                new BufferedWriter(
                                        new OutputStreamWriter(tcpSocket.getOutputStream())), true);

                //receives the message which the server sends back
                mBufferIn =
                        new BufferedReader(
                                new InputStreamReader(tcpSocket.getInputStream()));
            } else
                Log.i(TAG, "Tcp socket already created");
            if (!tcpSocket.isConnected()) {
                Log.i(TAG, "Connecting to ip addr");
                SocketAddress sockaddr = new InetSocketAddress(IP, tcpPort);
                tcpSocket.connect(sockaddr);
            } else
                Log.i(TAG, "Already connected");

            String in_message;

            mBufferOut.println(request_data);
            mBufferOut.flush();

            in_message = mBufferIn.readLine();

            if (in_message == null) {
                Log.i(TAG, "Received empty message");
                return false;
            }

            Log.i(TAG, in_message);
        } catch (IOException e) {
            Log.e(TAG, "ERROR: " + e.getMessage());
            e.printStackTrace();
            tcpSocket = null;
            retry = true;
        }

        Log.i(TAG, "Finishing SendSetStateCommandTask function");
        return retry;
    }
}

AndroidManifest.xml:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.tcpclienttest">

    <uses-permission android:name="android.permission.INTERNET"/>

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

main_activity.xml:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <Button
        android:id="@+id/button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Send"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

Python客户端:

import socket
import json

TCP_IP = "192.168.1.66"
TCP_PORT = 12345

print("TCP target IP:", TCP_IP)
print("TCP target port:", TCP_PORT)

sock = socket.socket(socket.AF_INET, # Internet
                     socket.SOCK_STREAM) # TCP
sock.connect((TCP_IP, TCP_PORT))

MESSAGE = str.encode('{"cmd":"set_controls", "data":{"controls":{"name1":6}}}')

print("message:", MESSAGE)

sock.send(MESSAGE)

data = sock.recv(1024)  # buffer size is 1024 bytes
json_data = json.loads(data.decode("ascii"))
print("received message:")
print(json.dumps(json_data, indent=4))

Python服务器:

#!/usr/bin/env python

import socket

TCP_IP = ''
TCP_PORT = 12345
BUFFER_SIZE = 1024

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind((TCP_IP, TCP_PORT))
s.listen(1)

conn, addr = s.accept()
print('Connection address:', addr)
while 1:
    data = conn.recv(BUFFER_SIZE)
    if not data:
        break
    print("received data:", data)
    conn.send(data)  # echo
conn.close()

SEND单击按钮后,来自应用程序 lanuch 的控制台日志:

I/ViewRootImpl: jank_removeInvalidNode all the node in jank list is out of time
V/AudioManager: playSoundEffect   effectType: 0
    querySoundEffectsEnabled...
I/******** INFO ********: Creating new tcp socket
I/******** INFO ********: Already connected
E/******** INFO ********: ERROR: Read timed out
W/System.err: java.net.SocketTimeoutException: Read timed out
W/System.err:     at java.net.SocketInputStream.socketRead0(Native Method)
        at java.net.SocketInputStream.socketRead(SocketInputStream.java:119)
W/System.err:     at java.net.SocketInputStream.read(SocketInputStream.java:176)
        at java.net.SocketInputStream.read(SocketInputStream.java:144)
        at sun.nio.cs.StreamDecoder.readBytes(StreamDecoder.java:288)
W/System.err:     at sun.nio.cs.StreamDecoder.implRead(StreamDecoder.java:351)
        at sun.nio.cs.StreamDecoder.read(StreamDecoder.java:180)
W/System.err:     at java.io.InputStreamReader.read(InputStreamReader.java:184)
        at java.io.BufferedReader.fill(BufferedReader.java:172)
W/System.err:     at java.io.BufferedReader.readLine(BufferedReader.java:335)
        at java.io.BufferedReader.readLine(BufferedReader.java:400)
        at com.example.tcpclienttest.MainActivity.SendSetStateCommandTaskFunction(MainActivity.java:93)
W/System.err:     at com.example.tcpclienttest.MainActivity$1$1.run(MainActivity.java:44)
        at java.lang.Thread.run(Thread.java:784)
I/******** INFO ********: Finishing SendSetStateCommandTask function
I/******** INFO ********: Retrying SendSetStateCommandTask function
    Creating new tcp socket
I/******** INFO ********: Already connected
E/******** INFO ********: ERROR: Read timed out
W/System.err: java.net.SocketTimeoutException: Read timed out
W/System.err:     at java.net.SocketInputStream.socketRead0(Native Method)
W/System.err:     at java.net.SocketInputStream.socketRead(SocketInputStream.java:119)
        at java.net.SocketInputStream.read(SocketInputStream.java:176)
W/System.err:     at java.net.SocketInputStream.read(SocketInputStream.java:144)
        at sun.nio.cs.StreamDecoder.readBytes(StreamDecoder.java:288)
        at sun.nio.cs.StreamDecoder.implRead(StreamDecoder.java:351)
W/System.err:     at sun.nio.cs.StreamDecoder.read(StreamDecoder.java:180)
        at java.io.InputStreamReader.read(InputStreamReader.java:184)
W/System.err:     at java.io.BufferedReader.fill(BufferedReader.java:172)
        at java.io.BufferedReader.readLine(BufferedReader.java:335)
W/System.err:     at java.io.BufferedReader.readLine(BufferedReader.java:400)
        at com.example.tcpclienttest.MainActivity.SendSetStateCommandTaskFunction(MainActivity.java:93)
        at com.example.tcpclienttest.MainActivity$1$1.run(MainActivity.java:46)
W/System.err:     at java.lang.Thread.run(Thread.java:784)
I/******** INFO ********: Finishing SendSetStateCommandTask function

标签: javaandroidtcp

解决方案


该问题可能与响应末尾缺少换行符有关。添加它并没有立即帮助,但是在服务器中进行了一些更改后,Android 应用程序开始能够获得响应。添加新行字符后,我无法确定到底是什么问题。


推荐阅读