当前位置:首页 > 技术 > 正文内容

Java网络编程实战指南:TCP/UDP通信、HTTP请求与正则爬虫开发

访客 技术 2026年7月19日 1

网络基础与诊断工具

在进行网络编程之前,掌握基本的网络诊断工具是必不可少的。可以使用 ipconfig (Windows) 或 ifconfig (Linux/macOS) 查看本机网络配置,使用 ping 测试网络连通性。为了监控本机的网络连接状态和端口占用情况,netstat -an 或系统自带的资源监视器是非常实用的工具。在调试阶段,Telnet 或 NetAssist 等网络调试助手可以作为简易的客户端或服务器,帮助验证端口监听和数据收发是否正常。

TCP客户端编程实战

TCP协议提供面向连接的可靠数据传输。在Java中,通过 Socket 类即可实现TCP客户端。编写客户端时,核心在于正确处理输入输出流,并解决常见的缓冲区刷新与字符编码问题。

以下是一个重构后的TCP客户端示例,实现了与控制台交互并向服务器收发数据的功能,同时使用 InputStreamReaderOutputStreamWriter 解决了中文乱码问题:


import java.io.*;
import java.net.Socket;
import java.nio.charset.StandardCharsets;
import java.util.Scanner;

public class TcpClientApp {
    public static void main(String[] args) {
        String serverHost = "192.168.1.100"; 
        int serverPort = 8888;

        try (Socket clientConnection = new Socket(serverHost, serverPort);
             BufferedReader reader = new BufferedReader(new InputStreamReader(clientConnection.getInputStream(), StandardCharsets.UTF_8));
             PrintWriter writer = new PrintWriter(new OutputStreamWriter(clientConnection.getOutputStream(), StandardCharsets.UTF_8), true);
             Scanner consoleScanner = new Scanner(System.in)) {

            System.out.println("已连接到服务器,请输入消息(输入'exit'退出):");
            
            while (true) {
                String userInput = consoleScanner.nextLine();
                writer.println(userInput); 
                
                if ("exit".equalsIgnoreCase(userInput)) {
                    break;
                }

                String serverResponse = reader.readLine(); 
                if (serverResponse != null) {
                    System.out.println("服务器响应: " + serverResponse);
                } else {
                    System.out.println("服务器已断开连接。");
                    break;
                }
            }
        } catch (IOException e) {
            System.err.println("网络通信异常: " + e.getMessage());
        }
    }
}

注意事项:

  • 如果服务器端使用 readLine() 读取数据,客户端发送的消息必须包含换行符(如使用 println)。
  • 若遇到 Connection Refused,请检查服务器是否启动、IP是否正确以及防火墙设置。
  • 严格交替的"一发一收"模式容易导致死锁,实际应用中通常需要引入多线程分别处理读写操作。

TCP服务器与多线程模型

服务器端使用 ServerSocket 监听特定端口。当接收到客户端连接时,会返回一个新的 Socket 用于与该客户端通信。为了支持多客户端并发访问,必须引入多线程机制。

下面展示了一个基于线程池的多线程菜单服务器实现,相比传统的为每个连接创建新线程,使用线程池能有效控制资源消耗并提升响应速度:


import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class MultiThreadMenuServer {
    private static final int PORT = 8888;

    public static void main(String[] args) {
        ExecutorService threadPool = Executors.newCachedThreadPool();

        try (ServerSocket serverListener = new ServerSocket(PORT)) {
            System.out.println("菜单服务器已启动,监听端口: " + PORT);

            while (true) {
                Socket clientSocket = serverListener.accept();
                threadPool.execute(new ClientHandler(clientSocket));
            }
        } catch (IOException e) {
            System.err.println("服务器启动失败: " + e.getMessage());
        } finally {
            threadPool.shutdown();
        }
    }
}

class ClientHandler implements Runnable {
    private final Socket clientSocket;

    public ClientHandler(Socket socket) {
        this.clientSocket = socket;
    }

    @Override
    public void run() {
        String clientInfo = clientSocket.getInetAddress().getHostAddress() + ":" + clientSocket.getPort();
        System.out.println("新客户端连接: " + clientInfo);

        try (BufferedReader in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream(), StandardCharsets.UTF_8));
             PrintWriter out = new PrintWriter(new OutputStreamWriter(clientSocket.getOutputStream(), StandardCharsets.UTF_8), true)) {

            out.println("欢迎使用菜单系统:\n1. 获取服务器时间\n2. 获取您的IP和端口\n3. 退出");
            
            String request;
            while ((request = in.readLine()) != null) {
                switch (request.trim()) {
                    case "1":
                        out.println("当前服务器时间: " + LocalDateTime.now());
                        break;
                    case "2":
                        out.println("您的连接信息: " + clientInfo);
                        break;
                    case "3":
                        out.println("再见!");
                        return; 
                    default:
                        out.println("无效选项,请重新输入。");
                }
                out.println("请继续输入选项(1-3):");
            }
        } catch (IOException e) {
            System.err.println("处理客户端 " + clientInfo + " 时发生错误: " + e.getMessage());
        } finally {
            try { clientSocket.close(); } catch (IOException ignored) {}
            System.out.println("客户端断开: " + clientInfo);
        }
    }
}

如果需要在客户端和服务器之间传输对象或字节数组,可以将 InputStream 包装为 ObjectInputStream,或直接操作字节流进行文件下载与上传。

HTTP协议交互与网页抓取

