15.Java 网络编程(网络相关概念、InetAddress、NetworkInterface、TCP 网络通信、UDP 网络通信、超时中断)

一、网络相关概念

1、网络通信
  • 网络通信指两台设备之间通过网络实现数据传输,将数据通过网络从一台设备传输到另一台设备

  • java.net 包下提供了一系列的类和接口用于完成网络通信

2、网络
  • 两台以上设备通过一定物理设备连接构成网络,根据网络的覆盖范围不同,对网络进行分类
网络 说明
局域网 覆盖范围最小,仅仅覆盖一个教室
城域网 覆盖范围较大,可以覆盖一个城市
广域网 覆盖范围最大,可以覆盖全国,设置全球,万维网是广域网的代表
3、IP 地址
  1. IP 地址用于唯一标识网络中的每台计算机

  2. 查看本机 IP 地址:CMD 中输入 ipconfig 命令

  3. IP 地址的表示形式为点分十进制(XX.XX.XX.XX),每一个十进制数的范围为 0 - 255

  4. IP 地址的组成:网络地址 + 主机地址

  5. IPv4 使用 4 个字节(32 位)表示地址,IPv6 使用 16 个字节(128 位)表示地址

  6. IPv4 最大的问题在于网络地址资源有限,IPv6 是互联网工程任务组设计的用于替代 IPv4 的下一代 IP 协议,其地址数量号称可以为全世界的每一粒沙子编上一个地址

  7. IPv4 地址分为五类,具体分类如下

  • 注:其中 127.0.0.1 为本机地址
4、域名
  • 域名是 IP 地址的映射,如 www.baidu.com,它是为了方便记忆
5、端口
  1. 端口用于标识计算机上某个特定的网络程序

  2. 表示形式:整数形式,范围 0 ~ 65535

  3. 已被占用的端口号:0 ~ 1024,如 ssh(22)、ftp(21)、smtp(25)、http(80)

  4. 常见网络程序端口号:Tomcat(8080)、MySQL(3306)

6、网络通信协议
  • TCP / IP 协议,Internet 最基本的协议,由网络层的 IP 协议和传输层的 TCP 协议组成

7、TCP 协议与 UDP 协议
(1)TCP 协议
  1. 使用 TCP 协议前,需建立 TCP 连接,形成传输数据通道

  2. 传输前,采用三次握手方式(在吗、在的、好的),所以是可靠的

  1. TCP 协议进行通信的两个应用程序:客户端,服务端

  2. 可进行大数据传输

  3. 传输完毕,需要释放资源,效率低

(2)UDP 协议
  1. 将数据、源、目的地封装成数据报,不需要建立连接

  2. 因为无需建立连接,所以是不可靠的

  3. 每份数据报的大小限制在 64kb 内

  4. 传输完毕,无需释放资源,速度快


二、InetAddress 类

1、常用方法
方法 说明
InetAddress.getByAddress(byte[] addr) 根据指定 IP 地址字节数组获取 InetAddress 对象
InetAddress.getByAddress(String host, byte[] addr) 根据指定主机名和 IP 地址字节数组获取 InetAddress 对象
InetAddress.getLocalHost() 获取本机的 InetAddress 对象
InetAddress.getByName(String host) 根据指定主机名 / 域名获取 InetAddress 对象
【InetAddress 对象】.getHostName() 获取 InetAddress 对象的主机名
【InetAddress 对象】.getHostAddress() 获取 InetAddress 对象的 IP 地址
2、基本使用
  • InetAddressTest.java
java 复制代码
package com.sunke.net;

import java.net.InetAddress;
import java.net.UnknownHostException;

public class InetAddressTest {
    public static void main(String[] args) throws UnknownHostException {
        byte[] ipAddressBytes = {(byte) 192, (byte) 168, 1, 1};
        InetAddress inetAddress1 = InetAddress.getByAddress(ipAddressBytes);
        System.out.println(inetAddress1);

        InetAddress inetAddress2 = InetAddress.getByAddress("Hello", ipAddressBytes);
        System.out.println(inetAddress2);

        InetAddress inetAddress3 = InetAddress.getLocalHost();
        System.out.println(inetAddress3);

        InetAddress inetAddress4 = InetAddress.getByName("www.baidu.com");
        System.out.println(inetAddress4);

        System.out.println(inetAddress2.getHostAddress());

        System.out.println(inetAddress2.getHostName());
    }
}

三、NetworkInterface

