JavaSocket系列-TCP链接

2021/4/24 JavaSocket

# 简单介绍

基于 TCP/IP协议 (opens new window)Java方式的Socket实现,它是 流套接字 (SOCK_STREAM) 的一种传输类型,是 面向连接 的服务类型。通过 Socket 数据流的方式进行通信和交互,特点是稳定性链接和长链接,适合重要数据、长时间持续通信、安全交互等等场景。

其实 socket 在我们日常使用的许多互联网软件中是很常见的,用于需要稳定和长时间的连接进行相关交互的场景,接下来开始我们的 JavaSocket TCP 的相关实战吧~

# 实现流程

  1. 服务端创建一个指定端口的 ServerSocket
  2. 开启服务端Socket的监听,等待客户端的链接后接受并处理客户端请求;
  3. 创建一个客户端 SocketClient,链接到指定IP和端口ServerSocket
  4. 客户端链接完成后,发送请求给服务端并接受服务端的响应产生交互行为。
  5. 在服务完成指定的任务后,需要关闭客户端和服务端之间的连接,服务端也可以根据需要是否需要继续监听连接请求,决定是否关闭服务程序。

# 创建服务端代码

public class SocketServer {
    private static final String TAG = ServerSocket.class.getSimpleName();
    private int port;
    private boolean receive = true;
    private ServerSocket serverSocket;

    /**
     * 创建一个指定port的SocketServer对象.
     *
     * @param port 绑定指定的port.
     */
    public SocketServer(int port) {
        this.port = port;
    }

    /**
     * 创建SocketServer,并绑定指定的port,接收客户端的访问.
     *
     * @throws IOException
     */
    public synchronized void run() throws IOException {
        serverSocket = new ServerSocket(port);
        receive = true;
        while (receive) {
            System.out.println(TAG + " run: Server receiving...");
            Socket accept = serverSocket.accept();
            byte[] buff = new byte[1024 * 8];
            // 连接设备的address
            InetAddress inetAddress = accept.getInetAddress();
            // 连接设备的port
            int port = accept.getPort();
            System.out.println(TAG + " run: receive ==> client address = " + inetAddress + ", port = " + port);
            // 获取设备的输入流
            InputStream inputStream = accept.getInputStream();
            // 接收数据的字符缓冲
            StringBuilder sb = new StringBuilder();
            while (true) {
                int len = inputStream.read(buff);
                if (len == -1) {    // 断开链接
                    break;
                }
                // 将读取的字节流转换为字符对象,并append到StringBuilder中
                sb.append(new String(buff, 0, len, StandardCharsets.UTF_8));
                // 将接收到的消息打印出来
                System.out.println(TAG + " run: receive msg = " + sb.toString().trim());
                // 回复客户端消息
                accept.getOutputStream().write(("Server msg has receive: msg = " + sb.toString()).getBytes(StandardCharsets.UTF_8));
                // 清除StringBuilder中的数据
                sb.delete(0, sb.length());
            }
            // 关闭相关流对象
            inputStream.close();
            accept.close();
        }
    }

    /**
     * 关闭SocketServer.
     *
     * @throws IOException
     */
    public void shutDown() throws IOException {
        System.out.println(TAG + " shutDown:");
        if (null != serverSocket) {
            receive = false;
            serverSocket.close();
        }
    }

}

# 创建客户端代码

public class SocketClient {
    private static final String TAG = SocketClient.class.getSimpleName();
    private Socket socket;
    private OutputStream outputStream;
    private InputStream inputStream;
    private volatile boolean isReceiving = false;

    /**
     * 创建一个SocketClient,并且连接到指定host和port的Server.
     *
     * @param host 目标Server的host.
     * @param port 目标Server的port.
     * @throws IOException
     */
    public SocketClient(String host, int port) throws IOException {
        socket = new Socket(host, port);
        outputStream = socket.getOutputStream();
        inputStream = socket.getInputStream();
    }

    /**
     * 向Server发送指定的msg数据.
     *
     * @param msg 发送的字符串数据.
     * @throws IOException
     */
    public synchronized void sendMessage(String msg) throws IOException {
        if (null != msg && msg.length() > 0 && null != outputStream) {
            outputStream.write(msg.getBytes(StandardCharsets.UTF_8));
            outputStream.flush();
            System.out.println(TAG + " sendMessage: msg is send, msg = " + msg);
        }
    }

