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

Java网络编程核心技术与实践

访客 技术 2026年7月18日 1

网络编程基础概念

网络是由多个节点通过通信链路连接而成的系统,用于实现信息交换和资源共享。在计算机领域中,网络编程指的是让不同设备之间建立连接并进行数据交互的技术手段。

计算机网络分类

  • 局域网(LAN):覆盖范围小,如办公室或家庭内部网络
  • 城域网(MAN):城市级别的网络覆盖
  • 广域网(WAN):跨区域甚至全球范围的网络,典型代表是互联网(Internet)

其中:

  • 万维网(WWW)提供网页浏览服务
  • 物联网(IoT)实现物与物之间的智能互联

OSI七层参考模型

层级名称功能说明
7应用层提供HTTP、FTP、SMTP等用户直接使用的协议
6表示层处理数据格式转换、加密解密等工作
5会话层管理会话建立与恢复,支持断点续传
4传输层控制数据传输可靠性,如TCP/UDP协议
3网络层负责IP地址分配与路由选择
2数据链路层使用MAC地址确保帧正确传输
1物理层定义物理介质和信号传输方式,如光纤、双绞线

TCP/IP四层模型

  • 应用层:面向用户的高层协议,如HTTP、DNS
  • 传输层:提供端到端的数据传输服务(TCP/UDP)
  • 网络层:基于IP协议完成主机间寻址与数据包转发
  • 链路层:负责底层硬件连接,如以太网驱动

TCP与UDP对比

TCP 是一种面向连接的可靠传输协议,具有以下特点:

  • 通过三次握手建立连接,四次挥手断开连接
  • 保证数据顺序和完整性
  • 适用于文件传输、网页访问等场景

UDP 是无连接的传输协议,其特性包括:

  • 不保证送达,也不重传丢失数据
  • 每个数据报最大64KB
  • 适合实时性要求高的应用,如视频通话、在线游戏

IP地址详解

IP协议为每台联网设备分配唯一标识符:

  • IPv4:32位地址,表示为四个0~255之间的十进制数,例如 192.168.1.1
  • IPv6:128位地址,采用十六进制分段表示,如 2001:0db8::1

IPv4地址分类:

  • A类:1.0.0.0 ~ 126.255.255.255(大型组织)
  • B类:128.0.0.0 ~ 191.255.255.255(中型企业)
  • C类:192.0.0.0 ~ 223.255.255.255(个人/小型网络)
  • D类:组播地址
  • E类:科研实验用途

特殊地址:

  • 127.0.0.1 表示本地回环地址,常用于测试
  • 常用命令:ping 测试连通性,ipconfig 查看本机IP配置

端口机制

端口号用于区分同一台机器上的不同网络服务,取值范围0~65535:

  • 公认端口:0–1023,由系统保留(如HTTP:80, FTP:21)
  • 注册端口:1024–49151,供第三方应用程序注册使用
  • 动态/私有端口:49152–65535,临时分配给客户端程序

常见服务端口:

  • MySQL: 3306
  • Oracle: 1521
  • Tomcat: 8080
  • SMTP邮件发送: 25

InetAddress 类使用

Java中的 InetAddress 类封装了IP地址相关信息,常用方法如下:

import java.net.InetAddress;
import java.io.IOException;

public class AddressDemo {
    public static void main(String[] args) throws IOException {
        // 获取本地主机信息
        InetAddress local = InetAddress.getLocalHost();
        System.out.println("本机IP: " + local.getHostAddress());
        System.out.println("主机名: " + local.getHostName());

        // 根据域名获取公网IP
        InetAddress remote = InetAddress.getByName("www.baidu.com");
        System.out.println("百度IP: " + remote.getHostAddress());
        System.out.println("是否可达: " + remote.isReachable(3000));

        // 获取所有关联IP(DNS轮询)
        InetAddress[] addresses = InetAddress.getAllByName("www.google.com");
        for (InetAddress addr : addresses) {
            System.out.println("Google IP: " + addr.getHostAddress());
        }
    }
}

TCP Socket 编程原理

Socket 是网络通信的基本单元,由IP地址和端口号组成唯一通信端点。

基本通信流程