1、基本介绍
  • NetworkInterface 是 Java 网络编程中用于表示一个网络接口的类,它封装了关于网络接口的信息(例如,物理网卡、虚拟网卡),使得开发者可以方便地获取和操作这些信息,它有以下特点
  1. 网络接口表示:NetworkInterface 可以表示一个物理网络接口,例如,以太网卡(常见于防火墙和路由器),也可以表示一个虚拟网络接口,这些虚拟接口通常与机器的其它 IP 地址绑定到同个物理硬件。

  2. 访问网卡信息:NetworkInterface 类提供了多种方法来访问网卡设备的相关信息,例如,通过 getName 方法,可以获取到网络设备在操作系统中的名称,这些名称多数都以 eth 开头,后面跟着数字序号,例如,eth0、eth1,而 getIndex 方法返回的是网络接口的索引,它是一个大于或等于 0 的整数,当索引未知时,值就是 -1

  3. 判断接口状态:NetworkInterface 还提供了一些方法来判断网络接口的状态,例如,isUp 方法用于判断网络接口是否已经开启并正常工作,而 isLoopback 方法则用于判断该网络接口是否为 localhost 回调 / 回环接口

  4. 创建 NetworkInterface 对象:由于 NetworkInterface 对象表示物理硬件和虚拟地址,因此不能任意构造,Java 提供了一些静态方法,可以通过名称、IP 地址或枚举的方式返回与某个网络接口关联的 NetworkInterface 对象