    /**
     * 断开Server的连接.
     *
     * @throws IOException
     */
    public synchronized void disConnect() throws IOException {
        isReceiving = false;
        if (null != socket) {
            if (null != outputStream) {
                outputStream.close();
            }
            if (null != inputStream) {
                inputStream.close();
            }
            inputStream = null;
            outputStream = null;
            socket.close();
            socket = null;
            System.out.println(TAG + " disConnect: ");
        }
    }

    /**
     * 接收Server的消息.
     */
    public void receiveMessage() {
        if (!isReceiving) {
            isReceiving = true;
            new Thread(new Runnable() {
                @Override
                public void run() {
                    if (null != inputStream) {
                        StringBuilder sb = new StringBuilder();
                        try {
                            while (true) {
                                byte[] buff = new byte[1024 * 8];
                                int readLen = inputStream.read(buff);
                                if (readLen == -1) {   // 断开链接
                                    break;
                                }
                                sb.append(new String(buff, 0, readLen, StandardCharsets.UTF_8));
                                System.out.println(TAG + " run: receiveMessage msg = " + sb.toString().trim());
                                sb.delete(0, sb.length());  // 一条消息读取结束,清除缓存
                            }
                        } catch (Exception e) {
                            isReceiving = false;
                            System.out.println(TAG + " receiveMessage finish");
                        }
                    }
                }
            }).start();
        } else {
            System.out.println(TAG + " receiveMessage: SocketClient has receiving...");
        }
    }
}

# 使用示例

/**
* Java Tcp Socket Demo.
*
* @throws IOException
*/
public static void javaTcpSocketDemo() throws Exception {
    // 启动一个JavaServerServer,绑定一个端口号,并监听客户端的链接
    SocketServer socketServer = new SocketServer(9999);
    new Thread(new Runnable() {
        @Override
        public void run() {
            try {
                // 开启Server
                socketServer.run();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }).start();

    Thread.sleep(500); // 多线程延迟,防止异步创建对象不同步问题
    // 本处ip是本地host,也可以是局域网下Server设备的实际ip
    SocketClient client = new SocketClient("127.0.0.1", 9999);
    client.receiveMessage(); // 客户端接收回复消息

    // 接收输入的数据发送给Server
    Scanner scanner = new Scanner(System.in);
    while (true) {
        Thread.sleep(500); // 数据输入发送间隔设置为500ms
        System.out.println("\n==> [Client --> Server]请输入发送的消息(输入quit退出):");
        String s = scanner.nextLine();
        if ("quit".equals(s)) {
            client.disConnect();
            socketServer.shutDown();
            break;
        }
        // 发送输入的字符串数据
        client.sendMessage(s);
    }
    System.out.println("------------------------- javaTcpSocketDemo finished -------------------------");
}

# 运行示例

运行程序后,SocketClient 通过控制台的输入的数据向 ServerSocket 发送了数据,服务端收到消息后反馈给客户端一个已经接收消息的反馈消息,最后客户端执行断开链接,当前的链接结束。

ServerSocket run: Server receiving...
ServerSocket run: receive ==> client address = /127.0.0.1, port = 41710

==> [Client --> Server]请输入发送的消息(输入quit退出):
Hello JavaSocket, I am JiangMing.
SocketClient sendMessage: msg is send, msg = Hello JavaSocket, I am JiangMing.
ServerSocket run: receive msg = Hello JavaSocket, I am JiangMing.
SocketClient run: receiveMessage msg = Server msg has receive: msg = Hello JavaSocket, I am JiangMing.

==> [Client --> Server]请输入发送的消息(输入quit退出):
quit
SocketClient disConnect: 
ServerSocket shutDown:
SocketClient receiveMessage finish

可以看到,程序已经成功的运行起来,Socket客户端(SocketClient)与服务端(ServerSocket)已经运行并产生了数据的交互了,好了简单的JavaSocket的TCP链接的使用介绍就到此结束了,有兴趣的朋友可以去参考相关资料,进行更深一步的研究~