一、Socket和Client

1、Socket是客户端创建的通信端点,比较官方的翻译是:套接字,但是这个翻译对于初学者来说,比较抽象,不易理解。为了方便理解,我这里举个简单的例子。

我们可以把客户端比作电话,服务器端比作某移动公司的客服电话,当你拨打该客服电话时,需要输入电话号码,此时这个号码我们就可以理解为服务器的地址(IP),但是由于客服业务众多,就算电话接通后,也并不知道你要办理或咨询什么业务,此时会提示你按某数字键来确定你想要办理的业务,此时的数字键我们就可以理解为服务器的端口号(PORT),只有当IP和PORT都确定了,服务器才能知道你想要的是具体的哪一个服务。

所以,当我们创建客户端与服务器的通信端点(套接字对象)时,我们需要告诉它服务器的IP和端口号。

2、下面代码是使用Socket + IO流实现的简易版Client

package com.xiao.socket;

import java.io.*;
import .Socket;

/**
 * @description Socket 简易版客户端
 * @auther: 笑笑是一个码农
 * @date: 22:34 2020/11/20
 */
public class Client {

    // 默认服务器主机地址
    final static String DEFAULT_SERVER_HOST = "127.0.0.1";
    // 默认服务器端口
    final static int DEFAULT_SERVER_PORT = 8888;
    /**
     * 默认客户端退出指令定义
     */
    final static String QUIT = "quit";

    public static void main(String[] args) {

        Socket socket = null;
        BufferedReader reader = null;
        BufferedWriter writer = null;
        try {
            // 创建Socket
            socket = new Socket(DEFAULT_SERVER_HOST, DEFAULT_SERVER_PORT);
            System.out.println("[客户端日志] 已连接服务器");

            // 创建输入流,读取服务器发送的数据
            reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            // 创建输出流,向服务器发送数据
            writer = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));

            // 等待用户输入信息(TODO 思考:有几种方式实现? 这里为什么用这种方式?)
            BufferedReader consoleReader = new BufferedReader(new InputStreamReader(System.in));
            while (true){
                String input = consoleReader.readLine();
                // 发送消息给服务器
                writer.write(input +"\n");
                writer.flush();
                System.out.println("[客户端日志] 客户端发送消息:" + input);

                // 读取服务器返回的消息
                String serverMessage = reader.readLine();
                System.out.println("[客户端日志] 接收到服务器消息:["+serverMessage+"]");

                if (input.equals(QUIT)){
                    System.out.println("[客户端日志] 退出客户端");
                    break;
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            close(writer);
        }
    }

    /**
     * 关闭方法抽取
     * @param writer
     */
    private static void close(BufferedWriter writer) {
        if (writer != null){
            try {
                writer.close(); // 会自动关闭socket
                System.out.println("[客户端日志] 关闭客户端的socket");
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

二、ServerSocket和Server

1、ServerSocket是服务端监听通信的端点,其accept方法,会返回一个与客户端建立通信端点的Socket对象,通过上面的例子可知,我们在创建服务器端监听的通信端点时,需要指定监听的端口号,告诉服务器,我们是在哪个端口办理业务。

2、下面代码是使用ServerSocket+IO流实现的简易版Server

 

package com.xiao.socket;

import java.io.*;
import .ServerSocket;
import .Socket;

/**
 * @description ServerSocket 简易服务器
 * @auther: 笑笑是一个码农
 * @date: 22:12 2020/11/20
 */
public class Server {

    // 默认端口
    final static int DEFAULT_PORT = 8888;

    /**
     * 默认客户端退出指令定义
     */
    final static String QUIT = "quit";

    public static void main(String[] args) {

        ServerSocket serverSocket = null;
        try {
            // 绑定监听端口
            serverSocket = new ServerSocket(DEFAULT_PORT);
            System.out.println("[服务器日志] 服务器已启动,当前监听端口:" + DEFAULT_PORT);
            while (true){
                // accept方法,等待客户端连接,会阻塞当前线程,有客户端建立连接时,会返回一个Socket对象,使用该对象,就可以跟客户端通信
                Socket socket = serverSocket.accept();
                System.out.println("[服务器日志] 客户端["+socket.getInetAddress().getHostAddress()+":"+socket.getPort()+"]已连接");
                // 获取客户端的输入数据reader对象,读取客户端发送过来的数据
                BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
                // 获取向客户端发送数据writer对象,向客户端发送数据
                BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));

                String message;

                // 循环读取某个客户端发的消息
                // null判断,客户端的输入流关闭或不可用,读取的为null
                while ((message = reader.readLine()) != null) {
                    System.out.println("[服务器日志] 接收到客户端["+socket.getPort()+"]消息:" + message);
                    // 回复客户端消息
                    writer.write("服务器回复消息:" + message + "\n");
                    writer.flush();

                    // 客户端是否退出
                    if (QUIT.equals(message)){
                        System.out.println("[服务器日志] 客户端["+socket.getPort()+"]已断开连接");
                        break;
                    }
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            close(serverSocket);
        }
    }

    /**
     * 抽取关闭方法
     * @param serverSocket
     */
    private static void close(ServerSocket serverSocket) {
        if (serverSocket != null){
            try {
                serverSocket.close();
                System.out.println("[服务器日志] 关闭serverSocket");
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

 

三、源码地址

https://gitee.com/smile-coding/my-code 在io模型(BIO/NIO/AIO)目录下

如果对您有帮助,点个小心心(Star)支持一下哈~