2、常用方法
方法 说明
getNetworkInterfaces 返回一个 Enumeration 对象,它包含了当前主机上所有的网络接口 这些网络接口可以是物理的(例如,以太网卡)或虚拟的(例如,虚拟专用网络适配器)
getName 返回网络接口的名称,这个名称通常是操作系统分配的,用于唯一标识网络接口 例如,在类 Unix 系统上,物理网络接口的名称可能是 eth0、wlan0 等
isUp 返回一个布尔值,表示网络接口是否处于活动状态 如果网络接口已经启动并可以发送和接收数据,那么这个方法将返回 true
isLoopback 返回一个布尔值,用于判断网络接口是否是回环接口 回环接口是一个虚拟的网络接口,用于主机与自身的通信,大多数情况下它的地址是 127.0.0.1
getInetAddresses 返回一个 Enumeration 对象,包含了绑定到网络接口的所有 InetAddress 对象 这些地址可能包括 IPv4 和 IPv6 地址,通过该方法,可以获取网络接口的所有 IP 地址信息
getSubInterfaces 返回一个 Enumeration 对象,包含了当前网络接口的所有子接口 子接口是附加到主接口上的逻辑接口,它们可以有自己的配置和地址
3、基本使用
java 复制代码
try {
    Enumeration<NetworkInterface> networkInterfaces = NetworkInterface.getNetworkInterfaces();
    while (networkInterfaces.hasMoreElements()) {
        NetworkInterface ni = networkInterfaces.nextElement();

        // 打印网络接口名称
        System.out.println("Intface Nameer: " + ni.getName());

        // 打印网络接口是否启用
        System.out.println("Is Interface Up: " + ni.isUp());

        // 打印网络接口是否为回环接口
        System.out.println("Is Loopback Interface: " + ni.isLoopback());

        // 获取并打印网络接口的所有 IP 地址
        Enumeration<InetAddress> addresses = ni.getInetAddresses();
        while (addresses.hasMoreElements()) {
            InetAddress address = addresses.nextElement();
            System.out.println("Interface Address: " + address.getHostAddress());
        }

        // 打印网络接口的子接口(如果有)
        if (ni.getSubInterfaces().hasMoreElements()) {
            for (NetworkInterface subNi : Collections.list(ni.getSubInterfaces())) {
                System.out.println("Sub-interface Name: " + subNi.getName());
            }
        }

        System.out.println("------------------------------");
    }
} catch (SocketException e) {
    throw new RuntimeException(e);
}
  • 输出结果

    Intface Nameer: lo
    Is Interface Up: true
    Is Loopback Interface: true
    Interface Address: 127.0.0.1
    Interface Address: 0:0:0:0:0:0:0:1

    Intface Nameer: net0
    Is Interface Up: false
    Is Loopback Interface: false

    Intface Nameer: wlan0
    Is Interface Up: false
    Is Loopback Interface: false
    Interface Address: fe80:0:0:0:dbf4:3572:4b4e:e227%wlan0

    Intface Nameer: ppp0
    Is Interface Up: false
    Is Loopback Interface: false

    Intface Nameer: wlan1
    Is Interface Up: false
    Is Loopback Interface: false

    Intface Nameer: net1
    Is Interface Up: false
    Is Loopback Interface: false

    Intface Nameer: eth0
    Is Interface Up: false
    Is Loopback Interface: false

    Intface Nameer: net2
    Is Interface Up: false
    Is Loopback Interface: false

    Intface Nameer: eth1
    Is Interface Up: true
    Is Loopback Interface: false
    Interface Address: 192.168.40.1
    Interface Address: fe80:0:0:0:ce62:8aae:4aed:1b13%eth1

    Intface Nameer: eth2
    Is Interface Up: false
    Is Loopback Interface: false

    Intface Nameer: wlan2
    Is Interface Up: true
    Is Loopback Interface: false
    Interface Address: 192.168.0.5
    Interface Address: fe80:0:0:0:d22f:cfeb:5940:3a9f%wlan2

    Intface Nameer: wlan3
    Is Interface Up: false
    Is Loopback Interface: false
    Interface Address: fe80:0:0:0:1d73:1a4c:fc1f:85b8%wlan3

    Intface Nameer: wlan4
    Is Interface Up: false
    Is Loopback Interface: false

    Intface Nameer: eth3
    Is Interface Up: false
    Is Loopback Interface: false

    Intface Nameer: net3
    Is Interface Up: false
    Is Loopback Interface: false

    Intface Nameer: eth4
    Is Interface Up: false
    Is Loopback Interface: false

    Intface Nameer: net4
    Is Interface Up: false
    Is Loopback Interface: false

    Intface Nameer: net5
    Is Interface Up: false
    Is Loopback Interface: false

    Intface Nameer: net6
    Is Interface Up: false
    Is Loopback Interface: false

    Intface Nameer: eth5
    Is Interface Up: false
    Is Loopback Interface: false
    Interface Address: fe80:0:0:0:405d:de8:75b4:7fc%eth5

    Intface Nameer: eth6
    Is Interface Up: false
    Is Loopback Interface: false

    Intface Nameer: eth7
    Is Interface Up: false
    Is Loopback Interface: false
    Interface Address: fe80:0:0:0:76db:ea67:ac52:4fb1%eth7

    Intface Nameer: eth8
    Is Interface Up: true
    Is Loopback Interface: false
    Interface Address: 192.168.200.1
    Interface Address: fe80:0:0:0:6e5b:2ea2:2ec8:6c8a%eth8

    Intface Nameer: eth9
    Is Interface Up: false
    Is Loopback Interface: false

    Intface Nameer: eth10
    Is Interface Up: false
    Is Loopback Interface: false

    Intface Nameer: eth11
    Is Interface Up: false
    Is Loopback Interface: false

    Intface Nameer: eth12
    Is Interface Up: false
    Is Loopback Interface: false

    Intface Nameer: eth13
    Is Interface Up: false
    Is Loopback Interface: false

    Intface Nameer: eth14
    Is Interface Up: false
    Is Loopback Interface: false

    Intface Nameer: wlan5
    Is Interface Up: false
    Is Loopback Interface: false

    Intface Nameer: wlan6
    Is Interface Up: false
    Is Loopback Interface: false

    Intface Nameer: wlan7
    Is Interface Up: false
    Is Loopback Interface: false

    Intface Nameer: wlan8
    Is Interface Up: false
    Is Loopback Interface: false

    Intface Nameer: wlan9
    Is Interface Up: false
    Is Loopback Interface: false

    Intface Nameer: wlan10
    Is Interface Up: false
    Is Loopback Interface: false

    Intface Nameer: wlan11
    Is Interface Up: false
    Is Loopback Interface: false

    Intface Nameer: wlan12
    Is Interface Up: false
    Is Loopback Interface: false

    Intface Nameer: wlan13
    Is Interface Up: false
    Is Loopback Interface: false

    Intface Nameer: eth15
    Is Interface Up: false
    Is Loopback Interface: false

    Intface Nameer: eth16
    Is Interface Up: false
    Is Loopback Interface: false

    Intface Nameer: eth17
    Is Interface Up: false
    Is Loopback Interface: false

    Intface Nameer: eth18
    Is Interface Up: false
    Is Loopback Interface: false

    Intface Nameer: eth19
    Is Interface Up: false
    Is Loopback Interface: false

    Intface Nameer: eth20
    Is Interface Up: false
    Is Loopback Interface: false

    Intface Nameer: wlan14
    Is Interface Up: false
    Is Loopback Interface: false

    Intface Nameer: wlan15
    Is Interface Up: false
    Is Loopback Interface: false

    Intface Nameer: wlan16
    Is Interface Up: false
    Is Loopback Interface: false

    Intface Nameer: wlan17
    Is Interface Up: false
    Is Loopback Interface: false

4、补充
  • InetAddress.getLocalHost().getHostAddress() 通常用于获取运行 Java 程序的主机的本地 IP 地址,然而,它并不总是返回期望的特定网络接口的地址(例如,无线局域网 WLAN 接口),而是根据 Java 虚拟机和底层操作系统的网络配置和解析逻辑来确定,如果需要获取特定网络接口的地址(例如,WLAN),应该使用 NetworkInterface 类来枚举所有网络接口,并查找需要的接口
