Java 网络编程:TCP 与 UDP 的「通信江湖」(基于TCP回显服务器)

Java 网络编程:TCP 与 UDP 的「通信江湖」(基于TCP回显服务器)

上篇博客写了关于UDP的基础特性和基于UDP回显服务器,书接上回,今天,我们就来好好聊聊 TCP,从基础特性到工作机制最后构建回显服务器,揭开它 "靠谱" 的秘密。

UDP与TCP的最主要的核心区别就是UDP为无连接,不可靠传输,面向数据报,UDP为有连接,可靠传输,面向字节流。二者都为全双工,此篇不再重点介绍。

一、TCP 协议:面向连接的可靠传输

UDP的核心特征为:有连接,可靠传输,面向字节流和全双工

1.核心技术特征
  • 有连接: 对于 TCP 来说,TCP 协议中,就已经保存了对端的信息
  • 可靠传输: 确保数据无丢失、无重复、按序交付,尽可能的提高传输成功的概率
  • 面向字节流:数据以连续字节为单位传输
  • 全双工: 一个通信链路,支持双向通信,能读也能写
2. Java 中 TCP 的实现

API 介绍
ServerSocket

ServerSocket 是创建TCP服务端Socket的API。

构造⽅法:

⽅法签名 ⽅法说明
ServerSocket(int port) 创建⼀个服务端流套接字Socket,并绑定到指定端⼝

⽅法:

⽅法签名 ⽅法说明
Socket accept() 开始监听指定端⼝(创建时绑定的端⼝),有客⼾端连接后,返回⼀个服务端Socket对象,并基于该Socket建⽴与客⼾端的连接,否则阻塞等待
void close() 关闭此套接字

TCP是"有连接",这里的accept是联通连接的关键

Socket

Socket 是客⼾端Socket,或服务端中接收到客⼾端建⽴连接(accept⽅法)的请求后,返回的服

务端Socket。

不管是客⼾端还是服务端Socket,都是双⽅建⽴连接以后,保存的对端信息,及⽤来与对⽅收发数据

的。

构造⽅法:

⽅法签名 ⽅法说明
Socket(String host, int port) 创建⼀个客⼾端流套接字Socket,并与对应IP的主机上,对应端⼝的进程建⽴连接

这两个参数是服务器IP和端口号

⽅法:

⽅法签名 ⽅法说明
InetAddress getInetAddress() 返回套接字所连接的地址
InputStream getInputStream() 返回此套接字的输⼊流(字节流对象
OutputStream getOutputStream() 返回此套接字的输出流(字节流对象

二、构造TCP Echo Server

服务器主要思路:
  1. 读取请求并解析
  2. 根据请求计算响应
  3. 返回响应到客户端
  4. 打印日志

代码⽰例:

java 复制代码
public class TcpEchoServer {
    private ServerSocket serverSocket=null;


    //这里和udp服务器类似,也是在构造对象的时候,绑定端口号
    public TcpEchoServer(int port) throws IOException {
        serverSocket=new ServerSocket(port);
    }
    public void start() throws IOException {
        System.out.println("启动服务器");
        try {
            while (true) {
                //tcp 来说,需要先处理客户端发来的连接
                //通过读写clientsocket,和客户端进行连接
                //如果没有客户端发起连接,此时accept就会阻塞
                Socket clientSocket = serverSocket.accept();
                processConnection(clientSocket);
            }
            }finally{
                serverSocket.close();
            }          
        }
    }
    //处理一个客户端的连接
    //可能要涉及到多个客户端的请求和响应

    private void processConnection(Socket clientSocket) {
        //打印日志
        System.out.printf("[%s:%d]客户端上线!\n",clientSocket.getInetAddress(),clientSocket.getPort());
        try(InputStream inputStream=clientSocket.getInputStream();
            OutputStream outputStream=clientSocket.getOutputStream()){
            //针对 InputStream 套了一层
            Scanner scanner=new Scanner(inputStream);
            //针对 OutputStram 套了一层
            PrintWriter writer=new PrintWriter(outputStream);
            //分成三个步骤
            while (true){
                //1.读取请求并解析,可以直接read,也可以借助Scanner 来辅助
                if (!scanner.hasNext()){
                    System.out.printf("[%s:%d] 客户端下线!\n",clientSocket.getInetAddress(),clientSocket.getPort());
                    //连接断开了
                    break;
                }
                String request=scanner.next();
                //2.根据请求计算响应
                String response=process(request);
                //3.返回响应到客户端
                //outputStream.write(response.getBytes());
                writer.println(response);
                //打印日志
                System.out.printf("[%s:%d] rep: %s, resp: %s\n",clientSocket.getInetAddress(),clientSocket.getPort(),request,response);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

    private 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. 打印到控制台

代码⽰例:

java 复制代码
public class TcpEchoClient {
    private Socket socket=null;
    public  TcpEchoClient(String sergerIp,int serverPort) throws IOException {
        //直接把字符串的IP,设置进来
        //127.0.0.0这种字符串
        socket=new Socket(sergerIp,serverPort);
    }
    public  void start(){
        Scanner scanner=new Scanner(System.in);
        try(InputStream inputStream=socket.getInputStream();
            OutputStream outputStream=socket.getOutputStream()){
            //为了使用方便,套壳使用
            Scanner scannerNet=new Scanner(inputStream);
            PrintWriter writer=new PrintWriter(outputStream);
            //从控制读取请求,发送给服务器
            while (true){
                //1.从控制台读取用户输入
                String requset=scanner.next();
                //2.发送给服务器
                writer.println(requset);
                //3.读取服务器返回的响应
                String response=scannerNet.next();
                //打印到控制台
                System.out.println(response);
       }
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

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

重点细节解析:

三、小结:Java 网络编程的 "入门钥匙"​

TCPUDP 是 Java 网络编程的基础,记住这两点:​

  • 要 "靠谱" 选 TCP:需要确认、有序的场景(比如用户登录时传密码,不能丢);
  • 要 "极速" 选 UDP:能接受偶尔丢包、追求速度的场景(比如游戏里的角色移动,丢一两个帧不影响)。

其实你再深入想一层:平时用的 HTTP 协议(上网刷网页)是基于 TCP 的,而直播用的 RTMP 协议很多是基于 UDP 的 ------ 原来这两个 "通信高手" 早就藏在我们每天的操作里啦!​

如果想进一步玩起来,可以试试修改上面的代码:比如让 TCP 客户端和服务端互相聊天(加个循环读取输入),或者让 UDP 接收方给发送方回消息(类似 "已读" 确认),动手的过程中理解会更透彻~

相关推荐
大卫小东(Sheldon)6 小时前
使用JMH对远程接口进行压测
java
杜子不疼.6 小时前
【Rust】异步处理器(Handler)实现:从 Future 本质到 axum 实战
android·开发语言·rust
学习编程之路6 小时前
Rust内存对齐与缓存友好设计深度解析
开发语言·缓存·rust
JMzz6 小时前
Rust 中的内存对齐与缓存友好设计:性能优化的隐秘战场 ⚡
java·后端·spring·缓存·性能优化·rust
Java水解6 小时前
2026java面试题(含答案,持续更新中)
java·后端·面试
何中应6 小时前
Oracle数据库安装(Windows)
java·数据库·后端·oracle
无限进步_6 小时前
C语言字符串连接实现详解:掌握自定义strcat函数
c语言·开发语言·c++·后端·算法·visual studio
Han.miracle6 小时前
Java的多线程——多线程(二)
java·开发语言·线程·多线程