1. 网络编程概述

目的: 直接或间接地通过网络协议与其他计算机实现数据交换,进行通讯

两个主要问题:

  1. 如何准确定位网络上的一台或多台主机以及定位主机上的特定的应用
  2. 定位主机后如何可靠高效地进行通信

需要明确一点,网络编程 ≠ 网页编程

2. 网络通信两个要素

  1. 通信双方的地址:IP 地址 + 端口号
  2. 一定的规则:即网络通信协议

3. IP 地址

InetAddress 类,代表 IP 地址

InetAddress 类常用方法:

  1. 静态方法,返回 InetAddress 对象

    • static InetAddress getByName(String host) - 在给定主机名的情况下确定主机的 IP 地址

      代码示例:

      1
      2
      3
      InetAddress inetAddress = InetAddress.getByName("www.baidu.com");

      InetAddress inetAddress = InetAddress.getByName("127.0.0.1");
    • static InetAddress getByAddress(byte[] addr)

      :在给定原始 IP 地址的情况下,返回 InetAddress 对象

    • static InetAddress getByAddress(String host, byte[] addr)

      :根据提供的主机名和 IP 地址创建 InetAddress 对象

    • static InetAddress getLocalHost() - 返回本地主机

  2. 非静态方法,获取 InetAddress 对象信息

    • String getHostName() - 获取此 IP 地址的主机名

    • String getHostAddress() - 返回 IP 地址字符串

4. 端口 Port

端口号标识计算机上正在运行的程序(即进程)

  • TCP 协议与 UDP 协议结构中端口地址都是 16 比特,可以有在 0~65535 范围内的端口号
  • 单个协议下端口不能冲突

端口号与 IP 地址的组合,就是网络套接字:Socket

一些网络编程也被称为Socket编程

hosts 文件

hosts 文件是一个没有扩展名的系统文件,主要作用是定义 IP 地址和主机名的映射关系,是一个映射 IP 地址和主机名的规定。

Windows 下 hosts 文件位置是在 “C:\Windows\System32\drivers\etc”,注意这个文件一定是在系统盘

InetSocketAddress

InetSocketAddress 类主要作用是封装端口,它是在 InetAddress 类基础上加端口,但它是有构造器的。

InetSocketAddress 类常用方法:

  • 构造方法

    1. InetSocketAddress(int port) - 创建一个套接字地址,其中 IP 地址是通配符地址,端口号是指定值
    2. InetSocketAddress(String hostname, int port) - 根据主机名和端口号创建套接字地址
    3. InetSocketAddress(InetAddress addr, int port) - 根据 IP 地址和端口号创建套接字地址
  • 普通方法

    • public final int getPort() - 获取端口号
    • public final InetAddress getAddress() - 获取 InetAddress
    • public final String getHostName() - 获取 hostname

5. 通信协议

TCP/IP 协议族的传输层协议中有两个非常重要的协议:

  • 传输控制协议 TCP(Transmission Control Protocol)

    建立连接:“三次握手”

    断开连接:“四次挥手”

  • 用户数据报协议 UDP(User Datagram Protocol)

6. TCP 实现

代码示例:

  • 服务端:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    import java.io.ByteArrayOutputStream;
    import java.io.IOException;
    import java.io.InputStream;
    import java.net.ServerSocket;
    import java.net.Socket;

    public class TCPServer {

    public static void main(String[] args) {
    ByteArrayOutputStream baos = null;
    InputStream is = null;
    Socket accept = null;
    ServerSocket serverSocket = null;

    try {
    // 1、创建服务器套接字并等待客户端连接
    serverSocket = new ServerSocket(9999);
    accept = serverSocket.accept();
    // 2、获取输入流并初始化 ByteArrayOutputStream
    is = accept.getInputStream();
    baos = new ByteArrayOutputStream();
    /*
    3-1、循环从输入流读取数据到buffer数组
    3-2、从buffer数组读取数据写入到 ByteArrayOutputStream
    */
    byte[] buffer = new byte[1024];
    int len = 0;
    while ((len = is.read(buffer)) != -1) {
    baos.write(buffer, 0, len);
    }
    // 4、打印读取的数据
    System.out.println(baos);
    } catch (IOException ignored) {
    } finally {
    // 关闭资源
    try {
    if (baos != null){
    baos.close();
    }
    if (is != null){
    is.close();
    }
    if (accept != null){
    accept.close();
    }
    if (serverSocket != null){
    serverSocket.close();
    }
    }catch (Exception ignored){
    }
    }
    }

    }
  • 客户端:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    import java.io.IOException;
    import java.io.OutputStream;
    import java.net.InetAddress;
    import java.net.Socket;

    public class TCPClient {

    public static void main(String[] args) {
    Socket socket = null;
    OutputStream os = null;

    try {
    // 1、获取服务器地址
    InetAddress serverIP = InetAddress.getLocalHost();
    int port = 9999;
    // 2、创建 Socket
    socket = new Socket(serverIP, port);
    // 3、创建输出流,向外传输数据
    os = socket.getOutputStream();
    os.write("Hello, 你好".getBytes());
    // 4、关闭输出流
    socket.shutdownOutput();
    } catch (IOException ignored) {
    } finally {
    // 关闭资源
    try {
    if (os != null) {
    os.close();
    }
    if (socket != null) {
    socket.close();
    }
    } catch (Exception ignored) {
    }
    }
    }

    }

