Java 网络编程

网络编程

网络编程是指通过因特网(Internet)实现计算机之间的数据传输。

常见软件架构:

  1. C/S: 客户端/服务端
  2. B/S: 浏览器/服务器

网络编程三要素

  1. IP 地址:唯一标识计算机的数字地址。
  2. 端口号:端口号,标识计算机上的进程。
  3. 通信协议:描述计算机之间如何通信,如:TCP、UDP、http、ftp、smtp、pop3、imap 等。

IP

IP 地址(Internet Protocol Address)是 Internet 协议栈中的重要组件,用于唯一标识 Internet 中的计算机。IP 分为 IPV4 和 IPV6 两种。

  1. IPV4 地址格式为:A.B.C.D,其中 A、B、C、D 为 0-255 的数字。
  2. IPV6 地址格式为:ABCD:ABCD:ABCD:ABCD:ABCD:ABCD:ABCD:ABCD,其中 A、B、C、D 为 0-65535 的数字。

本机 IP 地址可以通过 ipconfig 命令查看,但是这个 IP 地址是本机的局域网 IP 地址,不是外网 IP 地址。当我们切换局域网时,会被重新分配新的 IP 地址。我们也可以通过 127.0.0.1 或者 localhost 来访问本机,这两个 IP 地址称为本地回环地址,永远不会被改变。

InetAddress 类

InetAddress 类是 Internet 协议栈中的重要类,用于表示 Internet 中的计算机。InetAddress 类提供了获取 IP 地址的方法。

InetAddress 构造方法

构造方法描述
getByAddress(String host, byte[] addr)通过主机名和字节数组创建 InetAddress 对象。
getByName(String host)通过主机名创建 InetAddress 对象。
getAllByName(String host)通过主机名创建 InetAddress 对象。
getLoopbackAddress()获取本地回环地址。
getByAddress(byte[] addr)通过字节数组创建 InetAddress 对象。
getLocalHost()获取本地主机地址。

InetAddress 常用方法

方法描述
getHostName()获取主机名。
getHostAddress()获取 IP 地址。

InetAddress 代码示例

java
复制代码
InetAddress address = InetAddress.getLocalHost(); System.out.println(address); // 获取主机名 String hostName = address.getHostName(); System.out.println(hostName); // 获取 IP String hostAddress = address.getHostAddress(); System.out.println(hostAddress);

端口

端口(Port)是计算机网络中的一个数字,用于标识计算机上的进程。端口号范围:0-65535。

协议

协议(Protocol)是计算机网络中的重要概念,用于描述计算机之间如何通信。协议定义了计算机如何进行数据传输,以及如何处理数据。

模型

  1. OSI 模型:应用层 --> 表示层 --> 会话层 --> 传输层 --> 网络层 --> 数据链路层 --> 物理层
  2. TCP/IP 模型:应用层 --> 传输层 --> 网络层 --> 物理链路层

通信协议

  1. UDP: 无连接协议,速度快,单次最大传输 64KB,数据不安全,易丢失。
  2. TCP: 有连接协议,传输速度慢,无大小限制,数据安全。

UDP

UDP 是一种无连接的协议,它不建立连接,只发送数据,不保证数据安全。

UDP 有三种通信方法:单播、组播、广播。

  1. 单播:发送方发送数据给指定的接收方。
  2. 组播:发送方发送数据给多个接收方。
  3. 广播:发送方发送数据给所有接收方。

单播

单播需要指定具体的 IP 地址和端口号。

java
复制代码
// 发送端 DatagramSocket socket = new DatagramSocket(); Scanner sc = new Scanner(System.in); while (true) { String content = sc.nextLine(); byte[] bytes = content.getBytes(); DatagramPacket packet = new DatagramPacket(bytes, 0, bytes.length, InetAddress.getLocalHost(), 8888); socket.send(packet); if ("exit".equals(content)) break; } sc.close(); socket.close();
java
复制代码
// 接收端 DatagramSocket socket = new DatagramSocket(8888); while (true) { byte[] buffer = new byte[1024]; DatagramPacket packet = new DatagramPacket(buffer, buffer.length); socket.receive(packet); byte[] data = packet.getData(); int length = packet.getLength(); InetAddress address = packet.getAddress(); String content = new String(data, 0, length); System.out.println(address.getHostName() + ":" + content); if ("exit".equals(content)) break; } socket.close();

