java - 为什么我无法从 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
解决方案
该问题可能与响应末尾缺少换行符有关。添加它并没有立即帮助,但是在服务器中进行了一些更改后,Android 应用程序开始能够获得响应。添加新行字符后,我无法确定到底是什么问题。
推荐阅读
- bash - 在 bash 变量上使用 cut 时出现“找不到命令”
- http - NMEA 数据的正确 Content-Type 标头?
- php - Symfony UniqueEntity 验证消息
- php - 无法加载请求的类:Phpmailer_library
- python - 去掉空方括号给出 ValueError: invalid literal for int() with base 10:
- php - Laravel 按季节价格过滤产品并提供折扣
- c# - 如何从网页重定向到 Windows 应用程序?
- javascript - 如何在特定数字的每两个偶数数字之间插入破折号
- python - 新手的心率计数
- python - 在 Python 中用空格替换 nan