java 复制代码
try {
    Enumeration<NetworkInterface> networkInterfaces = NetworkInterface.getNetworkInterfaces();
    while (networkInterfaces.hasMoreElements()) {
        NetworkInterface networkInterface = networkInterfaces.nextElement();
        if (networkInterface.getName().startsWith("wlan") ||
                networkInterface.isUp() && !networkInterface.isLoopback() && !networkInterface.isVirtual()) {
            Enumeration<InetAddress> inetAddresses = networkInterface.getInetAddresses();
            while (inetAddresses.hasMoreElements()) {
                InetAddress inetAddress = inetAddresses.nextElement();
                if (!inetAddress.isLoopbackAddress()) {
                    System.out.println("WLAN Address: " + inetAddress.getHostAddress());
                }
            }
        }
    }
} catch (SocketException e) {
    throw new RuntimeException(e);
}
  • 输出结果

    WLAN Address: fe80:0:0:0:dbf4:3572:4b4e:e227%wlan0
    WLAN Address: 192.168.40.1
    WLAN Address: fe80:0:0:0:ce62:8aae:4aed:1b13%eth1
    WLAN Address: 192.168.0.5
    WLAN Address: fe80:0:0:0:d22f:cfeb:5940:3a9f%wlan2
    WLAN Address: fe80:0:0:0:1d73:1a4c:fc1f:85b8%wlan3
    WLAN Address: 192.168.200.1
    WLAN Address: fe80:0:0:0:6e5b:2ea2:2ec8:6c8a%eth8


四、TCP 网络通信

1、Socket
  1. Socket 又称套接字,用于开发网络应用程序

  2. 通信的两端都要有 Socket,这是两台机器间通信的端点

  3. Socket 允许把网络连接当成一个流,数据通过 IO 流传输

  4. 一般主动发起通信的是客户端,等待通信的是服务端

2、TCP 连接
  1. 开启监听
java 复制代码
ServerSocket serverSocket = new ServerSocket(【端口号】);
Socket socket = serverSocket.accept();
  1. 建立连接
java 复制代码
Socket socket = new Socket("【IP 地址】", 【端口号】);
  • 设置写入结束标记
java 复制代码
socket.shutdownOutput();
3、TCP 字节流编程
(1)服务端
  • TCPServerTest.java
java 复制代码
package com.sunke.tcp;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;

public class TCPServerTest {
    public static void main(String[] args) throws IOException {
        // 服务端

        // 在本机 9999 端口监听,等待连接
        ServerSocket serverSocket = new ServerSocket(9999);
        System.out.println("服务端在 9999 端口监听,等待连接...");
        Socket socket = serverSocket.accept();

        // 得到 Socket 对象的输入流对象并打印数据
        InputStream inputStream = socket.getInputStream();
        byte[] dataArr = new byte[10];
        int length;
        while ((length = inputStream.read(dataArr)) != -1) {
            System.out.print(new String(dataArr, 0, length));
        }
        System.out.println();
        System.out.println("接收传输信息完成");

        // 回送消息
        OutputStream outputStream = socket.getOutputStream();
        outputStream.write("hello, client".getBytes());
        System.out.println("回送信息完成");

        // 设置结束标记
        socket.shutdownOutput();

        // 释放资源
        inputStream.close();
        socket.close();
        serverSocket.close();
    }
}
  • 输出结果

    服务端在 9999 端口监听,等待连接...
    hello, server
    接收传输信息完成
    回送信息完成

(2)客户端
  • TCPClientTest.java
java 复制代码
package com.sunke.tcp;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.Socket;

public class TCPClientTest {
    public static void main(String[] args) throws IOException {
        // 客户端

        // 连接服务端:IP 地址 + 端口号
        Socket socket = new Socket(InetAddress.getLocalHost().getHostAddress(), 9999);

        // 得到 Socket 对象的输出流对象并写入数据
        OutputStream outputStream = socket.getOutputStream();
        outputStream.write("hello, server".getBytes());
        System.out.println("传输信息完成");

        // 设置结束标记
        socket.shutdownOutput();

        // 接受回送信息
        InputStream inputStream = socket.getInputStream();
        byte[] dataArr = new byte[10];
        int length;
        while ((length = inputStream.read(dataArr)) != -1) {
            System.out.print(new String(dataArr, 0, length));
        }
        System.out.println();
        System.out.println("接收回送信息完成");

        // 释放资源
        outputStream.close();
        socket.close();
    }
}
  • 输出结果

    传输信息完成
    hello, client
    接收回送信息完成

4、TCP 字符流编程
(1)服务端
  • NewTCPServerTest.java
java 复制代码
package com.sunke.tcp;

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;