组播

组播需要指定组播地址和组播端口,组播地址范围:224.0.0.0-239.255.255.255

下面的代码示例只有 接收端 1 和 接收端 2 才能接收数据。

java
复制代码
// 发送端 MulticastSocket socket = new MulticastSocket(); Scanner sc = new Scanner(System.in); while (true) { String content = sc.nextLine(); byte[] bytes = content.getBytes(); DatagramPacket packet = new DatagramPacket(bytes, 0, bytes.length, InetAddress.getByName("224.0.0.2"), 10000); socket.send(packet); if ("exit".equals(content)) break; } sc.close(); socket.close();
java
复制代码
// 接收端 1 MulticastSocket socket = new MulticastSocket(10000); // 将当前主机添加到组播地址中 InetAddress group = InetAddress.getByName("224.0.0.2"); socket.joinGroup(group); while (true) { byte[] buffer = new byte[1024]; DatagramPacket packet = new DatagramPacket(buffer, buffer.length); socket.receive(packet); byte[] data = packet.getData(); int length = packet.getLength(); InetAddress address = packet.getAddress(); String content = new String(data, 0, length); System.out.println(address.getHostName() + ":" + content); if ("exit".equals(content)) break; } socket.close();
java
复制代码
// 接收端 2 MulticastSocket socket = new MulticastSocket(10000); // 将当前主机添加到组播地址中 InetAddress group = InetAddress.getByName("224.0.0.2"); socket.joinGroup(group); while (true) { byte[] buffer = new byte[1024]; DatagramPacket packet = new DatagramPacket(buffer, buffer.length); socket.receive(packet); byte[] data = packet.getData(); int length = packet.getLength(); InetAddress address = packet.getAddress(); String content = new String(data, 0, length); System.out.println(address.getHostName() + ":" + content); if ("exit".equals(content)) break; } socket.close();
java
复制代码
// 接收端 3 MulticastSocket socket = new MulticastSocket(10000); // 将当前主机添加到其他组播地址中 InetAddress group = InetAddress.getByName("224.0.0.3"); socket.joinGroup(group); while (true) { byte[] buffer = new byte[1024]; DatagramPacket packet = new DatagramPacket(buffer, buffer.length); socket.receive(packet); byte[] data = packet.getData(); int length = packet.getLength(); InetAddress address = packet.getAddress(); String content = new String(data, 0, length); System.out.println(address.getHostName() + ":" + content); if ("exit".equals(content)) break; } socket.close();

广播

广播需要指定广播地址和端口,广播地址范围:255.255.255.255。

代码同单播一致,只需要将发送端代码中的 InetAddress.getLocalHost() 替换为 InetAddress.getByName("255.255.255.255")

TCP

TCP 是一种有连接的协议,它建立连接,发送数据,并确保数据安全。

代码示例

java
复制代码
Socket socket = new Socket(InetAddress.getLocalHost().getHostAddress(), 8888); OutputStream outputStream = socket.getOutputStream(); OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream); BufferedWriter bufferedWriter = new BufferedWriter(outputStreamWriter); Scanner scanner = new Scanner(System.in); while (true) { String content = scanner.nextLine(); bufferedWriter.write(content); bufferedWriter.newLine(); bufferedWriter.flush(); if ("exit".equals(content)) break; } scanner.close(); bufferedWriter.close(); socket.close();
java
复制代码
ServerSocket serverSocket = new ServerSocket(8888); System.out.println("服务启动,等待客户端连接"); Socket accept = serverSocket.accept(); System.out.println("客户端已连接"); InputStream inputStream = accept.getInputStream(); InputStreamReader inputStreamReader = new InputStreamReader(inputStream); BufferedReader bufferedReader = new BufferedReader(inputStreamReader); String content; while ((content = bufferedReader.readLine()) != null) { System.out.println(content); if ("exit".equals(content)) break; } bufferedReader.close(); accept.close(); serverSocket.close();

