第八章:Java网络编程(上)
在这一章中,我们将开始学习Java网络编程的基础知识。网络编程是现代应用程序开发中不可或缺的一部分,它使得不同计算机之间能够通过网络进行通信和数据交换。无论是浏览网页、发送电子邮件,还是使用即时通讯软件,背后都涉及到网络编程的原理。
1. 网络编程基础
1.1 网络通信基本概念
在开始学习Java网络编程之前,我们需要了解一些基本的网络通信概念:
- 网络(Network):连接计算机的硬件和软件系统,使它们能够相互通信。
- 协议(Protocol):计算机之间通信的规则和约定。
- IP地址(IP Address):网络上每台计算机的唯一标识符,如
192.168.1.1
。 - 端口(Port):在同一台计算机上区分不同网络应用的数字标识,范围从0到65535。
- DNS(Domain Name System):将域名(如
www.example.com
)转换为IP地址的系统。 - TCP(Transmission Control Protocol):面向连接的可靠传输协议。
- UDP(User Datagram Protocol):无连接的不可靠传输协议,但速度快。
1.2 Java网络编程API
Java提供了丰富的网络编程API,主要在java.net
包中,包括:
- InetAddress:表示IP地址
- Socket:TCP客户端套接字
- ServerSocket:TCP服务器套接字
- DatagramSocket:UDP套接字
- DatagramPacket:UDP数据包
- URL:统一资源定位符
- URLConnection:与URL建立连接的类
1.3 网络地址
Java中使用InetAddress
类来表示IP地址。下面是一些常用的方法:
import java.net.InetAddress;
import java.net.UnknownHostException;
public class InetAddressDemo {
public static void main(String[] args) {
try {
// 获取本机的InetAddress实例
InetAddress localHost = InetAddress.getLocalHost();
System.out.println("本机名: " + localHost.getHostName());
System.out.println("本机IP地址: " + localHost.getHostAddress());
// 根据主机名获取InetAddress实例
InetAddress baidu = InetAddress.getByName("www.baidu.com");
System.out.println("百度的IP地址: " + baidu.getHostAddress());
// 获取所有的IP地址
InetAddress[] addresses = InetAddress.getAllByName("www.google.com");
for (InetAddress address : addresses) {
System.out.println("谷歌的IP地址: " + address.getHostAddress());
}
} catch (UnknownHostException e) {
e.printStackTrace();
}
}
}
2. Socket编程
Socket(套接字)是网络通信的基本单位,它提供了计算机之间通信的端点。Java中的Socket编程主要分为TCP套接字和UDP套接字。
2.1 TCP通信
TCP(传输控制协议)是一种面向连接的、可靠的、基于字节流的传输层通信协议。在Java中,TCP通信使用Socket
和ServerSocket
类。
2.1.1 TCP服务器端
import java.io.*;
import java.net.*;
public class TCPServer {
public static void main(String[] args) {
try {
// 创建服务器套接字,监听8888端口
ServerSocket serverSocket = new ServerSocket(8888);
System.out.println("服务器已启动,等待客户端连接...");
// 接受客户端连接
Socket clientSocket = serverSocket.accept();
System.out.println("客户端已连接: " + clientSocket.getInetAddress());
// 获取输入流,读取客户端发送的数据
BufferedReader in = new BufferedReader(
new InputStreamReader(clientSocket.getInputStream()));
// 获取输出流,向客户端发送数据
PrintWriter out = new PrintWriter(
clientSocket.getOutputStream(), true);
String inputLine;
while ((inputLine = in.readLine()) != null) {
System.out.println("收到客户端消息: " + inputLine);
// 将收到的消息转为大写后发送回客户端
out.println("服务器回复: " + inputLine.toUpperCase());
// 如果客户端发送"再见",则结束通信
if (inputLine.equals("再见")) {
break;
}
}
// 关闭资源
in.close();
out.close();
clientSocket.close();
serverSocket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
2.1.2 TCP客户端
import java.io.*;
import java.net.*;
import java.util.Scanner;
public class TCPClient {
public static void main(String[] args) {
try {
// 创建套接字,连接服务器(本例中连接本机的8888端口)
Socket socket = new Socket("localhost", 8888);
System.out.println("已连接到服务器");
// 获取输入流,读取服务器发送的数据
BufferedReader in = new BufferedReader(
new InputStreamReader(socket.getInputStream()));
// 获取输出流,向服务器发送数据
PrintWriter out = new PrintWriter(
socket.getOutputStream(), true);
// 从控制台读取输入
Scanner scanner = new Scanner(System.in);
String userInput;
System.out.println("请输入消息(输入'再见'结束):");
while (!(userInput = scanner.nextLine()).equals("再见")) {
// 发送消息到服务器
out.println(userInput);
// 接收服务器的响应
String response = in.readLine();
System.out.println(response);
}
// 发送"再见"消息
out.println("再见");
// 关闭资源
scanner.close();
in.close();
out.close();
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
2.2 UDP通信
UDP(用户数据报协议)是一种无连接的传输层协议,它不保证可靠的数据传输。在Java中,UDP通信使用DatagramSocket
和DatagramPacket
类。
2.2.1 UDP服务器端
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
public class UDPServer {
public static void main(String[] args) {
try {
// 创建DatagramSocket,监听9999端口
DatagramSocket socket = new DatagramSocket(9999);
System.out.println("UDP服务器已启动,等待数据...");
byte[] receiveData = new byte[1024];
while (true) {
// 创建DatagramPacket用于接收数据
DatagramPacket receivePacket = new DatagramPacket(
receiveData, receiveData.length);
// 接收数据
socket.receive(receivePacket);
// 将接收到的数据转换为字符串
String message = new String(
receivePacket.getData(), 0, receivePacket.getLength());
System.out.println("收到消息: " + message);
System.out.println("来自: " + receivePacket.getAddress() +
":" + receivePacket.getPort());
// 准备回复数据
String response = "服务器已收到: " + message;
byte[] sendData = response.getBytes();
// 创建DatagramPacket用于发送数据
DatagramPacket sendPacket = new DatagramPacket(
sendData, sendData.length,
receivePacket.getAddress(), receivePacket.getPort());
// 发送数据
socket.send(sendPacket);
// 如果收到"再见",则退出循环
if (message.equals("再见")) {
break;
}
}
// 关闭资源
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
2.2.2 UDP客户端
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.util.Scanner;
public class UDPClient {
public static void main(String[] args) {
try {
// 创建DatagramSocket
DatagramSocket socket = new DatagramSocket();
// 获取服务器地址
InetAddress serverAddress = InetAddress.getByName("localhost");
int serverPort = 9999;
// 从控制台读取输入
Scanner scanner = new Scanner(System.in);
byte[] receiveData = new byte[1024];
System.out.println("请输入消息(输入'再见'结束):");
String userInput;
while (!(userInput = scanner.nextLine()).equals("再见")) {
// 将用户输入转换为字节数组
byte[] sendData = userInput.getBytes();
// 创建DatagramPacket用于发送数据
DatagramPacket sendPacket = new DatagramPacket(
sendData, sendData.length, serverAddress, serverPort);
// 发送数据
socket.send(sendPacket);
// 创建DatagramPacket用于接收数据
DatagramPacket receivePacket = new DatagramPacket(
receiveData, receiveData.length);
// 接收数据
socket.receive(receivePacket);
// 将接收到的数据转换为字符串
String response = new String(
receivePacket.getData(), 0, receivePacket.getLength());
System.out.println(response);
}
// 发送"再见"消息
byte[] sendData = "再见".getBytes();
DatagramPacket sendPacket = new DatagramPacket(
sendData, sendData.length, serverAddress, serverPort);
socket.send(sendPacket);
// 关闭资源
scanner.close();
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
2.3 多客户端处理
在实际应用中,服务器通常需要同时处理多个客户端的连接。这可以通过多线程来实现:
import java.io.*;
import java.net.*;
public class MultiThreadTCPServer {
public static void main(String[] args) {
try {
ServerSocket serverSocket = new ServerSocket(8888);
System.out.println("多线程服务器已启动,等待客户端连接...");
while (true) {
// 接受客户端连接
Socket clientSocket = serverSocket.accept();
System.out.println("新客户端已连接: " +
clientSocket.getInetAddress());
// 为每个客户端创建一个新的线程
ClientHandler handler = new ClientHandler(clientSocket);
new Thread(handler).start();
}
} catch (IOException e) {
e.printStackTrace();
}
}
// 处理客户端通信的线程类
static class ClientHandler implements Runnable {
private Socket clientSocket;
public ClientHandler(Socket socket) {
this.clientSocket = socket;
}
@Override
public void run() {
try {
// 获取输入流,读取客户端发送的数据
BufferedReader in = new BufferedReader(
new InputStreamReader(clientSocket.getInputStream()));
// 获取输出流,向客户端发送数据
PrintWriter out = new PrintWriter(
clientSocket.getOutputStream(), true);
String inputLine;
while ((inputLine = in.readLine()) != null) {
System.out.println("收到客户端 " +
clientSocket.getInetAddress() +
" 消息: " + inputLine);
// 将收到的消息转为大写后发送回客户端
out.println("服务器回复: " + inputLine.toUpperCase());
// 如果客户端发送"再见",则结束通信
if (inputLine.equals("再见")) {
break;
}
}
// 关闭资源
in.close();
out.close();
clientSocket.close();
System.out.println("客户端 " +
clientSocket.getInetAddress() +
" 已断开连接");
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
3. URL处理
URL(统一资源定位符)是互联网上资源的地址。Java提供了URL
和URLConnection
类来处理URL资源。
3.1 URL类
URL
类表示一个统一资源定位符,它可以用来访问网络上的资源。
import java.net.MalformedURLException;
import java.net.URL;
public class URLDemo {
public static void main(String[] args) {
try {
// 创建URL对象
URL url = new URL("https://www.baidu.com:443/index.html?query=java#section1");
// 获取URL的各个部分
System.out.println("协议: " + url.getProtocol());
System.out.println("主机名: " + url.getHost());
System.out.println("端口: " + url.getPort());
System.out.println("路径: " + url.getPath());
System.out.println("查询参数: " + url.getQuery());
System.out.println("片段标识符: " + url.getRef());
} catch (MalformedURLException e) {
e.printStackTrace();
}
}
}
3.2 URLConnection类
URLConnection
类表示应用程序和URL之间的通信链接,它可以用来读取和写入URL引用的资源。
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URL;
import java.net.URLConnection;
public class URLConnectionDemo {
public static void main(String[] args) {
try {
// 创建URL对象
URL url = new URL("https://www.baidu.com");
// 打开连接
URLConnection connection = url.openConnection();
// 设置请求属性
connection.setRequestProperty("User-Agent",
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36");
// 建立实际连接
connection.connect();
// 获取响应头信息
System.out.println("内容类型: " + connection.getContentType());
System.out.println("内容长度: " + connection.getContentLength());
System.out.println("最后修改时间: " + new java.util.Date(connection.getLastModified()));
// 读取响应内容
BufferedReader reader = new BufferedReader(
new InputStreamReader(connection.getInputStream(), "UTF-8"));
String line;
StringBuilder content = new StringBuilder();
while ((line = reader.readLine()) != null) {
content.append(line).append("\n");
}
reader.close();
// 打印前100个字符的内容
System.out.println("响应内容(前100个字符): " +
content.substring(0, Math.min(100, content.length())));
} catch (IOException e) {
e.printStackTrace();
}
}
}
3.3 读取网页内容
下面是一个更完整的例子,展示如何读取网页内容并保存到文件中:
import java.io.*;
import java.net.URL;
import java.net.URLConnection;
public class WebPageDownloader {
public static void main(String[] args) {
try {
// 要下载的网页URL
String urlString = "https://www.baidu.com";
// 保存的文件名
String fileName = "baidu_homepage.html";
// 创建URL对象
URL url = new URL(urlString);
// 打开连接
URLConnection connection = url.openConnection();
// 设置请求头,模拟浏览器访问
connection.setRequestProperty("User-Agent",
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36");
// 建立实际连接
connection.connect();
// 创建输入流读取网页内容
BufferedReader reader = new BufferedReader(
new InputStreamReader(connection.getInputStream(), "UTF-8"));
// 创建输出流保存到文件
BufferedWriter writer = new BufferedWriter(
new FileWriter(fileName));
String line;
while ((line = reader.readLine()) != null) {
// 写入文件
writer.write(line);
writer.newLine();
}
// 关闭资源
reader.close();
writer.close();
System.out.println("网页已成功下载到 " + fileName);
} catch (IOException e) {
e.printStackTrace();
}
}
}
总结
在本章的第一部分中,我们学习了Java网络编程的基础知识,包括网络通信的基本概念、Java网络编程API、Socket编程(TCP和UDP)以及URL处理。这些知识为我们开发网络应用程序奠定了基础。
在下一部分中,我们将继续学习更高级的网络编程内容,包括HTTP客户端、网络爬虫入门和邮件发送等。
练习
- 编写一个简单的聊天程序,使用TCP套接字实现客户端和服务器之间的通信。
- 实现一个文件传输程序,允许客户端向服务器上传文件。
- 编写一个程序,使用URL类下载指定网页的所有图片。
- 实现一个简单的网络端口扫描器,检测指定主机上哪些端口是开放的。
- 创建一个多线程的聊天服务器,支持多个客户端同时连接和通信。