Как обнаружить сломанный клиентский сокет без проверки связи с сервером - PullRequest
0 голосов
/ 10 февраля 2019

Я создаю клиентское приложение для сокетов, которое подключается к ESP8266-01, и единственное, что я не могу понять - как обнаружить сломанный сокет без проверки связи с сервером (ESP8266-01).

До сих пор я обнаружил 3 случая, когда я считаю, что сокет сломан, потому что клиентское приложение не может их обнаружить:

1) Резкое отключение ESP8266 от источника питания без надлежащего закрытия сокета

2) Отключение Wi-Fi, к которому подключено и мое приложение, и ESP8266.

3) Вызов AT + CIPCLOSE из ESP8266.

Если произойдет что-либо из вышеперечисленного, и я отправлю данные с помощью SendData() будет сгенерировано и обработано исключение, за исключением случая 1., в котором оно просто остается незамеченным.Я пытался использовать блокировку io раньше, и она учитывает случай 3. Однако каждый вызов Disconnect будет вызывать исключение из-за вызовов Thread.interrupt ().

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();
}
}

Чтобы быть более конкретным, я хочучтобы узнать, есть ли способ проверить, не сломан ли сокет (как 3 случая, которые я указал выше), не пропингуя сервер.

ОБНОВЛЕНИЕ:

Я повторнонаписал код, и теперь я могу легко обнаружить сломанный сокет, и 3 случая, которые я не смог обнаружить ранее, решены.

ТЕКУЩАЯ ПРОБЛЕМА

Всякий раз, когда я посылаю "выход"из приложения вызывается метод Disconnect () (правильное поведение).Однако это вызывает исключение: «InputReaderThread exception: Socket closed» (ОШИБКА).

Я понимаю, что это происходит потому, что я вызываю client_socket_.close () в методе Disconnect () и потому что inputStream.read(byte [] b) является методом блокировки (внутри класса InputReaderThread), он обнаруживает, что сокет был закрыт, и выдает исключение «Socket closed», потому что чтение из закрытого сокета невозможно.

Я попытался обменяться:

data_out_.close();
data_in_.close();

С:

client_socket_.shutdownOutput();
client_socket_.shutdownInput();

И даже если описание функции Socket.shutdownInput () равно:

Помещает входной поток для этогосокет в "конце потока".Любые данные, отправленные на сторону входного потока сокета, подтверждаются, а затем молча отбрасываются.Если вы читаете из входного потока сокета после вызова shutdownInput () для сокета, поток вернет EOF.

Вызов read (byte [] b) после shutdownInput () фактически не возвращает EOFкогда это было необходимо, и я также проверил это, вызвав метод join () для input_reader после вызова client_socket_.shutdownInput (), и в результате программа остановилась на строке while ((bytes_read = data_in_.read (buffer))! = -1) и noEOF когда-либо читали.

Я что-то упускаю из виду?или это просто нормальное поведение для Java?

NEW CODE

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();
    }
}
...