三次握手、四次挥手

TCP 三次握手是 TCP 连接的初始阶段,它用于建立连接。

  1. 客户端发送一个 SYN 包给服务端,表示要建立连接。
  2. 服务端收到 SYN 包后,发送一个 SYN+ACK 包给客户端,表示服务端已收到连接请求,并准备建立连接。
  3. 客户端收到 SYN+ACK 包后,发送一个 ACK 包给服务端,表示客户端已收到连接请求,并准备建立连接。

TCP 四次挥手是 TCP 断开的最后阶段,它用于断开连接。

  1. 客户端发送一个 FIN 包给服务端,表示要断开连接。
  2. 服务端收到 FIN 包后,发送一个 ACK 包给客户端,表示服务端已收到断开连接请求。
  3. 客户端收到 ACK 包后,发送一个 ACK 包给服务端,表示客户端已收到断开连接请求。
  4. 服务端收到 ACK 包后,发送一个 FIN 包给客户端,表示服务端已收到断开连接请求。

综合练习

多发多收

java
复制代码
// 略

互相发送

java
复制代码
Socket socket = new Socket(InetAddress.getLocalHost().getHostAddress(), 8888); OutputStream outputStream = socket.getOutputStream(); OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream); BufferedWriter bufferedWriter = new BufferedWriter(outputStreamWriter); String content = "Hello"; bufferedWriter.write(content); bufferedWriter.newLine(); bufferedWriter.flush(); InputStream inputStream = socket.getInputStream(); InputStreamReader inputStreamReader = new InputStreamReader(inputStream); BufferedReader bufferedReader = new BufferedReader(inputStreamReader); String reply = bufferedReader.readLine(); System.out.println(reply); bufferedWriter.close(); socket.close();
java
复制代码
ServerSocket serverSocket = new ServerSocket(8888); System.out.println("服务启动,等待客户端连接"); Socket accept = serverSocket.accept(); System.out.println("客户端已连接"); InputStream inputStream = accept.getInputStream(); InputStreamReader inputStreamReader = new InputStreamReader(inputStream); BufferedReader bufferedReader = new BufferedReader(inputStreamReader); String content = bufferedReader.readLine(); System.out.println(content); OutputStream outputStream = accept.getOutputStream(); OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream); BufferedWriter bufferedWriter = new BufferedWriter(outputStreamWriter); String reply = "Hello!"; bufferedWriter.write(reply); bufferedWriter.newLine(); bufferedWriter.flush(); bufferedWriter.close(); bufferedReader.close(); accept.close(); serverSocket.close();

文件上传

java
复制代码
Socket socket = new Socket(InetAddress.getLocalHost().getHostAddress(), 8888); OutputStream outputStream = socket.getOutputStream(); BufferedOutputStream bos = new BufferedOutputStream(outputStream); File file = new File(System.getProperty("user.dir") + File.separator + "test.zip"); FileInputStream fis = new FileInputStream(file); BufferedInputStream bis = new BufferedInputStream(fis); byte[] buffer = new byte[1024]; int len; while ((len = bis.read(buffer)) != -1) { bos.write(buffer, 0, len); } socket.shutdownOutput(); InputStream inputStream = socket.getInputStream(); InputStreamReader inputStreamReader = new InputStreamReader(inputStream); BufferedReader bufferedReader = new BufferedReader(inputStreamReader); String reply = bufferedReader.readLine(); System.out.println(reply); bufferedReader.close(); bis.close(); bos.close(); socket.close();
java
复制代码
ServerSocket serverSocket = new ServerSocket(8888); System.out.println("服务启动,等待客户端连接"); Socket accept = serverSocket.accept(); System.out.println("客户端已连接"); InputStream inputStream = accept.getInputStream(); BufferedInputStream bis = new BufferedInputStream(inputStream); File file = new File(System.getProperty("user.dir") + File.separator + "test-copy.zip"); FileOutputStream fos = new FileOutputStream(file); BufferedOutputStream bos = new BufferedOutputStream(fos); byte[] buffer = new byte[1024]; int len; while ((len = bis.read(buffer)) != -1) { bos.write(buffer, 0, len); } OutputStream outputStream = accept.getOutputStream(); OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream); BufferedWriter bufferedWriter = new BufferedWriter(outputStreamWriter); String reply = "Hello!"; bufferedWriter.write(reply); bufferedWriter.newLine(); bufferedWriter.flush(); bufferedWriter.close(); bos.close(); bis.close(); accept.close(); serverSocket.close();

