第64章

Java网络编程

掌握Socket编程与网络通信的核心技术和实际应用

学习目标

网络编程基础概念

网络编程是指编写能够在网络上进行数据通信的程序。Java提供了强大的网络编程API,使得开发网络应用变得相对简单。网络编程的核心是Socket(套接字),它是网络通信的端点。

Socket概念

Socket基本概念:
// Socket是网络通信的端点
// 包含IP地址和端口号
// 用于建立网络连接
  • 网络通信的基础
  • 包含IP地址和端口号
  • 支持TCP和UDP协议
  • 提供双向数据传输

客户端/服务器模型

基本架构:
// 服务器:监听端口,等待连接
// 客户端:主动连接服务器
// 建立连接后进行数据交换
  • 服务器被动等待连接
  • 客户端主动发起连接
  • 支持多客户端连接
  • 可以双向通信

TCP Socket编程

TCP(传输控制协议)是一种可靠的、面向连接的协议。它保证数据的完整性和顺序,适用于对数据准确性要求较高的应用。

TCP服务器端编程

TCP服务器示例:
import java.io.*;
import java.net.*;

public class TCPServer {
    public static void main(String[] args) {
        try {
            // 创建服务器Socket,监听8080端口
            ServerSocket serverSocket = new ServerSocket(8080);
            System.out.println("服务器启动,监听端口8080...");
            
            while (true) {
                // 等待客户端连接
                Socket clientSocket = serverSocket.accept();
                System.out.println("客户端连接:" + clientSocket.getInetAddress());
                
                // 创建新线程处理客户端请求
                new Thread(new ClientHandler(clientSocket)).start();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

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("收到消息:" + inputLine);
                out.println("服务器回复:" + inputLine);
            }
            
            clientSocket.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

TCP客户端编程

TCP客户端示例:
import java.io.*;
import java.net.*;
import java.util.Scanner;

public class TCPClient {
    public static void main(String[] args) {
        try {
            // 连接到服务器
            Socket socket = new Socket("localhost", 8080);
            System.out.println("连接到服务器成功!");
            
            // 获取输入输出流
            PrintWriter out = new PrintWriter(
                socket.getOutputStream(), true);
            BufferedReader in = new BufferedReader(
                new InputStreamReader(socket.getInputStream()));
            
            Scanner scanner = new Scanner(System.in);
            String userInput;
            
            while (true) {
                System.out.print("请输入消息(输入'quit'退出):");
                userInput = scanner.nextLine();
                
                if ("quit".equalsIgnoreCase(userInput)) {
                    break;
                }
                
                // 发送消息到服务器
                out.println(userInput);
                
                // 接收服务器回复
                String response = in.readLine();
                System.out.println(response);
            }
            
            socket.close();
            scanner.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

UDP Socket编程

UDP(用户数据报协议)是一种无连接的协议,传输速度快但不保证数据的可靠性。适用于对实时性要求较高的应用,如视频流、游戏等。

UDP服务器端编程

UDP服务器示例:
import java.net.*;
import java.io.*;

public class UDPServer {
    public static void main(String[] args) {
        try {
            // 创建DatagramSocket,监听9999端口
            DatagramSocket socket = new DatagramSocket(9999);
            System.out.println("UDP服务器启动,监听端口9999...");
            
            byte[] buffer = new byte[1024];
            
            while (true) {
                // 创建数据包接收数据
                DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
                socket.receive(packet);
                
                // 获取客户端信息
                String message = new String(packet.getData(), 0, packet.getLength());
                InetAddress clientAddress = packet.getAddress();
                int clientPort = packet.getPort();
                
                System.out.println("收到来自 " + clientAddress + ":" + clientPort + " 的消息:" + message);
                
                // 回复客户端
                String response = "服务器收到:" + message;
                byte[] responseData = response.getBytes();
                DatagramPacket responsePacket = new DatagramPacket(
                    responseData, responseData.length, clientAddress, clientPort);
                socket.send(responsePacket);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

UDP客户端编程

UDP客户端示例:
import java.net.*;
import java.io.*;
import java.util.Scanner;

public class UDPClient {
    public static void main(String[] args) {
        try {
            DatagramSocket socket = new DatagramSocket();
            InetAddress serverAddress = InetAddress.getByName("localhost");
            int serverPort = 9999;
            
            Scanner scanner = new Scanner(System.in);
            
            while (true) {
                System.out.print("请输入消息(输入'quit'退出):");
                String message = scanner.nextLine();
                
                if ("quit".equalsIgnoreCase(message)) {
                    break;
                }
                
                // 发送数据到服务器
                byte[] data = message.getBytes();
                DatagramPacket packet = new DatagramPacket(
                    data, data.length, serverAddress, serverPort);
                socket.send(packet);
                
                // 接收服务器回复
                byte[] buffer = new byte[1024];
                DatagramPacket responsePacket = new DatagramPacket(buffer, buffer.length);
                socket.receive(responsePacket);
                
                String response = new String(responsePacket.getData(), 0, responsePacket.getLength());
                System.out.println(response);
            }
            
            socket.close();
            scanner.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

TCP vs UDP 协议比较

特性 TCP UDP
连接性 面向连接 无连接
可靠性 可靠传输,保证数据完整性 不可靠传输,可能丢失数据
速度 相对较慢 快速
数据顺序 保证数据顺序 不保证数据顺序
头部开销 20字节 8字节
适用场景 文件传输、网页浏览、邮件 视频流、游戏、DNS查询

高级网络编程技术

多线程服务器

线程池服务器示例:
import java.io.*;
import java.net.*;
import java.util.concurrent.*;

public class ThreadPoolServer {
    private static final int PORT = 8080;
    private static final int THREAD_POOL_SIZE = 10;
    
    public static void main(String[] args) {
        // 创建线程池
        ExecutorService executor = Executors.newFixedThreadPool(THREAD_POOL_SIZE);
        
        try {
            ServerSocket serverSocket = new ServerSocket(PORT);
            System.out.println("线程池服务器启动,监听端口" + PORT);
            
            while (true) {
                Socket clientSocket = serverSocket.accept();
                // 将客户端处理任务提交给线程池
                executor.submit(new ClientTask(clientSocket));
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            executor.shutdown();
        }
    }
}

class ClientTask implements Runnable {
    private Socket clientSocket;
    
    public ClientTask(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) {
                // 处理客户端请求
                String response = processRequest(inputLine);
                out.println(response);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                clientSocket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    
    private String processRequest(String request) {
        // 模拟业务处理
        return "处理结果:" + request.toUpperCase();
    }
}

异常处理和资源管理

安全的网络编程示例:
public class SafeNetworkClient {
    public static void main(String[] args) {
        // 使用try-with-resources确保资源正确关闭
        try (Socket socket = new Socket("localhost", 8080);
             PrintWriter out = new PrintWriter(socket.getOutputStream(), true);
             BufferedReader in = new BufferedReader(
                 new InputStreamReader(socket.getInputStream()))) {
            
            // 设置超时时间
            socket.setSoTimeout(5000); // 5秒超时
            
            out.println("Hello Server");
            String response = in.readLine();
            System.out.println("服务器回复:" + response);
            
        } catch (SocketTimeoutException e) {
            System.err.println("连接超时:" + e.getMessage());
        } catch (ConnectException e) {
            System.err.println("连接被拒绝:" + e.getMessage());
        } catch (IOException e) {
            System.err.println("网络错误:" + e.getMessage());
        }
    }
}

网络编程最佳实践

推荐做法

  • 使用try-with-resources管理资源
  • 设置合适的超时时间
  • 使用线程池处理并发连接
  • 正确处理网络异常
  • 使用缓冲流提高性能
  • 及时关闭网络连接

避免做法

  • 忘记关闭Socket连接
  • 不处理网络异常
  • 在主线程中进行阻塞操作
  • 不设置超时时间
  • 忽略线程安全问题
  • 不验证网络数据

实际应用示例

简单聊天室应用

结合TCP Socket和多线程技术,可以实现一个简单的聊天室应用,支持多用户同时在线聊天。

安全注意事项

在实际应用中,需要考虑数据加密、身份验证、防止DOS攻击等安全问题。

性能优化建议

使用NIO(非阻塞I/O)可以进一步提高网络应用的性能和并发处理能力。

💻 查看完整代码 - 在线IDE体验

章节总结