Java网络编程 ServeSocket、Socket 方法 setSoTimeout() 详解

- ServerSocket的setSoTimeout(20000) :

单位为毫秒,用于设置20s内无客户端 Socket 连接,则抛出SocketTimeoutException异常。

  • ServerSocket的setSoTimeout(20000)示例代码如下:
//ServerSocketDemo
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketTimeoutException;

public class ServerSocketDemo extends Thread {

    private ServerSocket serverSocket;
    private int i = 1;

    public ServerSocketDemo(int port) throws IOException {
        serverSocket = new ServerSocket(port);
        //设置20s内无客户端连接,则抛出SocketTimeoutException异常
        serverSocket.setSoTimeout(20000);
    }

    public void run(){
        while(true) {
            System.out.println("服务端第"+i+"次启动中...对应的端口号为:"+ serverSocket.getLocalPort());
            i++;
            try {
                Socket server = serverSocket.accept();
                
                //当服务端监听到客户端的连接后才会执行以下代码
                System.out.println("服务端打印的远程主机地址为:"+server.getRemoteSocketAddress());

                //监听来自客户端的消息
                DataInputStream dis = new DataInputStream(server.getInputStream());
                System.out.println("服务端接收到的来自于客户端的信息为:"+dis.readUTF());

                //通过socket向客户端发送信息
                DataOutputStream dos = new DataOutputStream(server.getOutputStream());
                dos.writeUTF("我是服务端,您已连接到:"+server.getLocalSocketAddress());
                server.close();

            }catch (SocketTimeoutException e){
                System.out.println("20s内无客户端连接,正在关闭服务端监听服务");
                continue;
            }catch (IOException e) {
                e.printStackTrace();
                break;
            }


        }
    }

    public static void main(String[] args) {
        try {
            Thread t1 = new ServerSocketDemo(8089);
            t1.run();
        }catch(IOException e){
            e.printStackTrace();
            return;
        }
    }

}
//ClientSocketDemo
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;
import java.util.logging.SocketHandler;

public class ClientSocketDemo {

    public static void main(String[] args) {
        String serverName = "localhost";
        int port = 8089;
        try {
            Socket client = new Socket(serverName, port);

            //当客户端连接到服务端后,才会执行以下代码
            System.out.println("客户端已连接到远程主机地址:"+client.getRemoteSocketAddress());

            //向服务端发送消息
            DataOutputStream dos = new DataOutputStream(client.getOutputStream());
            dos.writeUTF("服务端你好,我是客户端,我的地址是: "+client.getLocalSocketAddress());

            //接收来自服务端的消息
            DataInputStream dis = new DataInputStream(client.getInputStream());
            System.out.println("来自于服务端的消息:"+dis.readUTF());
            client.close();

        } catch (IOException e) {
            e.printStackTrace();
            return;
        }
    }

}
  • 两个类的执行结果如下:
//ServerSocketDemo
服务端第1次启动中...对应的端口号为:8089
服务端打印的远程主机地址为:/127.0.0.1:62770
服务端接收到的来自于客户端的信息为:服务端你好,我是客户端,我的地址是: /127.0.0.1:62770
服务端第2次启动中...对应的端口号为:8089
20s内无客户端连接,正在关闭服务端监听服务
服务端第3次启动中...对应的端口号为:8089
20s内无客户端连接,正在关闭服务端监听服务

//ClientSocketDemo
客户端已连接到远程主机地址:localhost/127.0.0.1:8089
来自于服务端的消息:我是服务端,您已连接到:/127.0.0.1:8089

Process finished with exit code 0
- Socket的setSoTimeout(5) :
public void setSoTimeout(int timeout) throws SocketException
//启用/禁用带有指定超时值的 SO_TIMEOUT,以毫秒为单位。将此选项设为非零的超时值时,在与此 Socket 关联的 InputStream 上调用 read() 将只阻塞此时间长度。
//如果超过超时值,将引发 java.net.SocketTimeoutException,虽然 Socket 仍旧有效。选项必须在进入阻塞操作前被启用才能生效。
//超时值必须是 > 0 的数。超时值为 0 被解释为无穷大超时值。
//参数
//timeout - 指定的以毫秒为单位的超时值。
//抛出:
//SocketException - 如果底层协议出现错误,例如 TCP 错误。
  • 简单来说就是Socket对象的接收消息的非阻塞实现,注意此方法只是针对read()方法,上述的DataInputStream调用readUTF()方法无效,在固定时间内没有得到结果,就会结束本次阻塞,等待进行下一次的阻塞轮询。也就实现了应用层面的非阻塞。具体含义是指:在5毫秒内,如果没有读取完客户端发送的消息,就会停止读取,并抛出SocketTimeoutException异常。
  • 示例代码如下:
