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 接收方给发送方回消息(类似 "已读" 确认),动手的过程中理解会更透彻~

相关推荐
Scott9999HH7 小时前
【IIoT流量实战】蒸汽管道阀门全关却仍有流量?用 Python 实现涡街信号 FFT 频谱分析与温压全补偿积算网关,深度拆解靠谱的涡街流量计厂家硬核技术标准
开发语言·python
腻害兔7 小时前
【若依项目-产品经理视角】深度拆解 RuoYi-Vue-Pro 商城模块:从商品管理到交易引擎,50 张表撑起一整套电商系统
java·大数据·vue.js·产品经理·ai编程
码智社8 小时前
AES加密原理详解及Java实现加解密实战
java·开发语言
AI云海8 小时前
python 列表、元组、集合和字典
开发语言·python
克里斯蒂亚诺更新8 小时前
ruoyi角色权限的对应关系
笔记
萧瑟余晖9 小时前
JDK 26 新特性详解
java·开发语言
马优晨9 小时前
Freemarker 完整讲解(后端 Java 模板引擎)
java·开发语言·freemarker·freemarker 完整讲解·freemarker模板引擎
a11177611 小时前
2FA 验证码生成器(github登录验证 app)
笔记·学习
吳所畏惧11 小时前
宝塔面板Redis密码修改指南:SSH命令修改 vs 面板UI界面修改,哪个更靠谱?
运维·服务器·数据库·redis·缓存·ssh
DFT计算杂谈11 小时前
无 Root 权限在 Tesla K80 零门槛部署 DeepSeek 大模型
linux·服务器·网络·数据库·机器学习