服务器端步骤:

  1. 创建 ServerSocket 并绑定监听端口
  2. 调用 accept() 等待客户端连接(阻塞操作)
  3. 通过输入流接收客户端请求数据
  4. 通过输出流向客户端发送响应
  5. 关闭资源

客户端步骤:

  1. 创建 Socket 指定目标服务器IP和端口
  2. 通过输出流发送请求
  3. 通过输入流读取服务器响应
  4. 关闭连接

案例一:文本消息收发

服务器端实现:

ServerSocket listener = new ServerSocket(8899);
System.out.println("服务启动,等待连接...");
Socket client = listener.accept();
InputStream input = client.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(input, "UTF-8"));
String msg = reader.readLine();
System.out.println("收到消息:" + msg);
reader.close(); client.close(); listener.close();

客户端实现:

Socket socket = new Socket("192.168.0.103", 8899);
OutputStream out = socket.getOutputStream();
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(out, "UTF-8"));
writer.write("你好服务器!");
writer.newLine();
writer.flush();
writer.close(); socket.close();

案例二:文件上传功能

服务器端接收图片:

ServerSocket server = new ServerSocket(9999);
Socket conn = server.accept();
InputStream netIn = conn.getInputStream();
FileOutputStream fileOut = new FileOutputStream("received_image.jpg");
byte[] buffer = new byte[4096];
int len;
while ((len = netIn.read(buffer)) != -1) {
    fileOut.write(buffer, 0, len);
}
fileOut.close(); netIn.close(); conn.close(); server.close();

客户端发送文件:

Socket sock = new Socket("192.168.0.103", 9999);
FileInputStream fileIn = new FileInputStream("local_photo.jpg");
OutputStream netOut = sock.getOutputStream();
byte[] buf = new byte[4096];
int length;
while ((length = fileIn.read(buf)) != -1) {
    netOut.write(buf, 0, length);
}
netOut.flush(); // 强制发送剩余数据
fileIn.close(); netOut.close(); sock.close();

案例三:多客户端并发聊天室

为了支持多个客户端同时连接,服务器需使用多线程处理每个连接。

ServerSocket server = new ServerSocket(1122);
while (true) {
    Socket client = server.accept();
    new Thread(() -> {
        try (BufferedReader br = new BufferedReader(
                new InputStreamReader(client.getInputStream()))) {
            String line;
            while ((line = br.readLine()) != null) {
                System.out.println(client.getInetAddress() + "说:" + line);
                if ("bye".equalsIgnoreCase(line)) break;
            }
        } catch (IOException ignored) {}
        finally {
            try { client.close(); } catch (IOException e) {}
        }
    }).start();
}

客户端可循环读取键盘输入并发送:

Scanner scanner = new Scanner(System.in);
BufferedWriter sender = new BufferedWriter(
    new OutputStreamWriter(socket.getOutputStream()));
String input;
while (!(input = scanner.nextLine()).equalsIgnoreCase("bye")) {
    sender.write(input);
    sender.newLine();
    sender.flush();
}

案例四:用户注册登录系统

利用Socket实现简单的账户管理系统,数据持久化至 properties 文件。

工具类 Tools.java:

public class Tools {
    private static final String FILE_PATH = "users.properties";