public class NewTCPServerTest {
    public static void main(String[] args) throws IOException {
        ServerSocket serverSocket = new ServerSocket(8888);
        System.out.println("服务端在 8888 端口监听,等待连接...");
        Socket socket = serverSocket.accept();

        InputStream inputStream = socket.getInputStream();
        InputStreamReader inputStreamReader = new InputStreamReader(inputStream);
        BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
        System.out.println(bufferedReader.readLine());
        System.out.println("接收传输信息完成");

        OutputStream outputStream = socket.getOutputStream();
        OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream);
        BufferedWriter bufferedWriter = new BufferedWriter(outputStreamWriter);
        bufferedWriter.write("hello, client");
        bufferedWriter.flush();
        System.out.println("回送信息完成");
        socket.shutdownOutput();

        bufferedWriter.close();
        bufferedReader.close();
        socket.close();
        serverSocket.close();
    }
}
  • 输出结果

    服务端在 8888 端口监听,等待连接...
    hello, server
    接收传输信息完成
    回送信息完成

(2)客户端
  • NewTCPClientTest.java
java 复制代码
package com.sunke.tcp;

import java.io.*;
import java.net.InetAddress;
import java.net.Socket;

public class NewTCPClientTest {
    public static void main(String[] args) throws IOException {
        Socket socket = new Socket(InetAddress.getLocalHost().getHostAddress(), 8888);

        OutputStream outputStream = socket.getOutputStream();
        OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream);
        BufferedWriter bufferedWriter = new BufferedWriter(outputStreamWriter);
        bufferedWriter.write("hello, server");
        bufferedWriter.flush();
        System.out.println("传输信息完成");
        socket.shutdownOutput();

        InputStream inputStream = socket.getInputStream();
        InputStreamReader inputStreamReader = new InputStreamReader(inputStream);
        BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
        System.out.println(bufferedReader.readLine());
        System.out.println("接收回送信息完成");

        bufferedReader.close();
        bufferedWriter.close();
        socket.close();
    }
}
  • 输出结果

    传输信息完成
    hello, client
    接收回送信息完成

5、文件上传
(1)服务端
  • ImgCopyTCPServerTest.java
java 复制代码
package com.sunke.tcp;

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;

public class ImgCopyTCPServerTest {
    public static void main(String[] args) throws IOException {

        // 服务端在 7777 端口监听,等待连接
        ServerSocket serverSocket = new ServerSocket(7777);
        System.out.println("服务端在 7777 端口监听,等待连接...");
        Socket socket = serverSocket.accept();

        // 包装 Socket 对象的字节输入流
        InputStream inputStream = socket.getInputStream();
        BufferedInputStream bufferedInputStream = new BufferedInputStream(inputStream);

        // 包装图片的字节输出流
        FileOutputStream fileOutputStream = new FileOutputStream("d:\\newSpider.jpg");
        BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(fileOutputStream);

        // 保存图片
        byte[] imgArr = new byte[1024];
        int imgLength;
        while ((imgLength = bufferedInputStream.read(imgArr)) != -1) {
            bufferedOutputStream.write(imgArr, 0, imgLength);
        }
        System.out.println("接收图片完成");

        // 回送消息
        BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
        bufferedWriter.write("收到图片");

        // 必要刷新
        bufferedWriter.flush();

        // 设置结束标记
        socket.shutdownOutput();

        // 释放资源
        bufferedWriter.close();
        bufferedOutputStream.close();
        bufferedInputStream.close();
        socket.close();
        serverSocket.close();
    }
}
  • 输出结果

    服务端在 7777 端口监听,等待连接...
    接收图片完成

(2)客户端
  • ImgCopyTCPClientTest.java
java 复制代码
package com.sunke.tcp;

import java.io.*;
import java.net.InetAddress;
import java.net.Socket;

public class ImgCopyTCPClientTest {
    public static void main(String[] args) throws IOException {

        // 建立连接
        Socket socket = new Socket(InetAddress.getLocalHost().getHostAddress(), 7777);

        // 包装 Socket 对象的字节输出流
        OutputStream outputStream = socket.getOutputStream();
        BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(outputStream);

        // 包装图片的字节输入流
        FileInputStream fileInputStream = new FileInputStream("d:\\spider.jpg");
        BufferedInputStream bufferedInputStream = new BufferedInputStream(fileInputStream);

        // 传输图片
        byte[] imgArr = new byte[1024];
        int imgLength;
        while ((imgLength = bufferedInputStream.read(imgArr)) != -1) {
            bufferedOutputStream.write(imgArr, 0, imgLength);
        }
        System.out.println("传输图片完成");

        // 必要刷新
        bufferedOutputStream.flush();

        // 设置结束标记
        socket.shutdownOutput();

        // 接受回送消息
        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
        System.out.println(bufferedReader.readLine());

        // 释放资源
        bufferedReader.close();
        bufferedInputStream.close();
        bufferedOutputStream.close();
        socket.close();
    }
}
  • 输出结果

    传输图片完成
    收到图片

