Android Socket tcp连接状态判断

发布时间 2023-05-29 10:50:27作者: 西瓜皮不甜

Android 实现tcp连接的方式

SocketChannel

AsynchronousSocketChannel

Socket

SocketChannel
  • SocketChannel是Java NIO库提供的一种通道(Channel)类型,用于基于NIO的网络通信。

  • SocketChannel提供了非阻塞的I/O操作,可以实现高效的多路复用(Multiplexing)。

  • 它是双向通信的,可以通过read()write()方法进行读取和写入操作。

  • 在Android中,可以使用SocketChannel来进行网络通信,但需要注意在主线程之外执行,以免阻塞主线程。

select()
方法通常用于服务器端的事件监听,用于监视多个通道的可读、可写等事件状态。对于客户端而言,select()方法不适合用于连接监听,因为客户端通常只需要与单个服务器建立连接,而不需要同时监听多个连接。
            socketChannel = SocketChannel.open();
            socketChannel.configureBlocking(false);//读写模式。false表示非阻塞,true表示阻塞。
            /**
             * 先注册再连接
             */
            mSelector = Selector.open();
            socketChannel.register(mSelector, SelectionKey.OP_READ | SelectionKey.OP_WRITE);

            socketChannel.connect(new InetSocketAddress(getIp(),getPort()));
            int count = mSelector.select();
            Log.e(TAG,"count = " + count);
AsynchronousSocketChannel
  • AsynchronousSocketChannel是Java NIO.2引入的异步I/O库中的一部分,用于异步非阻塞的网络通信。

  • AsynchronousSocketChannel提供了基于回调(Callback)的异步操作,使得在进行网络通信时可以非阻塞地执行其他任务。

  • 它支持读取和写入操作,并提供了read()write()方法来进行异步操作。

在Android中,可以使用AsynchronousSocketChannel进行异步的网络通信,适用于需要在进行网络操作时不阻塞主线程的场景。

connect() 连接时判断
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.channels.AsynchronousSocketChannel;
import java.nio.channels.CompletionHandler;