    public static Properties loadUserConfig() {
        Properties props = new Properties();
        File file = new File(FILE_PATH);
        if (file.exists()) {
            try (FileInputStream fis = new FileInputStream(file)) {
                props.load(fis);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return props;
    }

    public static void saveUserInfo(String userId, String userInfo) {
        try (FileOutputStream fos = new FileOutputStream(FILE_PATH, true)) {
            Properties props = new Properties();
            props.setProperty(userId, userInfo);
            props.store(fos, "Auto-saved user data");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

注册线程 RegisterHandler:

public class RegisterHandler extends Thread {
    public void run() {
        try (ServerSocket srv = new ServerSocket(666);
             Socket cli = srv.accept();
             BufferedReader in = new BufferedReader(
                 new InputStreamReader(cli.getInputStream()));
             BufferedWriter out = new BufferedWriter(
                 new OutputStreamWriter(cli.getOutputStream()))) {

            System.out.println("注册服务已就绪...");
            String userData = in.readLine();
            String uid = parseField(userData, "id");

            Properties allUsers = Tools.loadUserConfig();
            if (allUsers.containsKey(uid)) {
                out.write("该账号已存在");
            } else {
                Tools.saveUserInfo(uid, userData);
                out.write("注册成功");
            }
            out.newLine(); out.flush();

        } catch (IOException e) { e.printStackTrace(); }
    }

    private String parseField(String json, String key) {
        String pattern = key + "\s*:\s*\"?([^,\"]+)\"?";
        java.util.regex.Pattern p = java.util.regex.Pattern.compile(pattern);
        java.util.regex.Matcher m = p.matcher(json);
        return m.find() ? m.group(1) : null;
    }
}

登录验证逻辑 LoginHandler:

public class LoginHandler extends Thread {
    public void run() {
        try (ServerSocket srv = new ServerSocket(777);
             Socket cli = srv.accept();
             BufferedReader in = new BufferedReader(
                 new InputStreamReader(cli.getInputStream()));
             BufferedWriter out = new BufferedWriter(
                 new OutputStreamWriter(cli.getOutputStream()))) {

            String cred = in.readLine();
            String uid = parseField(cred, "id");
            String pwdInput = parseField(cred, "pwd");

            Properties users = Tools.loadUserConfig();
            if (!users.containsKey(uid)) {
                out.write("用户名或密码错误");
            } else {
                String stored = users.getProperty(uid);
                String correctPwd = extractPassword(stored);
                if (pwdInput.equals(correctPwd)) {
                    out.write("登录成功");
                } else {
                    out.write("密码错误");
                }
            }
            out.newLine(); out.flush();

        } catch (IOException e) { e.printStackTrace(); }
    }

    private String extractPassword(String record) {
        return java.util.regex.Pattern.compile("pwd\\s*:\\s*\"?([^,\"]+)\"?")
            .matcher(record).find() ? 
            java.util.regex.Pattern.compile("pwd\\s*:\\s*\"?([^,\"]+)\"?")
                .matcher(record).group(1) : "";
    }
}

客户端主程序 UserClient:

public class UserClient {
    public static void main(String[] args) throws Exception {
        System.out.println("=== 欢迎 ===\n1. 注册  2. 登录");
        Scanner scan = new Scanner(System.in);
        int choice = scan.nextInt();

        switch (choice) {
            case 1 -> performRegistration(scan);
            case 2 -> performLogin(scan);
            default -> System.out.println("无效选项");
        }
    }

    static void performRegistration(Scanner s) throws Exception {
        Socket sock = new Socket("192.168.0.103", 666);
        sendAndReceive(sock, buildJson(s, true));
    }

    static void performLogin(Scanner s) throws Exception {
        Socket sock = new Socket("192.168.0.103", 777);
        sendAndReceive(sock, buildJson(s, false));
    }

    static String buildJson(Scanner s, boolean isReg) {
        System.out.print("ID: "); int id = s.nextInt();
        System.out.print("密码: "); String pwd = s.next();
        if (isReg) {
            System.out.print("姓名: "); String name = s.next();
            System.out.print("年龄: "); int age = s.nextInt();
            return String.format("{\"id\":%d,\"name\":\"%s\",\"pwd\":\"%s\",\"age\":%d}", 
                id, name, pwd, age);
        } else {
            return String.format("{\"id\":%d,\"pwd\":\"%s\"}", id, pwd);
        }
    }

    static void sendAndReceive(Socket sock, String data) throws Exception {
        try (BufferedWriter w = new BufferedWriter(
                 new OutputStreamWriter(sock.getOutputStream()));
             BufferedReader r = new BufferedReader(
                 new InputStreamReader(sock.getInputStream()))) {

            w.write(data); w.newLine(); w.flush();
            System.out.println("服务器回复:" + r.readLine());
        }
    }
}

相关文章

Linux crontab 详解

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

富文本里可以允许的 HTML 属性

一、所有标签默认允许的安全属性(极少)class        (可选)id           (通常建议禁用)title️ 注意:id 容易被滥用做锚点注入,很多系统直接禁用class 允许的话最好只允许固定前缀(如 editor-*)二、a 标签允许属性<a href="" t...

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...

发表评论

访客

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