我正在制作一个连接到ESP8266-01的客户端套接字应用程序,我唯一无法解决的事情是如何在不对服务器执行ping操作的情况下检测到损坏的套接字(ESP8266-01)。
到目前为止,我发现3种情况下,我认为套接字已损坏,因为客户端应用无法检测到它们:
1)突然断开ESP8266的电源,而没有正确关闭插座
2)断开连接我的应用程序和ESP8266的wifi。
3)从ESP8266调用AT + CIPCLOSE。
如果发生以上任何情况,并且我使用SendData()发送数据,则将引发并处理异常(情况1除外),在这种情况下,它根本不会被检测到。 我以前尝试过使用阻塞io,它可以处理情况3。但是,由于Thread.interrupt()调用,每次对Disconnect的调用都会引发异常。
import java.io.ByteArrayOutputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.Scanner;
public class TCPClient {
private Socket client_socket_ = null;
private OutputWriterThread output_writer_ = null;
private InputReaderThread input_reader_ = null;
private LinkedBlockingQueue<String> send_queue_ = null;
private boolean connected_ = false;
public TCPClient()
{
send_queue_ = new LinkedBlockingQueue<String>();
}
private class OutputWriterThread extends Thread
{
@Override
public void run()
{
try (BufferedOutputStream data_out = new BufferedOutputStream(client_socket_.getOutputStream())){
while (!Thread.currentThread().isInterrupted()) {
if(send_queue_.size() > 0) {
data_out.write(send_queue_.poll().getBytes());
data_out.flush();
}
}
} catch(IOException ex) {
System.out.println("OutputWriterThread exception: " + ex.getMessage());
Disconnect();
}
}
}
private class InputReaderThread extends Thread
{
@Override
public void run()
{
try (BufferedInputStream data_in = new BufferedInputStream(client_socket_.getInputStream())) {
ByteArrayOutputStream parser = new ByteArrayOutputStream(2048);
byte[] buffer = new byte[2048];
while (!Thread.currentThread().isInterrupted()) {
if(data_in.available() > 0) {
parser.write(buffer, 0, data_in.read(buffer));
DataReceived(parser.toString("UTF-8"));
parser.reset();
}
}
} catch(IOException ex) {
System.out.println("InputReaderThread exception: " + ex.getMessage());
Disconnect();
}
}
}
public synchronized void Connect(final String ip, final String port, final int timeout)
{
System.out.println("connect attempted");
if(!connected_) {
try {
System.out.println("starting connect function");
client_socket_ = new Socket();
client_socket_.connect(new InetSocketAddress(ip, Integer.parseInt(port)), timeout);
output_writer_ = new OutputWriterThread();
input_reader_ = new InputReaderThread();
output_writer_.start();
input_reader_.start();
connected_ = true;
System.out.println("ending connect function");
} catch(IOException ex) {
System.out.println("Connect:" + ex.getMessage());
}
}
}
public void SendData(final String data)
{
try {
if(connected_) send_queue_.offer(data);
} catch(NullPointerException ex) {
System.out.println("SendData:" + ex.getMessage());
}
}
public void DataReceived(String data)
{
System.out.println(data);
}
public boolean IsConnected()
{
return connected_;
}
public synchronized void Disconnect()
{
System.out.println("disconnect attempted");
if(connected_) {
try {
System.out.println("starting disconnect function");
output_writer_.interrupt();
input_reader_.interrupt();
System.out.println("Before closing the socket");
client_socket_.close();
connected_ = false;
System.out.println("ending disconnect function");
} catch(IOException ex) {
System.out.println("Disconnected exception:" + ex.getMessage());
}
}
}
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
TCPClient client = new TCPClient();
client.Connect("10.0.0.8", "9001", 5000);
while(true) {
String msg = sc.nextLine();
if(msg.equals("exit"))
break;
client.SendData(msg);
}
client.Disconnect();
}
}
更具体地说,我想知道在不对服务器执行ping操作的情况下,是否有任何方法可以检查套接字是否损坏(如我上面指出的3种情况)。
更新:
我重新编写了代码,现在我可以轻松地检测到套接字损坏,并且解决了我之前无法检测到的3种情况。
当前问题
每当我从应用程序发送“退出”消息时,它都会调用Disconnect()方法(正确的行为)。但是,它会引发异常:“ InputReaderThread异常:套接字已关闭”(错误)。
我理解发生这种情况是因为我在Disconnect()方法中调用client_socket_.close(),并且因为inputStream.read(byte [] b)是一个阻塞方法(在InputReaderThread类内部),它检测到套接字已关闭并且抛出“套接字关闭”异常,因为无法从关闭的套接字读取。
我尝试了交换:
data_out_.close();
data_in_.close();
使用:
client_socket_.shutdownOutput();
client_socket_.shutdownInput();
即使Socket.shutdownInput()的功能描述为:
将此套接字的输入流放在“流的末尾”。任何数据 发送到套接字的输入流端的消息得到确认,然后 默默丢弃。如果您从套接字输入流读取后 在套接字上调用shutdownInput()时,流将返回EOF。
在shutdownInput()之后调用read(byte [] b)并没有实际返回EOF,我也通过在调用client_socket_.shutdownInput()之后在input_reader上调用join()对其进行了测试,结果程序停止了在while((bytes_read = data_in_.read(buffer))!= -1)行时,从未读取过EOF。
我缺少明显的东西吗?还是Java的正常行为?
新代码
import java.io.ByteArrayOutputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.util.Scanner;
public class TCPClient {
private Socket client_socket_ = null;
private BufferedOutputStream data_out_ = null;
private BufferedInputStream data_in_ = null;
private boolean connected_ = false;
private class InputReaderThread extends Thread
{
@Override
public void run()
{
try {
ByteArrayOutputStream parser = new ByteArrayOutputStream(2048);
byte[] buffer = new byte[2048];
int bytes_read;
while ((bytes_read = data_in_.read(buffer)) != -1) {
parser.write(buffer, 0, bytes_read);
DataReceived(parser.toString("UTF-8"));
parser.reset();
}
Disconnect();
} catch(IOException ex) {
System.out.println("InputReaderThread exception: " + ex.getMessage());
}
}
}
public synchronized void Connect(final String ip, final String port, final int timeout)
{
System.out.println("connect attempted");
if(!connected_) {
try {
System.out.println("starting connect function");
client_socket_ = new Socket();
client_socket_.connect(new InetSocketAddress(ip, Integer.parseInt(port)), timeout);
data_out_ = new BufferedOutputStream(client_socket_.getOutputStream());
data_in_ = new BufferedInputStream(client_socket_.getInputStream());
InputReaderThread input_reader = new InputReaderThread();
input_reader.start();
connected_ = true;
System.out.println("ending connect function");
} catch(IOException ex) {
System.out.println("Connect:" + ex.getMessage());
}
}
}
public synchronized void SendData(final String data)
{
if(connected_)
{
Thread output_writer = new Thread() {
@Override
public void run()
{
try {
data_out_.write(data.getBytes());
data_out_.flush();
} catch(IOException ex) {
System.out.println("SendData exception: " + ex.getMessage());
Disconnect();
}
}
};
output_writer.start();
}
}
public void DataReceived(String data)
{
System.out.println(data);
}
public boolean IsConnected()
{
return connected_;
}
public synchronized void Disconnect()
{
System.out.println("disconnect attempted");
if(connected_) {
try {
System.out.println("starting disconnect function");
data_out_.close();
data_in_.close();
client_socket_.close();
connected_ = false;
System.out.println("ending disconnect function");
} catch(IOException ex) {
System.out.println("Disconnected exception:" + ex.getMessage());
}
}
}
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
TCPClient client = new TCPClient();
client.Connect("10.0.0.6", "9001", 5000);
while(true) {
String msg = sc.nextLine();
if(msg.equals("exit"))
break;
client.SendData(msg);
}
client.Disconnect();
}
}