6、TCP 连续传输
(1)服务端
  • MyTCPServer.java
java 复制代码
package com.sunke.tcpmoretest;

import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;

public class MyTCPServer {
    public static void main(String[] args) throws IOException {
        ServerSocket serverSocket = new ServerSocket(9999);
        System.out.println("服务端在 9999 端口监听,等待连接...");

        Socket socket = serverSocket.accept();
        InputStream inputStream = socket.getInputStream();

        byte[] dataArr = new byte[100];
        int length;

        while ((length = inputStream.read(dataArr)) != -1) {
            String res = new String(dataArr, 0, length);
            System.out.println(res);
            if (res.equals("end")) {
                inputStream.close();
                socket.close();
                serverSocket.close();
                System.out.println("断开连接");
                return;
            }
        }
    }
}
  • 输出结果

    服务端在 9999 端口监听,等待连接...
    123
    456
    end
    断开连接

(2)客户端
  • MyTCPClient.java
java 复制代码
package com.sunke.tcpmoretest;

import java.io.IOException;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.Scanner;

public class MyTCPClient {
    public static void main(String[] args) throws IOException {
        Socket socket = new Socket(InetAddress.getLocalHost().getHostAddress(), 9999);
        System.out.println("连接已建立,请输入传输的内容");
        Scanner scanner = new Scanner(System.in);
        OutputStream outputStream = socket.getOutputStream();

        while (true) {
            String str = scanner.next();
            outputStream.write(str.getBytes());
            if (str.equals("end")) {
                outputStream.close();
                socket.close();
                System.out.println("断开连接");
                return;
            }
        }
    }
}
  • 输出结果

    连接已建立,请输入传输的内容
    123
    456
    end
    断开连接

7、TCP 一对多
(1)服务端
  • MyTCPFather.java
java 复制代码
package com.sunke.tcpmoretest;

import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;

public class MyTCPFather {
    public static void main(String[] args) throws IOException {
        ServerSocket serverSocket = new ServerSocket(9999);
        System.out.println("MyTCPFather 服务端在 9999 端口监听,等待连接...");

        Socket socket = serverSocket.accept();
        InputStream inputStream = socket.getInputStream();

        byte[] dataArr = new byte[100];
        int length;

        while ((length = inputStream.read(dataArr)) != -1) {
            System.out.println(new String(dataArr, 0, length));
        }
    }
}
  • 输出结果

    MyTCPFather 服务端在 9999 端口监听,等待连接...
    MyTCPSon1: 123

(2)客户端
  • MyTCPSon1.java
java 复制代码
package com.sunke.tcpmoretest;

import java.io.IOException;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.Scanner;

public class MyTCPSon1 {
    public static void main(String[] args) throws IOException {
        Socket socket = new Socket(InetAddress.getLocalHost().getHostAddress(), 9999);
        System.out.println("MyTCPSon1 连接已建立,请输入传输的内容");
        Scanner scanner = new Scanner(System.in);
        OutputStream outputStream = socket.getOutputStream();

        while (true) {
            String str = "MyTCPSon1: " + scanner.next();
            outputStream.write(str.getBytes());
        }
    }
}
  • 输出结果

    MyTCPSon1 连接已建立,请输入传输的内容
    123

  • MyTCPSon2.java

java 复制代码
package com.sunke.tcpmoretest;

import java.io.IOException;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.Scanner;

public class MyTCPSon2 {
    public static void main(String[] args) throws IOException {
        Socket socket = new Socket(InetAddress.getLocalHost().getHostAddress(), 9999);
        System.out.println("MyTCPSon2 连接已建立,请输入传输的内容");
        Scanner scanner = new Scanner(System.in);
        OutputStream outputStream = socket.getOutputStream();

        while (true) {
            String str = "MyTCPSon2: "+ scanner.next();
            outputStream.write(str.getBytes());
        }
    }
}
  • 输出结果

    MyTCPSon2 连接已建立,请输入传输的内容
    123

8、补充
(1)netstat 指令
  1. 查看当前主机网络情况(端口监听情况和网络连接情况)
bash 复制代码
netstat -an
  1. 分页查看当前主机网络情况
bash 复制代码
netstat -an | more
(2)TCP 网络通信补充
  • 当客户端连接到服务端后,客户端也会通过一个端口和服务端进行通信,这个端口是 TCP / IP 协议分配的

五、UDP 网络通信

1、DatagramSocket 与 DatagramPacket
(1)基本介绍
  1. UDP 数据报通过数据报套接字(DatagramSocket)发送和接收

  2. 系统不保证 UDP 数据报一定能送达,也不确定何时送达

  3. DatagramPacket 对象封装数据报,数据报中包含发送端的 IP 地址和端口号,以及接收端的 IP 地址和端口号

  4. UDP 协议中每个数据报都给出了完整的地址信息,因此无须建立发送方和接收方的连接