文件上传时文件重名

java
复制代码
// 使用 UUID 创建文件名 UUID uuid = UUID.randomUUID(); File file = new File(System.getProperty("user.dir") + File.separator + uuid + ".zip"); FileOutputStream fos = new FileOutputStream(file); BufferedOutputStream bos = new BufferedOutputStream(fos);

多线程接收文件

java
复制代码
Socket socket = new Socket(InetAddress.getLocalHost().getHostAddress(), 8888); OutputStream outputStream = socket.getOutputStream(); BufferedOutputStream bos = new BufferedOutputStream(outputStream); File file = new File(System.getProperty("user.dir") + File.separator + "test.zip"); FileInputStream fis = new FileInputStream(file); BufferedInputStream bis = new BufferedInputStream(fis); byte[] buffer = new byte[1024]; int len; while ((len = bis.read(buffer)) != -1) { bos.write(buffer, 0, len); } socket.shutdownOutput(); InputStream inputStream = socket.getInputStream(); InputStreamReader inputStreamReader = new InputStreamReader(inputStream); BufferedReader bufferedReader = new BufferedReader(inputStreamReader); String reply = bufferedReader.readLine(); System.out.println(reply); socket.close();
java
复制代码
ServerSocket serverSocket = new ServerSocket(8888); System.out.println("服务启动,等待客户端连接"); while (true) { Socket accept = serverSocket.accept(); System.out.println("客户端已连接"); new MyThread(accept).start(); }
java
复制代码
public class MyThread extends Thread { private Socket socket; public MyThread(Socket socket) { this.socket = socket; } @Override public void run() { try { InputStream inputStream = socket.getInputStream(); BufferedInputStream bis = new BufferedInputStream(inputStream); UUID uuid = UUID.randomUUID(); File file = new File(System.getProperty("user.dir") + File.separator + uuid + ".zip"); FileOutputStream fos = new FileOutputStream(file); BufferedOutputStream bos = new BufferedOutputStream(fos); byte[] buffer = new byte[1024]; int len; while ((len = bis.read(buffer)) != -1) { bos.write(buffer, 0, len); } OutputStream outputStream = socket.getOutputStream(); OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream); BufferedWriter bufferedWriter = new BufferedWriter(outputStreamWriter); String reply = "Hello!"; bufferedWriter.write(reply); bufferedWriter.newLine(); bufferedWriter.flush(); bufferedWriter.close(); socket.close(); } catch (IOException e) { e.printStackTrace(); } } }

线程池优化

java
复制代码
public class MyRunnable implements Runnable { ...... }
java
复制代码
ServerSocket serverSocket = new ServerSocket(8888); System.out.println("服务启动,等待客户端连接"); ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor( 3, 6, 60, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>(), Executors.defaultThreadFactory(), new ThreadPoolExecutor.AbortPolicy() ); while (true) { Socket accept = serverSocket.accept(); System.out.println("客户端已连接"); threadPoolExecutor.execute(new MyRunnable(accept)); }

接收浏览器请求

java
复制代码
// 启动任意 server 代码,在浏览器控制台输入 http://localhost:端口

聊天室

java
复制代码
// 略
0个评论
点击登录,快来和大家讨论吧~
表情
图片
暂无评论
下载 APP