Java-Socket

246 阅读3分钟

基础介绍

TCP是面向连接的,将连接作为了最基本的抽象。

TCP连接的端点称为套接字(socket)或插口。

根据RFC793的定义:端口号拼接到IP地址即构成了套接字。

——《计算机网络第七版》P212

常用工具类

Socket

套接字类,提供获取输入数据流和写出数据流的功能。

常用方法
方法介绍
Socket(String host, int port)根据主机地址host和端口号port构建套接字
InputStream getInputStream()获取读取数据的流
OutputStream getOutputStream()获取写出数据的流
void connect(SocketAddress Address)使套接字连接到指定的地址
void connect(SocketAddress Address, int timeoutMilliseconds)使套接字连接到指定的地址,并设置超时时间,如果超时InterruptedIOException异常
void setSoTimeout(int timeout)指定连接超时时间(以毫秒为单位
Boolean isConnected()是否已经连接
Boolean isClose()是否已经关闭连接
void close()关闭套接字
常见问题
套接字超时问题-初始连接超时
  • 情况介绍

平常使用Socket(String host, int port)方法进行套接字构建,但是这个方法的缺陷时,如果目的主机不可达则会一直阻塞,只会受到底层系统的限制并才抛出异常。

  • 解决办法

为连接设置超时时间 可以构建一个空的套接字(Socket),然后调用void connect(SocketAddress Address, int timeoutMilliseconds)方法设置连接超时时间,超时抛出InterruptedIOException异常。

  • InetSocketAddress | 方法 | 介绍 | | --- | --- | | InetSocketAddress(String host, int port) | 根据主机地址host和端口port创建一个InetSocketAddress |
  • Demo
// 原方式
try {
    Socket socket = new Socket("127.0.0.1", 8081);
} catch (IOException e) {
    e.printStackTrace();
}

// 新方式
try {
    Socket socket = new Socket();
    socket.connect(new InetSocketAddress("127.0.0.1", 8081), 100000);
} catch (IOException e) {
    e.printStackTrace();
}
套接字超时问题-连接中断导致读写超时
  • 情况介绍

在连接成功之后,可能会遇到连接中断的情况。很可能在读取数据或者写数据未完成时中断,但是又不会抛出异常导致一直阻塞,只会受到底层系统的限制并才抛出异常。

  • 解决办法

使用setSoTimeout()方法指定连接超时时间,超过时间会抛出SocketTimeoutException,可以捕获异常进行处理操作。

  • Demo

注意try catch语句和while的嵌套关系,while一般会放在try catch内部,不容易出现死循环抛出异常的情况。

try {
    Socket socket = new Socket();
    socket.connect(new InetSocketAddress("127.0.0.1", 8084));
    socket.setSoTimeout(10000);

    while (true) {
        InputStream is = socket.getInputStream();
        BufferedReader br = new BufferedReader(new InputStreamReader(is));
        System.out.println(br.readLine());
    }
    
} catch (SocketTimeoutException e) {
    e.printStackTrace();
} catch (SocketException e) {
    e.printStackTrace();
} catch (IOException e) {
    e.printStackTrace();
}

ServerSocket

服务器套接字类,用于创建套接字(Socket)对象。

常用方法
方法介绍
ServerSocket(String host, int port)创建服务器套接字
Socket accept()获取连接,返回套接字Socket对象。如果未获取到连接会一直阻塞,直到获取成功为止。
void close()关闭服务器套接字
常见问题

Socket Demo

Server

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * Socket Demo - Server
 * @author zhangyao
 * @version 1.0
 */
public class ServerDemo {
    public static void main(String[] args) throws IOException {
        ExecutorService executorService = Executors.newCachedThreadPool();
        try (ServerSocket serverSocket = new ServerSocket(8084)) {
            while (true) {
                Socket socket = serverSocket.accept();
                executorService.submit(new ServerRunnable(socket));
            }
        }
    }

    static class ServerRunnable implements Runnable {
        private Socket socket;

        public ServerRunnable (Socket socket) {
            this.socket = socket;
        }

        @Override
        public void run() {
            System.out.println(Thread.currentThread().getName());
            PrintStream ps = null;
            BufferedReader br = null;
            try (InputStream is = socket.getInputStream();OutputStream os = socket.getOutputStream()) {
                br = new BufferedReader(new InputStreamReader(is));
                ps = new PrintStream(os);
                while (true) {
                    ps.println("欢迎连接服务器");
                    ps.flush();

                    while (true) {
                        String str = br.readLine();
                        ps.println("your input: " + str);
                        ps.flush();
                    }
                }
            } catch (IOException e) {
                e.printStackTrace();
                System.out.println(Thread.currentThread().getName() + "->退出");
            }
        }
    }
}

Client

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.net.Socket;
import java.util.Scanner;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

/**
 * Socket Demo - Client
 * @author zhangyao
 * @version 1.0
 */
public class ClientDemo {
    public static void main(String[] args) throws IOException {
        try (Socket socket = new Socket("127.0.0.1", 8084)) {
            ReentrantLock lock = new ReentrantLock();
            Condition condition = lock.newCondition();

            new Thread(new ReadRunnable(socket, lock, condition)).start();
            new Thread(new WriteRunnable(socket, lock, condition)).start();
        }
    }

    public static class ReadRunnable implements Runnable {
        ReentrantLock lock;
        Condition condition;
        BufferedReader br;
        Socket socket;

        public ReadRunnable(Socket socket, ReentrantLock lock, Condition condition) {
            this.socket = socket;
            this.lock = lock;
            this.condition = condition;
        }

        @Override
        public void run() {
            try (BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(socket.getInputStream()))) {
                this.br = bufferedReader;

                while (true) {
                    String str = br.readLine();
                    System.out.println(str);

                    if (!br.ready()) {
                        condition.signalAll();
                        condition.await();
                    }
                }
            } catch (IOException | InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public static class WriteRunnable implements Runnable {
        ReentrantLock lock;
        Condition condition;
        Socket socket;
        PrintStream ps;

        public WriteRunnable(Socket socket, ReentrantLock lock, Condition condition) {
            this.socket = socket;
            this.lock = lock;
            this.condition = condition;
        }

        @Override
        public void run() {
            try (PrintStream printStream = new PrintStream(socket.getOutputStream())) {
                this.ps = printStream;

                while (true) {
                    try (Scanner scanner = new Scanner(System.in)) {
                        String str = scanner.nextLine();
                        if (str != null) {
                            ps.println(str);
                            ps.flush();

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