零基础学Java——第八章:Java网络编程(上)

第八章: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通信使用SocketServerSocket类。

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通信使用DatagramSocketDatagramPacket类。

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提供了URLURLConnection类来处理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客户端、网络爬虫入门和邮件发送等。

练习

  1. 编写一个简单的聊天程序,使用TCP套接字实现客户端和服务器之间的通信。
  2. 实现一个文件传输程序,允许客户端向服务器上传文件。
  3. 编写一个程序,使用URL类下载指定网页的所有图片。
  4. 实现一个简单的网络端口扫描器,检测指定主机上哪些端口是开放的。
  5. 创建一个多线程的聊天服务器,支持多个客户端同时连接和通信。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值