编写Java程序,实现一个简单的echo程序(网络编程TCP实践练习)

简介: 编写Java程序,实现一个简单的echo程序(网络编程TCP实践练习)

首先启动服务端,客户端通过TCP的三次握手与服务端建立连接;


然后,客户端发送一段字符串,服务端收到字符串后,原封不动的发回给客户端。


ECHO 程序是网络编程通信交互的一个经典案例,称为回应程序,即客户端输入哪些内容,服务端会在这些内容前加上“ECHO”并将信息发回给客户端。

45.png46.png


EchoServer.java

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.net.ServerSocket;
import java.net.Socket;
public class EchoServer {
  private ServerSocket server;
  public EchoServer() throws IOException {
    // 创建服务端套接字对象
    this.server = new ServerSocket(8888);
    System.out.println("服务器启动" + "在" + 8888 + "端口监听连接请求");
  }
  // 服务器主要的处理逻辑
  public void service() {
    // 是否关闭服务端连接
    boolean flag = true;
    while (flag) {
      Socket socket = null;
      try {
        // 获取连接的客户端套接字对象
        socket = server.accept();
        // 获取socket相关的输入流和输出流
        BufferedReader reader = getReader(socket);
        BufferedWriter writer = getWriter(socket);
        // 保存客户端发送的数据
        String data = null;
        // 读取客户端发送的数据
        while ((data = reader.readLine()) != null) {
          // 当获取的信息是“bye”时,关闭流
          if ("bye".equals(data)) {
            flag = false;
            if (reader != null) {
              reader.close();
            }
            if (writer != null) {
              writer.close();
            }
            break;
          } else {
            System.out.println("来自客户端的数据:" + data);
            // 回显给客户端的数据
            writer.write("echo:" + data);
            // 插入一个行分割符,readLine()方法用来判定字符串有没结束
            writer.newLine();
            // 刷新输出缓冲区
            writer.flush();
          }
        }
      } catch (IOException e) {
        // 设置结束循环
        flag = false;
      } finally {
        try {
          if (server != null && (!server.isClosed())) {
            server.close();
            System.out.println("服务端关闭");
          }
        } catch (IOException e) {
          e.printStackTrace();
        }
      }
    }
  }
  // 获取socket相关的输入流对象
  private BufferedReader getReader(Socket socket) throws IOException {
    InputStream in = socket.getInputStream();
    return new BufferedReader(new InputStreamReader(in));
  }
  // 获取socket相关的输出流对象
  private BufferedWriter getWriter(Socket socket) throws IOException {
    OutputStream out = socket.getOutputStream();
    return new BufferedWriter(new OutputStreamWriter(out));
  }
  public static void main(String arg[]) throws IOException {
    new EchoServer().service();
  }
}


EchoClient.java