(2)DatagramSocket 常用方法
  1. DatagramSocket(int port)

  2. close()

  3. receive(DatagramPacket p)

  4. send(DatagramPacket p)

(3)DatagramPacket 常用方法
  1. DatagramPacket(byte[] buf, int length)

  2. DatagramPacket(byte[] buf, int length, InetAddress address, int port)

2、UDP 基本使用
(1)服务端
  • UDPServer.java
java 复制代码
package com.sunke.udp;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;

public class UDPServer {
    public static void main(String[] args) throws IOException {

        // 构建一个 DatagramSocket 对象,在 9999 端口接收数据
        DatagramSocket datagramSocket = new DatagramSocket(9999);

        // 准备接受数据报
        byte[] buf = new byte[1024];
        DatagramPacket datagramPacket = new DatagramPacket(buf, buf.length);

        // 调用接收方法,通过网络传输的 DatagramPacket 对象会填充到此
        System.out.println("Server 在 9999 端口等待接受数据报...");
        datagramSocket.receive(datagramPacket);

        // 拆包
        int length = datagramPacket.getLength();
        byte[] data = datagramPacket.getData();
        String s = new String(data, 0, length);
        System.out.println(s);

        // 释放资源
        datagramSocket.close();
    }
}
  • 输出结果

    Server 在 9999 端口等待接受数据报...
    Hello Server

(2)客户端
  • UDPClient.java
java 复制代码
package com.sunke.udp;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;

public class UDPClient {
    public static void main(String[] args) throws IOException {
        // 创建 DatagramSocket 对象,准备发送数据
        DatagramSocket datagramSocket = new DatagramSocket(8888);

        // 准备发送数据报
        byte[] bytes = "Hello Server".getBytes();
        DatagramPacket datagramPacket = new DatagramPacket(bytes, bytes.length, InetAddress.getLocalHost(), 9999);

        // 发送数据报
        datagramSocket.send(datagramPacket);
        System.out.println("数据报发送完毕");

        // 释放资源
        datagramSocket.close();
    }
}
  • 输出结果

    数据报发送完毕

3、UDP 连续传输
(1)服务端
  • MyUDPServer.java
java 复制代码
package com.sunke.udpmoretest;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;

public class MyUDPServer {
    public static void main(String[] args) throws IOException {
        DatagramSocket datagramSocket = new DatagramSocket(9999);
        System.out.println("Server 在 9999 端口等待接收数据报...");

        byte[] buf = new byte[1024];
        DatagramPacket datagramPacket = new DatagramPacket(buf, buf.length);

        while (true) {
            datagramSocket.receive(datagramPacket);
            int length = datagramPacket.getLength();
            byte[] data = datagramPacket.getData();
            String s = new String(data, 0, length);
            System.out.println(s);
            if (s.equals("end")) {
                datagramSocket.close();
                System.out.println("Server 关闭");
                return;
            }
        }
    }
}
  • 输出结果

    Server 在 9999 端口等待接收数据报...
    123
    123
    end
    Server 关闭

(2)客户端
  • MyUDPClient.java
java 复制代码
package com.sunke.udpmoretest;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.util.Scanner;

public class MyUDPClient {
    public static void main(String[] args) throws IOException {
        DatagramSocket datagramSocket = new DatagramSocket(8888);
        Scanner scanner = new Scanner(System.in);

        System.out.println("请输入传输内容");

        while (true) {
            String str = scanner.next();
            byte[] bytes = str.getBytes();
            DatagramPacket datagramPacket = new DatagramPacket(bytes, bytes.length, InetAddress.getLocalHost(), 9999);
            datagramSocket.send(datagramPacket);
            if (str.equals("end")) {
                datagramSocket.close();
                System.out.println("Client 关闭");
                return;
            }
        }
    }
}
  • 输出结果

    请输入传输内容
    123
    123
    end
    Client 关闭

4、UDP 一对多
(1)服务端
  • MyUDPFather.java
java 复制代码
package com.sunke.udpmoretest;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;

public class MyUDPFather {
    public static void main(String[] args) throws IOException {
        DatagramSocket datagramSocket = new DatagramSocket(9999);
        System.out.println("Server 在 9999 端口等待接收数据报...");

        byte[] buf = new byte[1024];
        DatagramPacket datagramPacket = new DatagramPacket(buf, buf.length);

        while (true) {
            datagramSocket.receive(datagramPacket);
            int length = datagramPacket.getLength();
            byte[] data = datagramPacket.getData();
            String s = new String(data, 0, length);
            System.out.println(s);
        }
    }
}
  • 输出结果

    Server 在 9999 端口等待接收数据报...
    MyUDPSon1: 123
    MyUDPSon2: 123