//ServerSocketDemo
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketTimeoutException;

public class ServerSocketDemo extends Thread {

    private ServerSocket serverSocket;
    private int i = 1;

    public ServerSocketDemo(int port) throws IOException {
        serverSocket = new ServerSocket(port);
        //设置20s内无客户端连接,则抛出SocketTimeoutException异常
        serverSocket.setSoTimeout(20000);
    }

    public void run(){
        while(true) {
            System.out.println("服务端第"+i+"次启动中...对应的端口号为:"+ serverSocket.getLocalPort());
            i++;
            try {
                Socket server = serverSocket.accept();

                // 接收消息非阻塞实现。
                // 注意此方法只是针对read()方法,下方的DataInputStream调用readUTF()方法无效
                // 方法设置超时时间,在固定时间内没有得到结果,就会结束本次阻塞,等待进行下一次的阻塞轮询。也就实现了应用层面的非阻塞。
                // 具体含义是指:在5毫秒内,如果没有读取完客户端发送的消息时,就会停止读取,并抛出SocketTimeoutException异常
                server.setSoTimeout(5);

                //当服务端监听到客户端的连接后才会执行以下代码
                System.out.println("服务端打印的远程主机地址为:"+server.getRemoteSocketAddress());
                
                /*
                * server.setSoTimeout(5);起作用是在以下代码的情况下inputStream调用read()方法时才起作用。
                * */
                InputStream inputStream = server.getInputStream();
                byte[] bytes = new byte[1024];
                while ((inputStream.read(bytes))!=-1){
                    System.out.println("在5ms内读取的内容有:"+bytes.toString());
                }

				//以下代码不会执行,因为5ms内读取不完客户端发送的消息,会产生SocketTimeoutException异常,接着进入下一次阻塞轮询
                //通过socket向客户端发送信息
                DataOutputStream dos = new DataOutputStream(server.getOutputStream());
                dos.writeUTF("我是服务端,您已连接到:"+server.getLocalSocketAddress());
                server.close();

            }catch (SocketTimeoutException e){
                System.out.println("5ms内未读取完客户端的信息,正在关闭服务端监听服务");
                continue;
            }catch (IOException e) {
                e.printStackTrace();
                break;
            }


        }
    }

    public static void main(String[] args) {
        try {
            Thread t1 = new ServerSocketDemo(8089);
            t1.run();
        }catch(IOException e){
            e.printStackTrace();
            return;
        }
    }

}
//ClientSocketDemo
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;
import java.util.logging.SocketHandler;

public class ClientSocketDemo {

    public static void main(String[] args) {
        String serverName = "localhost";
        int port = 8089;
        try {
            Socket client = new Socket(serverName, port);

            //当客户端连接到服务端后,才会执行以下代码
            System.out.println("客户端已连接到远程主机地址:"+client.getRemoteSocketAddress());

            //向服务端发送消息
            DataOutputStream dos = new DataOutputStream(client.getOutputStream());
            dos.writeUTF("服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n123123"+
                    "我的地址是: "+client.getLocalSocketAddress());

            //接收来自服务端的消息
            DataInputStream dis = new DataInputStream(client.getInputStream());
            System.out.println("来自于服务端的消息:"+dis.readUTF());
            client.close();

        } catch (IOException e) {
            e.printStackTrace();
            return;
        }
    }

}
  • 两个类的执行结果如下:
//ServerSocketDemo
服务端第1次启动中...对应的端口号为:8089
服务端打印的远程主机地址为:/127.0.0.1:63446
在5ms内读取的内容有:[B@4a574795
在5ms内读取的内容有:[B@4a574795
在5ms内读取的内容有:[B@4a574795
在5ms内读取的内容有:[B@4a574795
在5ms内读取的内容有:[B@4a574795
在5ms内读取的内容有:[B@4a574795
在5ms内读取的内容有:[B@4a574795
在5ms内读取的内容有:[B@4a574795
在5ms内读取的内容有:[B@4a574795
在5ms内读取的内容有:[B@4a574795
在5ms内读取的内容有:[B@4a574795
在5ms内读取的内容有:[B@4a574795
5ms内未读取完客户端的信息,正在关闭服务端监听服务
服务端第2次启动中...对应的端口号为:8089

//ClientSocketDemo
客户端已连接到远程主机地址:localhost/127.0.0.1:8089
//同时客户端也处于阻塞状态,不会关闭应用进程。因为客户端在等待服务端返回消息

Process finished with exit code 0
  • 注:为什么 dos.writeUTF() 方法传递了这么长的字符串?
    为了让服务端在 5ms 内读取不完,从而触发 SocketTimeoutException 异常。