User_芊芊君子头像
【JavaEE初阶 - 网络原理】用 TCP Socket 写个网络计算器,你就懂协议了封面图

【JavaEE初阶 - 网络原理】用 TCP Socket 写个网络计算器,你就懂协议了

在这里插入图片描述

🎁个人主页:User_芊芊君子
🎉欢迎大家点赞👍评论📝收藏⭐文章
🔍系列专栏:JavaEE

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

摘要

只懂协议理论不动手等于没学。本文基于 Java TCP
Socket,从零实现一个网络版计算器:约定"运算符+操作数"的请求格式与运算结果的响应格式,完成 Request/Response 的序列化与反序列化,服务端用线程池支持多客户端并发,客户端从控制台循环读取输入。文末附完整可运行代码与运行效果图。

在这里插入图片描述

代码示例

约定好协议:

明确传递的数据

  • 请求:运算符(±*/);操作数

  • 响应:运算结果

组织传输 数据格式

  • 自定义文本格式

  • 请求:运算符,操作数1,操作数\n

  • 响应:结果\n

请求Request:

package application;
//表示一个请求
public class Request {
    private String operator;
    private double num1;
    private double num2;

    public Request(String operator, double num1, double num2) {
        this.operator = operator;
        this.num1 = num1;
        this.num2 = num2;
    }
    //对象-->字符串
    public String covertToString(){
        //String.format():类似printf,但只是构造到String中,没有打印
        return String.format("%s,%f,%f\n",operator,num1,num2);
    }
    public static Request convertFromString(String request){
        //1.获取参数
        String[] split = request.split(",");//切分
        String opertor = split[0];
        double num1 = Double.parseDouble(split[1]);//字符串转成double
        double num2 = Double.parseDouble(split[2]);

        //2.创建对象
        return new Request(opertor,num1,num2);
    }

    public String getOperator() {
        return operator;
    }

    public double getNum1() {
        return num1;
    }

    public double getNum2() {
        return num2;
    }
}

响应Response:

package application;

public class Response {
    private double result;
    Response(double result){
        this.result = result;
    }
    //转换成字符串
    public String convertToString(){
        return String.format("%f\n",result);
    }

    //把字符串转换成对象
    public static Response convertFromString(String response){
        double result = Double.parseDouble(response);
        return new Response(result);
    }
    public double getResult() {
        return result;
    }
}

服务器:

package application;

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.ExecutorService;
import java.util.concurrent.Executors;

public class TcpCalcServer {
    private ServerSocket serverSocket = null;
    public TcpCalcServer(int port)throws IOException{
        serverSocket = new ServerSocket(port);
    }
    public void start() throws IOException{
        System.out.println("服务器启动");

        ExecutorService service = Executors.newCachedThreadPool();

        while (true){
            Socket socket = serverSocket.accept();
            service.submit(()->{
                try {
                    processConnection(socket);
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            });
        }
    }

    private void processConnection(Socket socket) throws IOException {
        System.out.printf("[%s:%d 客户端上线!]",socket.getInetAddress().toString(),socket.getPort());

        try (InputStream inputStream = socket.getInputStream();
             OutputStream outputStream = socket.getOutputStream()){

            Scanner scanner = new Scanner(inputStream);
            PrintWriter printWriter = new PrintWriter(outputStream);

            while (true){
                //1.读取请求并解析
                if (!scanner.hasNext()){
                    break;
                }
                String requestString = scanner.next();
                //反序列化
                Request request = Request.convertFromString(requestString);
                //2.根据请求计算响应
                Response response = process(request);
                //3.把响应写回客户端
                String responseString = response.convertToString();
                //构造String时已经带了\n,直接print写入即可
                printWriter.print(responseString);
                printWriter.flush();
            }

        }catch (IOException e){
            e.printStackTrace();
        }finally {
            System.out.printf("[%s:%d] 客户端下线!\n",socket.getInetAddress().toString(),socket.getPort());
            socket.close();
        }
    }

    private Response process(Request request) {
        //根据参数和运算符完成计算,构造响应
        if(request.getOperator().equals("+")){
            return new Response(request.getNum1() + request.getNum2());
        }else if(request.getOperator().equals("-")){
            return new Response(request.getNum1() - request.getNum2());
        }else if(request.getOperator().equals("*")){
            return new Response(request.getNum1() * request.getNum2());
        }else if(request.getOperator().equals("/")){
            return new Response(request.getNum1() / request.getNum2());
        }else {
            throw new RuntimeException("不支持的运算符:"+request.getOperator());
        }
    }

    public static void main(String[] args) throws IOException {
        TcpCalcServer server = new TcpCalcServer(9090);
        server.start();
    }
}

img

客户端:

package application;

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 TcpCalcClient {
    private Socket socket = null;

    public TcpCalcClient(String serverIp,int serverPort) throws IOException {
        socket = new Socket(serverIp,serverPort);
    }
    public void start(){
        System.out.println("客户端运行中");

        try (InputStream inputStream = socket.getInputStream();
             OutputStream outputStream = socket.getOutputStream()){

            Scanner scannerConsole = new Scanner(System.in);
            Scanner scannerNetwork = new Scanner(inputStream);
            PrintWriter printWriter = new PrintWriter(outputStream);

            while (true){
                //1.从控制台读取用户输入内容
                System.out.println("请输入要进行的运算(+ - * /)");
                String operator = scannerConsole.next();
                System.out.println("请输入第一个数字:");
                double num1 = scannerConsole.nextDouble();
                System.out.println("请输入第二个数字:");
                double num2 = scannerConsole.nextDouble();

                //2.构造请求并发送
                Request request = new Request(operator,num1,num2);
                String requsetString = request.covertToString();
                printWriter.println(requsetString);
                printWriter.flush();

                //3.读取服务器响应结果
                if(!scannerNetwork.hasNext()){
                    break;
                }
                String responseString = scannerNetwork.next();
                Response response = Response.convertFromString(responseString);

                //4.打印响应结果
                System.out.println(response.getResult());
            }
        }catch (IOException e){
            e.printStackTrace();
        }
    }

    public static void main(String[] args) throws IOException {
        TcpCalcClient client = new TcpCalcClient("127.0.0.1",9090);
        client.start();
    }
}

img

总结

一个标准的网络程序 = 协议设计 + 序列化/反序列化 + Socket 收发循环。服务端固定 accept → 读请求 → 算响应 → 写回的流程,客户端则是 构造请求 → 发送 → 等响应 → 展示。多客户端并发借助线程池即可实现。这套骨架就是以后写任何 TCP业务服务端的模板。
在这里插入图片描述
在这里插入图片描述

转载自 CSDN-专业IT技术社区

原文链接:https://blog.csdn.net/user340/article/details/166009973

文章来源转载

评论

评论列表

微信小程序
QQ小程序

关于作者

点赞数:0
关注数:0
粉丝:0
文章:0
关注标签:0
加入于:--