import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.net.Socket;
public class EchoClient {
  private Socket socket;
  public EchoClient() {
    try {
      // 创建客户端套接字对象
      this.socket = new Socket("127.0.0.1", 8888);
    } catch (IOException e) {
      e.printStackTrace();
    }
  }
  public void talkToServer() {
    System.out.println("请输入要发送给服务端的数据:");
    BufferedReader localReader = null;
    String data = null;
    // 获取客户端套接字相关的输入流和输出流
    BufferedReader reader = getReader(socket);
    BufferedWriter writer = getWriter(socket);
    try {
      // 创建读取用户输入的读取流
      localReader = new BufferedReader(new InputStreamReader(System.in));
      while ((data = localReader.readLine()) != null) {
        // 如果客户端输入的数据是"bye",则关闭I/O流
        if ("bye".equals(data)) {
          System.out.println("客服端关闭!");
          if (reader != null) {
            reader.close();
          }
          if (writer != null) {
            writer.close();
          }
          break;
        } else {
          // readLine方法必须读取到行分割符才返回读取。所以传递给输入流的字符串必须包含行分割符
          System.out.println("客户端输出的数据--->\t" + data);
          writer.write(data);
          // 插入一个行分隔符,作为内容结束的标识
          writer.newLine();
          // 非常重要的是必须显式的将数据推送到服务器哪里去
          writer.flush();
          // 读取服务端返回的数据
          System.out.println("服务器响应的数据--->\t " + reader.readLine());
        }
      }
    } catch (Exception e) {
      e.printStackTrace();
    } finally {
      // 关闭客户端套接字连接
      try {
        if (socket != null) {
          socket.close();
        }
      } catch (IOException e) {
        e.printStackTrace();
      }
    }
  }
  // 获取客户端套接字相关的输出流
  private BufferedWriter getWriter(Socket socket) {
    try {
      OutputStream out = null;
      BufferedWriter writer = null;
      out = socket.getOutputStream();
      writer = new BufferedWriter(new OutputStreamWriter(out));
      return writer;
    } catch (Exception e) {
      e.printStackTrace();
    }
    return null;
  }
  // 获取客户端套接字相关的输入流
  private BufferedReader getReader(Socket socket) {
    try {
      InputStream in = null;
      BufferedReader reader = null;
      in = socket.getInputStream();
      reader = new BufferedReader(new InputStreamReader(in));
      return reader;
    } catch (Exception e) {
      e.printStackTrace();
    }
    return null;
  }
  public static void main(String[] args) {
    new EchoClient().talkToServer();
  }
}
相关文章
|
2月前
|
存储 缓存 安全
Java内存模型深度解析:从理论到实践####
【10月更文挑战第21天】 本文深入探讨了Java内存模型(JMM)的核心概念与底层机制,通过剖析其设计原理、内存可见性问题及其解决方案,结合具体代码示例,帮助读者构建对JMM的全面理解。不同于传统的摘要概述,我们将直接以故事化手法引入,让读者在轻松的情境中领略JMM的精髓。 ####
47 6
|
2月前
|
设计模式 Java 开发者
Java中的异常处理:理解与实践
【10月更文挑战第42天】在Java的世界中,异常处理是每个开发者必须面对的挑战。它就像是一场不可预知的风暴,可能会在任何时候突然降临,打乱我们的计划。但是,如果我们能够掌握正确的处理方法,这场风暴也可以变成推动我们前进的力量。本文将带你深入理解Java中的异常处理机制,通过代码示例,我们将一起学习如何捕获、处理和预防异常,让你的程序在面对任何挑战时都能保持稳健和优雅。
|
3天前
|
Kubernetes Java 持续交付
小团队 CI/CD 实践:无需运维,Java Web应用的自动化部署
本文介绍如何使用GitHub Actions和阿里云Kubernetes(ACK)实现Java Web应用的自动化部署。通过CI/CD流程,开发人员无需手动处理复杂的运维任务,从而提高效率并减少错误。文中详细讲解了Docker与Kubernetes的概念,并演示了从创建Kubernetes集群、配置容器镜像服务到设置GitHub仓库Secrets及编写GitHub Actions工作流的具体步骤。最终实现了代码提交后自动构建、推送镜像并部署到Kubernetes集群的功能。整个过程不仅简化了部署流程,还确保了应用在不同环境中的稳定运行。
31 9
|
25天前
|
负载均衡 网络协议 算法
不为人知的网络编程(十九):能Ping通,TCP就一定能连接和通信吗?
这网络层就像搭积木一样,上层协议都是基于下层协议搭出来的。不管是ping(用了ICMP协议)还是tcp本质上都是基于网络层IP协议的数据包,而到了物理层,都是二进制01串,都走网卡发出去了。 如果网络环境没发生变化,目的地又一样,那按道理说他们走的网络路径应该是一样的,什么情况下会不同呢? 我们就从路由这个话题聊起吧。
56 4
不为人知的网络编程(十九):能Ping通,TCP就一定能连接和通信吗?
|
2月前
|
Arthas 监控 Java
拥抱 OpenTelemetry:阿里云 Java Agent 演进实践
本文介绍了阿里云 Java Agent 4.x 版本在基于 OTel Java Agent 二次开发过程中的实践与思考,并重点从功能、性能、稳定性、兼容性四个方面介绍了所做的工作。同时也介绍了阿里云可观测团队积极参与开源建设取得的丰厚成果。
278 6
拥抱 OpenTelemetry:阿里云 Java Agent 演进实践
|
21天前
|
网络协议
TCP报文格式全解析:网络小白变高手的必读指南
本文深入解析TCP报文格式,涵盖源端口、目的端口、序号、确认序号、首部长度、标志字段、窗口大小、检验和、紧急指针及选项字段。每个字段的作用和意义详尽说明,帮助理解TCP协议如何确保可靠的数据传输,是互联网通信的基石。通过学习这些内容,读者可以更好地掌握TCP的工作原理及其在网络中的应用。
|
1月前
|
存储 监控 小程序
Java中的线程池优化实践####
本文深入探讨了Java中线程池的工作原理,分析了常见的线程池类型及其适用场景,并通过实际案例展示了如何根据应用需求进行线程池的优化配置。文章首先介绍了线程池的基本概念和核心参数,随后详细阐述了几种常见的线程池实现(如FixedThreadPool、CachedThreadPool、ScheduledThreadPool等)的特点及使用场景。接着,通过一个电商系统订单处理的实际案例,分析了线程池参数设置不当导致的性能问题,并提出了相应的优化策略。最终,总结了线程池优化的最佳实践,旨在帮助开发者更好地利用Java线程池提升应用性能和稳定性。 ####
|
1月前
|
安全 Java 数据库连接
Java中的异常处理:理解与实践
在Java的世界里,异常处理是维护代码健壮性的守门人。本文将带你深入理解Java的异常机制,通过直观的例子展示如何优雅地处理错误和异常。我们将从基本的try-catch结构出发,探索更复杂的finally块、自定义异常类以及throw关键字的使用。文章旨在通过深入浅出的方式,帮助你构建一个更加稳定和可靠的应用程序。
37 5
|
2月前
|
缓存 Java 开发者
Java多线程并发编程:同步机制与实践应用
本文深入探讨Java多线程中的同步机制,分析了多线程并发带来的数据不一致等问题,详细介绍了`synchronized`关键字、`ReentrantLock`显式锁及`ReentrantReadWriteLock`读写锁的应用,结合代码示例展示了如何有效解决竞态条件,提升程序性能与稳定性。
202 6
|
1月前
|
安全 Java 程序员
Java内存模型的深入理解与实践
本文旨在深入探讨Java内存模型(JMM)的核心概念,包括原子性、可见性和有序性,并通过实例代码分析这些特性在实际编程中的应用。我们将从理论到实践,逐步揭示JMM在多线程编程中的重要性和复杂性,帮助读者构建更加健壮的并发程序。