HTTP协议是基于TCP的应用层协议。通过原生Socket发送符合HTTP规范的请求头,即可获取网页内容。一个标准的GET请求头如下:


GET /index.html HTTP/1.1
Host: www.example.com
Connection: close

注意请求头末尾必须包含一个空行。在Java中,除了使用底层Socket,更推荐使用高级API:

  • Java 11 HttpClient:提供了现代化的异步/同步请求能力,原生支持HTTP/2。
  • Jsoup:强大的HTML解析库,不仅能发起请求,还能通过CSS选择器轻松提取DOM节点,是编写网络爬虫的首选工具。

使用正则表达式提取网页链接

在不使用DOM解析库的情况下,可以通过正则表达式从HTML源码中提取超链接和图片地址。以下代码展示了如何匹配 <a href="..."><img src="..."> 标签中的URL:


import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.ArrayList;
import java.util.List;

public class HtmlLinkExtractor {
    
    private static final String URL_REGEX = "(?:href|src)\\s*=\\s*(?:\"([^\"]+)\"|'([^']+)'|([^\\s>]+))";
    private static final Pattern LINK_PATTERN = Pattern.compile(URL_REGEX, Pattern.CASE_INSENSITIVE);

    public static List<String> extractUrls(String htmlContent) {
        List<String> urlList = new ArrayList<>();
        Matcher matcher = LINK_PATTERN.matcher(htmlContent);

        while (matcher.find()) {
            String url = matcher.group(1) != null ? matcher.group(1) : 
                         (matcher.group(2) != null ? matcher.group(2) : matcher.group(3));
            
            if (url != null && !url.startsWith("javascript:")) {
                urlList.add(url);
            }
        }
        return urlList;
    }

    public static void main(String[] args) {
        String sampleHtml = "<div><a href=\"https://example.com/page1\">Link1</a>" +
                            "<img src='images/pic.jpg' border='0'/>" +
                            "<a href=https://example.com/page2>Link2</a></div>";
        
        List<String> extractedLinks = extractUrls(sampleHtml);
        for (String link : extractedLinks) {
            System.out.println("发现链接: " + link);
        }
    }
}

提取出相对路径后,通常需要结合当前网页的Base URL,将其转换为绝对路径,以便后续进行资源下载或深度爬取。

UDP数据报通信

与TCP不同,UDP是无连接的协议,通过 DatagramSocketDatagramPacket 进行数据收发。UDP服务器不需要 accept() 等待连接,而是直接通过 receive() 方法接收数据报,提取发送方的IP和端口后,再将响应数据打包成 DatagramPacket 发送回去。UDP适用于对实时性要求高、允许少量丢包的场景,如视频流传输和实时在线游戏。

相关文章

Linux crontab 详解

1) crontab 是什么cron 是 Linux 的定时任务守护进程;crontab 是用来编辑/查看“按时间周期执行命令”的表(cron table)。常见两类:用户 crontab:每个用户一份(crontab -e 编辑)系统级 crontab / cron.d:可指定执行用户(/etc/crontab、/etc/cron.d/*)2) crontab 时间...

Mac 安装 Node.js 指南

方法一:通过官网安装包(最简单,适合初学者)如果你只是想快速安装并开始使用,这是最直接的方法。访问 Node.js 官网。页面会显示两个版本:LTS (Recommended For Most Users):长期支持版,最稳定。建议选这个。Current:最新特性版,包含最新功能但可能不够稳定。下载 .pkg 安装包并运行。按照安装向导点击“下一步”即可完成。方法二:使用 Homebrew 安装(...

Dom\HTML_NO_DEFAULT_NS 的副作用:自动加闭合标签

在使用Dom\HTMLDocument时,Dom\HTML_NO_DEFAULT_NS 将禁止在解析过程中设置元素的命名空间, 此设置是为了与DOMDocument向后兼容而存在的。当使用它时,已知的一个副作用就是:自动加闭合标签例如 </img> 为什么会这样?当你使用:Dom\HTML_NO_DEFAULT_NS文档会变成 无命名空间模式,此时内部更接近 XML...

Laravel 事件和监听器创建

在 Laravel 中,使用 Artisan 命令创建 Events(事件) 和 Listeners(监听器) 是非常高效的。你可以通过以下几种方式来实现:1. 手动创建单个 Event如果你只想创建一个事件类,可以使用 make:event 命令:Bashphp artisan make:event UserRegistered执行后,文件将生成在 app/Even...

自定义域名解析神器 dnsmasq

什么是 dnsmasq?dnsmasq 是一个轻量级、功能强大的网络服务工具,专为小型和中等规模网络设计。它是一个综合的网络基础设施解决方案[1]。dnsmasq 能做什么?功能说明应用场景DNS 转发与缓存将 DNS 查询转发到上游服务器(ISP、Google DNS 等),并在本地缓存结果加快 DNS 查询速度,减少外部 DNS 流量本地 DNS解析本地网络设备的主机名,无需编辑&n...

linux screen 用法详情 (nohup 的替代方案)

一、screen 是什么?能干嘛?screen 是一个终端复用器,可以:在一个 SSH 会话中开多个“虚拟终端”SSH 断线后,程序仍然在后台运行随时重新连接到原来的会话特别适合:nohup 的替代方案跑脚本 / 爬虫 / 训练模型运维、远程开发二、安装 screen# CentOS / Rocky / Almayum install -y screen# Debian / Ubuntuapt i...

发表评论

访客

◎欢迎参与讨论,请在这里发表您的看法和观点。