7. 初识 Tomcat

浏览器相当于是一个客户端,而 Tomcat 服务器相当于是服务端

8. UDP 实现

DatagramSocketDatagramPacket 两个类实现了基于UDP协议的网络程序:

  • UDP 数据报通过 DatagramSocket 发送和接收,系统不保证 UDP 数据报一定能够安全送到目的地,也不确定什么时候可以抵达

  • DatagramPacket 对象封装了 UDP 数据报

    数据报中包含了发送端的IP地址和端口号以及接收端的IP地址和端口号。

代码示例:

  • Sender:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    import java.io.IOException;
    import java.net.*;

    public class UDPSender {

    public static void main(String[] args) {
    // 1、创建 DatagramSocket 对象
    DatagramSocket socket = null;
    try {
    socket = new DatagramSocket();
    } catch (SocketException ignore) {}

    /*
    2、准备数据包:ip + 端口 + 数据
    */
    InetAddress addr = null;
    try {
    addr = InetAddress.getByName("localhost");
    } catch (UnknownHostException ignore) {}
    int port = 9000;
    String info = "Hello World";
    byte[] msg = info.getBytes();
    DatagramPacket packet = new DatagramPacket(msg, 0, msg.length, addr, port);

    // 3、通过 DatagramSocket 发送数据包
    try {
    socket.send(packet);
    } catch (NullPointerException | IOException ignore) {}

    try {
    socket.close();
    } catch (NullPointerException ignore) {}
    }

    }
  • Receiver:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    import java.io.IOException;
    import java.net.DatagramPacket;
    import java.net.DatagramSocket;

    public class UDPReceiver {

    public static void main(String[] args) throws IOException {
    // 1、创建 DatagramSocket 对象准备接收数据包
    DatagramSocket socket = new DatagramSocket(9000);

    // 2、准备缓冲区
    byte[] buffer = new byte[1024];
    DatagramPacket packet = new DatagramPacket(buffer, 0, buffer.length);

    // 3、接收数据包
    socket.receive(packet);
    System.out.println(new String(packet.getData(), 0, packet.getLength()));

    socket.close();
    }
    }

9. URL 编程

URL(Uniform Resource Locator):统一资源定位符,表示 Internet 上某一资源的地址

  • URL 是一种具体的 URI,即 URL 可以用来标识一个资源,而且还指明了如何定位这个资源
  • 通过 URL,我们可以访问 Internet 上的各种网络资源
  • 浏览器通过解析给定的 URL 可以在网络上查找相应的文件或其他资源

URL 的基本结构:

scheme://host.domain:port/path/filename?parameter#anchor

参考资料

java.net 包中定义了 URL 类,该类用来处理有关 URL 的内容

示例:URL url = new URL("http://localhost:8080/study/index.html/?param=chatgpt");

通过 URL 下载资源,代码示例如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;

public class DownloadByURL {

public static void main(String[] args) {
try {
// 1、创建 URL 对象
URL url = new URL("https://afelixliu.site/img/index.png");
// 2、打开连接
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
// 3、获取输入流
InputStream is = connection.getInputStream();
// 4、创建文件输出流
FileOutputStream fos = new FileOutputStream("blog_index.png");
/*
5、读取和写入数据
*/
byte[] buffer = new byte[1024];
int len = 0;
while ((len = is.read(buffer)) != -1) {
fos.write(buffer, 0, len);
}
/*
6、关闭流和连接
*/
fos.close();
is.close();
connection.disconnect();
} catch (IOException e) {
throw new RuntimeException(e);
}
}

}