• Java 简单实现一个 TCP 回显服务器



    TCP 服务端

    package network;
    
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.OutputStream;
    import java.io.PrintWriter;
    import java.net.ServerSocket;
    import java.net.Socket;
    import java.util.Scanner;
    import java.util.concurrent.Executor;
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    
    public class TcpEchoServer {
        ServerSocket serverSocket = null;
    
        // 此处不应该创建固定线程数目的线程池.
        private ExecutorService service = Executors.newCachedThreadPool();
    
        // 绑定端口号
        public TcpEchoServer(int port) throws IOException {
            serverSocket = new ServerSocket(port);
        }
    
        public void start() throws IOException {
            System.out.println("服务器启动!");
            while (true) {
                Socket clientSocket = serverSocket.accept();
    
                service.submit(new Runnable() {
                    @Override
                    public void run() {
                        processConnection(clientSocket);
                    }
                });
    
    /*            Thread thread = new Thread(() -> {
                    processConnection(clientSocket);
                });
                thread.start();*/
            }
        }
    
        // 通过这个方法来处理一个连接的逻辑.
        private void processConnection(Socket clientSocket) {
            System.out.printf("[%s:%d] 客户端上线!\n", clientSocket.getInetAddress().toString(), clientSocket.getPort());
            // 接下来就可以读取请求, 根据请求计算响应, 返回响应三步走了.
            // Socket 对象内部包含了两个字节流对象, 可以把这俩字节流对象获取到, 完成后续的读写工作
            try (InputStream inputStream = clientSocket.getInputStream();
                 OutputStream outputStream = clientSocket.getOutputStream()) {
                while (true) {
                    // 1. 获取请求并解析, 使用 Scanner 来读取
                    Scanner scanner = new Scanner(inputStream);
                    if (!scanner.hasNext()) {
                        // 读取完毕, 客户端下线!
                        System.out.printf("[%s:%d] 客户端下线!\n", clientSocket.getInetAddress().toString(), clientSocket.getPort());
                        break;
                    }
    
                    // 这个代码暗含一个约定, 客户端发过来的请求, 得是文本数据, 同时, 还得带有空白符作为分割. (比如换行这种)
                    String request = scanner.next();
    
                    // 2. 根据请求计算响应结果
                    String response = process(request);
    
                    // 3. 把响应结果写回客户端
                    //    把 OutputStream 使用 PrinterWriter 包裹一下, 方便进行发数据.
                    PrintWriter writer = new PrintWriter(outputStream);
                    //    使用 PrintWriter 的 println 方法, 把响应返回给客户端.
                    //    此处用 println, 而不是 print 就是为了在结尾加上 \n . 方便客户端读取响应, 使用 scanner.next 读取.
                    writer.println(response);
                    //    这里还需要加一个 "刷新缓冲区" 操作.
                    writer.flush();
    
                    // 4. 日志显示当前请求详情.
                    System.out.printf("[%s:%d] req: %s, resp: %s\n", clientSocket.getInetAddress().toString(), clientSocket.getPort(),
                            request, response);
                }
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                try {
                    clientSocket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    
        public String process(String request) {
            return request;
        }
    
        public static void main(String[] args) throws IOException {
            TcpEchoServer server = new TcpEchoServer(9090);
            server.start();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98

    TCP 客户端

    package network;
    
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.OutputStream;
    import java.io.PrintWriter;
    import java.net.Socket;
    import java.util.Scanner;
    
    public class TcpEchoClient {
        private Socket socket = null;
    
        // 要和服务器通信, 就需要先知道, 服务器所在的位置.
        public TcpEchoClient(String serverIp, int serverPort) throws IOException {
            socket = new Socket(serverIp, serverPort);
        }
    
        public void start() {
            System.out.println("客户端启动!");
    
            Scanner scannerConsole = new Scanner(System.in);
    
            try (InputStream inputStream = socket.getInputStream();
                 OutputStream outputStream = socket.getOutputStream()) {
                while (true) {
                    // 1. 在控制台上输入字符串.
                    System.out.print("-> ");
                    String request = scannerConsole.next();
                    // 2. 将请求发送给服务器.
                    PrintWriter writer = new PrintWriter(outputStream);
                    writer.println(request);
                    writer.flush();
    
                    // 3. 从服务器读取响应.
                    Scanner scannerNetwork = new Scanner(inputStream);
                    String response = scannerNetwork.next();
    
                    // 4. 显示响应.
                    System.out.println(response);
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    
        public static void main(String[] args) throws IOException {
            TcpEchoClient client = new TcpEchoClient("localHost", 9090);
            client.start();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50

    实现效果

    在这里插入图片描述
    在这里插入图片描述

    TCP 服务端(实现字典功能)

    只需要继承自之前写的服务端, 然后重写父类 process 方法即可. 添加一些查询字典的逻辑.

    package network;
    
    import java.io.IOException;
    import java.util.HashMap;
    
    public class TcpDictServer extends TcpEchoServer {
        private HashMap<String, String> dict = new HashMap<>();
    
        public TcpDictServer(int port) throws IOException {
            super(port);
            dict.put("dog", "小狗");
            dict.put("cat", "小猫");
            dict.put("hello", "你好");
        }
    
        @Override
        public String process(String request) {
            return dict.getOrDefault(request, "查询的单词不存在!");
        }
    
        public static void main(String[] args) throws IOException {
            TcpDictServer server = new TcpDictServer(9090);
            server.start();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    效果

    在这里插入图片描述

    在这里插入图片描述


    总结

    ✨ 本文记录了一个简单的 TCP 回显服务器代码.
    ✨ 想了解更多计算机网络的知识, 可以收藏一下本人的计算机网络学习专栏, 里面会持续更新本人的学习记录, 跟随我一起不断学习.
    ✨ 感谢你们的耐心阅读, 博主本人也是一名学生, 也还有需要很多学习的东西. 写这篇文章是以本人所学内容为基础, 日后也会不断更新自己的学习记录, 我们一起努力进步, 变得优秀, 小小菜鸟, 也能有大大梦想, 关注我, 一起学习.

    再次感谢你们的阅读, 你们的鼓励是我创作的最大动力!!!!!

  • 相关阅读:
    Kafka 消息队列 ( 二 ) 安装
    QEMU显示虚拟化的几种选项
    LINQ to SQL (Group By/Having/Count/Sum/Min/Max/Avg操作符)
    JAVA该怎么学习,到公司会学的一样吗
    题记(43)--C翻转(矩阵旋转)
    背就有效!2024下《系统架构设计师》50个高频考点汇总
    第67步 时间序列建模实战:ARIMA建模(Stata)
    《python趣味工具》——酷炫二维码(3)计算机二级考试题
    深入理解FFmpeg--libavformat接口使用(一)
    万界星空科技低代码平台+协同制造MES产品
  • 原文地址:https://blog.csdn.net/qq_60366454/article/details/134360633