当前位置: 首页 > news >正文

TCP、UDP协议的应用、ServerSocket和Socket、DatagramSocket和DatagramPacket

DAY13.1 Java核心基础

TCP协议

TCP 协议是面向连接的运算层协议,比较复杂,应用程序在使用TCP协议之前必须建立连接,才能传输数据,数据传输完毕之后需要释放连接

就好比现实生活中的打电话,首先确保电话打通了才能进行通信,如果没有打通则阻塞,需要等待打通才能对话

TCP优缺点:

  • 优点:安全可靠,数据不会丢失,并且数据是按照先后顺序依次到达
  • 缺点:速度慢,效率低,常用于对于业务安全要求较高的场景

Java中如何使用 TCP 呢?

java中通过Socket类来建立TCP连接,使用这个类可以在服务端和客户端建立一个可靠的连接

Socket表示客户端,ServerSocket表示服务端

它们都在java.net包中

在服务端创建ServerSocket对象,通过对象的accept()方法可以接收到若干个表示客户端的Socket对象

ServerSocket

方法描述
public ServerSocket(int port)根据端口创建 ServerSocket 实例对象
public ServerSocket(int port,int backlog)根据端口和 backlog 创建 ServerSocket 对象
public ServerSocket(int port,int backlog,InetAddress address)根据端口、backlog、IP 创建 ServerSocket对象
public ServerSocket()创建没有绑定服务器的 ServerSocket 对象
public synchronized int getSoTimeout()获取 Sotimeout 的设置
public InetAddress getInetAddress()获取服务器的 IP 地址
public Socket accept()等待客户端请求,并返回 Socket 对象
public void close()关闭 ServerSocket
public boolean isClosed()返回 ServerSocket 的关闭状态
public void bind(SocketAddress address)将 ServerSocket 实例对象绑定到指定地址
public int getLocalPort()返回 ServerSocket 的端口

Socket

方法描述
public Socket(String host,int port)根据主机、端口创建 Socket 对象
public Socket(InetAddress host,int port)根据 IP、端口创建 Socket 对象
public Socket(String host,int port,InetAddress address,int localPort)根据主机、端口创建要连接的 Socket 对象并将其连接到指定的远程主机上的指定端口
public Socket(InetAddress host,int port,InetAddress address,int localPort)根据主机、端口创建要连接的 Socket 对象并将其连接到指定的远程主机上的指定端口
pubilc Socket()创建没有连接的 Socket 对象
public InputStream getInputStream()返回 Socket 的输入流
public synchronized void close()关闭 Socket
public boolean isClosed()返回 Socket 的关闭状态、

DataInputStream 的作用

读取基本数据类型:提供方法直接读取二进制数据为Java基本类型,例如:

  • int readInt():读取4字节为int。
  • double readDouble():读取8字节为double。
  • String readUTF():读取修改后的UTF-8编码字符串。

跨平台一致性:数据以**网络字节序(Big-Endian)**存储,确保不同平台间数据读写兼容。

DataOutputStream 的作用

写入基本数据类型:将Java基本类型转换为字节序列写入流中,例如:

  • void writeInt(int v):将int写入为4字节。
  • void writeDouble(double v):将double写入为8字节。
  • void writeUTF(String str):以修改后的UTF-8格式写入字符串。

数据序列化:常用于将数据结构(如对象的字段)转换为字节流,便于存储或网络传输。

服务端启动的时候可以接收多个客户端的信息

服务端:ServerSocket