(2)客户端
  1. MyUDPSon1.java
java 复制代码
package com.sunke.udpmoretest;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.util.Scanner;

public class MyUDPSon1 {
    public static void main(String[] args) throws IOException {
        DatagramSocket datagramSocket = new DatagramSocket(8888);
        Scanner scanner = new Scanner(System.in);

        System.out.println("MyUDPSon1 请输入传输内容");

        while (true) {
            String str = "MyUDPSon1: " + scanner.next();
            byte[] bytes = str.getBytes();
            DatagramPacket datagramPacket = new DatagramPacket(bytes, bytes.length, InetAddress.getLocalHost(), 9999);
            datagramSocket.send(datagramPacket);
        }
    }
}
  • 输出结果

    MyUDPSon1 请输入传输内容
    123

  1. MyUDPSon2.java
java 复制代码
package com.sunke.udpmoretest;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.util.Scanner;

public class MyUDPSon2 {
    public static void main(String[] args) throws IOException {
        DatagramSocket datagramSocket = new DatagramSocket(8889);
        Scanner scanner = new Scanner(System.in);

        System.out.println("MyUDPSon2 请输入传输内容");

        while (true) {
            String str = "MyUDPSon2: " + scanner.next();
            byte[] bytes = str.getBytes();
            DatagramPacket datagramPacket = new DatagramPacket(bytes, bytes.length, InetAddress.getLocalHost(), 9999);
            datagramSocket.send(datagramPacket);
        }
    }
}
  • 输出结果

    MyUDPSon2 请输入传输内容
    123


六、超时中断

1、基本介绍
  1. ServerSocket.setSoTimeout(int timeout):为 ServerSocket 设置的,它指定了服务器套接字接受连接请求的超时时间(毫秒),如果设置为非零值,那么 accept 方法将阻塞,直到有连接到来或者超时为止,如果超时,accept 方法将抛出 java.net.SocketTimeoutException,如果设置为 0,那么 accept 方法将无限期地等待连接

  2. DatagramSocket.setSoTimeout(int timeout):为 DatagramSocket 设置的,它指定了数据报套接字发送或接收数据报的超时时间(毫秒),如果设置为非零值,那么 receive 方法将阻塞,直到有数据报到来或者超时为止,如果超时,receive 方法将抛出 java.net.SocketTimeoutException,如果设置为 0,那么 receive() 方法将无限期地等待数据报

2、演示
(1)TCP 超时中断
java 复制代码
try {
    ServerSocket serverSocket = new ServerSocket(7777);
    serverSocket.setSoTimeout(2 * 1000);
    Socket accept = serverSocket.accept();
} catch (SocketTimeoutException e) {
    System.out.println("TCP 等待连接超时了");
} catch (IOException e) {
    e.printStackTrace();
}
java 复制代码
try {
    ServerSocket serverSocket = new ServerSocket(7777);
    serverSocket.setSoTimeout(2 * 1000);
    Socket accept = serverSocket.accept();
} catch (IOException e) {
    if (e instanceof SocketTimeoutException) {
        System.out.println("TCP 等待连接超时了");
    } else {
        e.printStackTrace();
    }
}
  • 输出结果

    TCP 等待连接超时了

(2)UDP 超时中断
java 复制代码
try {
    DatagramSocket datagramSocket = new DatagramSocket(8888);
    byte[] receiveBytes = new byte[1024];
    DatagramPacket datagramPacket = new DatagramPacket(receiveBytes, receiveBytes.length);
    datagramSocket.setSoTimeout(2 * 1000);
    datagramSocket.receive(datagramPacket);
} catch (SocketTimeoutException e) {
    System.out.println("UDP 接收数据超时了");
} catch (Exception e) {
    e.printStackTrace();
}
  • 输出结果

    UDP 接收数据超时了

相关推荐
egekm_sefg1 分钟前
【Golang】——Gin 框架中的模板渲染详解
开发语言·golang·gin
android_lingz22 分钟前
鸿蒙分享(五):axios网络请求+简单封装
网络·华为·harmonyos
fendouweiqian24 分钟前
python请求SSL问题
网络·python·ssl
drebander31 分钟前
Netty 的 SSL/TLS 安全通信
网络·netty·ssl
在在进步31 分钟前
R学习——数据框
开发语言·r语言
大G哥36 分钟前
27. 聚类分析 - 使用R进行客户分群
开发语言·信息可视化·r语言
别致的影分身1 小时前
网络协议介绍
网络·网络协议
我不是程序猿儿1 小时前
【C#】反射 和 特性(Attribute)、[AttributeUsage(AttributeTargets.Property)]
开发语言·c#
五月高高1 小时前
Lambda表达式对List对象实现多个动态属性排序
java·排序