ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

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

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

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



文章目录:

  • 摘要
  • 代码示例
      • 约定好协议:
      • 请求Request:
      • 响应Response:
      • 服务器:
      • 客户端:
  • 总结

摘要

只懂协议理论不动手等于没学。本文基于 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(); } }

客户端:

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(); } }

总结

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

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进