public class Server {
    public static void main(String[] args) {
        ServerSocket serverSocket = null;
        Socket socket = null;
        OutputStream outputStream = null;
        InputStream inputStream = null;
        DataInputStream dataInputStream = null;
        DataOutputStream dataOutputStream = null;
        try {
            serverSocket = new ServerSocket(8080);
            System.out.println("------服务端------");
            System.out.println("已启动,等待接收客户端请求...");
            while (true){
                socket = serverSocket.accept();
                inputStream = socket.getInputStream();
                dataInputStream = new DataInputStream(inputStream);
                String request = dataInputStream.readUTF();
                System.out.println("接收到了客户端请求:" + request);
                outputStream = socket.getOutputStream();
                dataOutputStream = new DataOutputStream(outputStream);
                String response = "Hello World";
                dataOutputStream.writeUTF(response);
                System.out.println("给客户端做出响应:" + response);
            }
        } catch (Exception e){

        } finally {
            try {
                dataOutputStream.close();
                outputStream.close();
                dataInputStream.close();
                inputStream.close();
                socket.close();
                serverSocket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

核心就是通过while循环accept()接收客户端的连接,然后通过DataInputStream和DataOutputStream实现了接收和发送的业务

启动等待客户端连接…

image-20250317173719789

客户端:Socket

public class Client {
    public static void main(String[] args) {
        Socket socket = null;
        OutputStream outputStream = null;
        DataOutputStream dataOutputStream = null;
        InputStream inputStream = null;
        DataInputStream dataInputStream = null;
        try {
            socket = new Socket("127.0.0.1", 8080);
            System.out.println("------客户端------");
            String request = "你好!";
            System.out.println("客户端说:" + request);
            outputStream = socket.getOutputStream();
            dataOutputStream = new DataOutputStream(outputStream);
            dataOutputStream.writeUTF(request);
            inputStream = socket.getInputStream();
            dataInputStream = new DataInputStream(inputStream);
            String response = dataInputStream.readUTF();
            System.out.println("服务器的响应是:" + response);
        } catch (Exception e){

        } finally {
            try {
                inputStream.close();
                dataInputStream.close();
                dataOutputStream.close();
                outputStream.close();
                socket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

客户端接收到的消息:

image-20250317174101983

此时服务端的输出为:

image-20250317174050370

UDP协议

TCP 协议连接可以建立稳定可靠的连接,保证信息的完整性,但是它的缺点也很明显,先建立连接再进行操作的方式效率必然低下

实际开发应用中,有些场景不需要可靠的连接,而是需要效率很高的传输,但连接不可靠,容易数据丢失

TCP:打电话,需要建立连接

UDP(发送消息,不需要建立连接):

  • 效率高,速度快,不需要建立连接,直接发送即可
  • 连接不可靠,容易数据丢失,安全性不高

DatagramSocket和DatagramPacket

DatagramSocket (邮箱,等待接收消息):

方法描述
public DatagramSocket(int port)根据端口创建 DatagramSocket 实例对象
public void send(DatagramPacket p)发送数据报
public synchronized void receive(DatagramPacket p)接收数据报
public InetAddress getInetAddress()获取 DatagramSocket 对应的 InetAddress 对象
public boolean isConnected()判断是否连接到服务

DatagramPacket(信封:封装发送的消息以及发送的地址):

方法描述
public DatagramPacket(byte[] buff,int length,InetAddress address,int port)根据发送的数据、数据长度、IP、端口创建 DatagramPacket 对象
public synchronized byte[] getData()获取接收的数据
public synchronized int getLength()获取数据长度
public synchronized int getPort()获取发送数据的 Socket 端口
public synchronized SocketAddress getSocketAddress()获取发送数据的 Socket 信息

客户端A:

public class TerminalA {
    public static void main(String[] args) {
        // 创建信箱
        try {
            byte[] buff= new byte[1024];
            SocketAddress socketAddress =new InetSocketAddress("127.0.0.1",8081);
            DatagramPacket datagramPacket= new DatagramPacket(buff,buff.length,socketAddress);
            DatagramSocket datagramSocket =new DatagramSocket(8080);
            // 等待接收8081信箱的消息
            System.out.println("客户端A等待接收消息....");
            datagramSocket.receive(datagramPacket);
            // 接收消息
            String msg = new String(datagramPacket.getData(),0,datagramPacket.getLength());
            System.out.println("接收到来自"+datagramPacket.getAddress()+":"+datagramPacket.getPort()+"端口的消息:"+msg);
            // 发送消息
            String s = "你好我是客户端A";
            byte[] bytes = s.getBytes();
            DatagramPacket datagramPacket1 =new DatagramPacket(bytes,bytes.length,new InetSocketAddress("127.0.0.1",8081));
            datagramSocket.send(datagramPacket1);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }

    }
}

image-20250318114800461

如果没有消息则一直阻塞等待

阻塞发生在 datagramSocket.receive(datagramPacket);

客户端 B:

public class TerminalB {
    public static void main(String[] args) {
        try {
            // 创建信箱
            DatagramSocket datagramSocket =new DatagramSocket(8081);
            // 发送消息给8080
            String s = "你好我是客户端B";
            byte[] bytes = s.getBytes();
            DatagramPacket datagramPacket1 =new DatagramPacket(bytes,bytes.length,new InetSocketAddress("127.0.0.1",8080));
            datagramSocket.send(datagramPacket1);

            // 接收消息
            SocketAddress socketAddress =new InetSocketAddress("127.0.0.1",8080);
            byte[] buff= new byte[1024];
            DatagramPacket datagramPacket= new DatagramPacket(buff,buff.length,socketAddress);
            // 等待接收8080信箱的消息
            System.out.println("客户端B等待接收消息....");
            datagramSocket.receive(datagramPacket);
            String msg = new String(datagramPacket.getData(),0,datagramPacket.getLength());
            System.out.println("接收到来自"+datagramPacket.getAddress()+":"+datagramPacket.getPort()+"端口的消息:"+msg);

        } catch (IOException e) {
            throw new RuntimeException(e);
        }

    }
}

发送消息给A,A给B回信

image-20250318114859741

image-20250318114847927

相关文章:

  • 查询修改ORACLE的server、客户端和导出dmp文件 字符集编码
  • SpringBoot-2整合MyBatis以及基本的使用方法
  • 大模型学习-让其他电脑可访问本地ollama的模型并进行流式响应
  • AMD锐龙8845HS+780M核显 虚拟机安装macOS 15 Sequoia 15.0.1 (2024.10)
  • 【ELK】节省存储 之 压缩存储方式调整
  • element-ui image 组件源码分享
  • cls(**dict(data, id=id))灵活地从一个字典生成实例,同时确保某些关键字段(如 id)被正确设置或覆盖
  • 问deepseek: 如何用CUDA实现PBiCGSTAB稀疏矩阵迭代算法,写段示例代码
  • OLE注册是什么?
  • 博客图床 VsCode + PicGo + 阿里云OSS
  • $.ajax的contentType设置及对应后端数据获取方式
  • ViT、DETR 和 Swin Transformer :基于 Transformer 的计算机视觉(CV)模型
  • vmware tools灰化
  • Unity打包的WebGL包打不开问题解决方案,以及WebGL包嵌入至Vue2中的步骤
  • QT程序双击可执行文件运行方法
  • vue3 引入element-plus组件后,发现输入的时候没有提示,而且鼠标移到el-button显示unknown的简单解决方法
  • 【谷粒商城踩坑记】第四坑 nacos 闪退问题
  • python 库笔记:pytorch-tcn
  • vue的绑定
  • 单片机开发资源分析的实战——以STM32G431RBT6为例子的单片机资源分析
  • 外交部亚洲司司长刘劲松向菲方严肃交涉
  • 五月院线片单:就看五一档表现了
  • 习近平对辽宁辽阳市白塔区一饭店火灾事故作出重要指示
  • 全国电影工作会:聚焦扩大电影国际交流合作,提升全球影响力
  • 《奇袭白虎团》原型人物之一赵顺合辞世,享年95岁
  • 杭州打造商业航天全产业链,请看《浪尖周报》第22期