public class Client {
    public static void main(String[] args) {
        try {
            AsynchronousSocketChannel channel = AsynchronousSocketChannel.open();
            channel.connect(new InetSocketAddress("example.com", 8080), null, new CompletionHandler<Void, Void>() {
                @Override
                public void completed(Void result, Void attachment) {
                    // 连接成功
                    System.out.println("Connected to the server");
                    // 执行后续操作
                }

                @Override
                public void failed(Throwable exc, Void attachment) {
                    // 连接失败
                    System.out.println("Failed to connect to the server");
                    // 执行失败处理逻辑
                }
            });

            // 可以继续进行其他操作
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
read()判断实时连接状态
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousSocketChannel;
import java.nio.channels.CompletionHandler;

public class Client {
    private static final int BUFFER_SIZE = 1024;

    public static void main(String[] args) {
        try {
            AsynchronousSocketChannel channel = AsynchronousSocketChannel.open();
            channel.connect(new InetSocketAddress("example.com", 8080), null, new CompletionHandler<Void, Void>() {
                @Override
                public void completed(Void result, Void attachment) {
                    System.out.println("Connected to the server");

                    startReading(channel); // 开始读取服务器数据
                    // 执行其他操作
                }

                @Override
                public void failed(Throwable exc, Void attachment) {
                    System.out.println("Failed to connect to the server");
                }
            });

            // 阻塞等待连接完成
            channel.awaitTermination(Long.MAX_VALUE, TimeUnit.DAYS);

        } catch (IOException | InterruptedException e) {
            e.printStackTrace();
        }
    }

    private static void startReading(AsynchronousSocketChannel channel) {
        ByteBuffer buffer = ByteBuffer.allocate(BUFFER_SIZE);
        channel.read(buffer, null, new CompletionHandler<Integer, Void>() {
            @Override
            public void completed(Integer bytesRead, Void attachment) {
                if (bytesRead == -1) {
                    // 服务器已经关闭了连接
                    System.out.println("Server disconnected");
                    // 执行处理断开连接的逻辑
                } else {
                    // 继续读取数据
                    buffer.flip();
                    byte[] data = new byte[buffer.remaining()];
                    buffer.get(data);
                    String receivedData = new String(data);
                    System.out.println("Received data from server: " + receivedData);
                    buffer.clear();

                    startReading(channel); // 继续读取服务器数据
                }
            }

            @Override
            public void failed(Throwable exc, Void attachment) {
                // 读取操作失败,可以认为服务器已经断开连接
                System.out.println("Failed to read data from server: " + exc.getMessage());
                // 执行处理断开连接的逻辑
            }
        });
    }
}

通过不断进行读取操作,客户端可以实时监听到服务器主动断开连接的情况,并执行相应的处理逻辑。

Socket
  • Socket是Java中经典的Socket类,用于基于传统的阻塞式I/O进行网络通信。

  • 它提供了阻塞式的读取和写入操作,即read()write()方法。

  • 在Android中,可以使用Socket类进行网络通信,但需要注意在Android的主线程中执行网络操作可能导致阻塞,因此通常建议在子线程中使用Socket类进行网络通信。

read() = -1
     int bytesRead = inputStream.read(buffer);
     if (bytesRead == -1) {
         // 服务器已经关闭了连接
         System.out.println("Server disconnected");
         // 执行处理断开连接的逻辑
     }

read()是阻塞的,非阻塞模式:将Socket设置为非阻塞模式,可以通过setSoTimeout()方法设置超时时间,然后使用available()方法检查是否有数据可读,再调用read()方法读取数据。

Java NIO(New I/O)中提供了更为灵活的非阻塞I/O操作方式,可以使用SelectorSelectableChannel等类来实现非阻塞的Socket通信。

     socket.setSoTimeout(timeout); // 设置超时时间
     InputStream inputStream = socket.getInputStream();
     while (true) {
         if (inputStream.available() > 0) {
             int bytesRead = inputStream.read(buffer);
             // 处理读取的数据
             break; // 退出循环
         } else {
               // 没有数据可读,可以执行其他操作
         }
     }

read()方法返回-1时,并不一定意味着连接已经断开,可能还存在其他情况。

在Socket编程中,当read()方法返回-1时,表示已经到达流的末尾,即远程服务器已经关闭了连接。这通常是一种常见的情况,可以可靠地判断连接是否已经断开。

还有一些其他情况可能导致read()方法返回-1:

  1. 读取超时:如果在设置的超时时间内没有读取到数据,read()方法可能会返回-1。这并不表示连接已经断开,而只是表示在超时时间内没有数据可读。这种情况下,您可以根据具体需求来判断是否认为连接已经断开。

  2. 错误发生:在某些错误情况下,read()方法也可能返回-1。例如,网络中断、连接重置等。这时候,read()方法返回-1可能表示连接已经断开,但也可能是由于其他错误导致的。为了确定连接的状态,您可能需要根据具体的错误类型进行额外的处理和判断。

因此,仅仅依靠read()方法返回-1是无法绝对确定连接已经断开的,需要结合其他上下文信息和错误处理来判断连接的状态。

上述适合不停读取消息的场景下判断连接状态

心跳机制

1.sendUrgentData()捕获IO异常,(接收端要处理、过滤1字节消息)

     try {
          mSocket.sendUrgentData(0xFF);
     } catch (IOException e) {
          e.printStackTrace();
          Log.e(TAG,"tcp HeartbeatThread run error = " + e.toString());
     }
      mOutputStream.write(heartbeatByte);
      mOutputStream.flush();

SocketChannelAsynchronousSocketChannel是Java NIO库提供的非阻塞I/O的方式,适用于需要高效、异步的网络通信。而Socket类是传统的阻塞式I/O的方式,适用于简单的网络通信场景。