一、Socket编程基础
Socket是网络通信的基石,提供了端到端的通信机制。想象它就像电话系统,两端通过Socket建立连接后就可以互相通信。
TCP Socket编程
java
// 服务器端
public class SimpleServer {
public static void main(String[] args) {
try (ServerSocket serverSocket = new ServerSocket(8080)) {
System.out.println("服务器启动,等待连接...");
while (true) {
Socket clientSocket = serverSocket.accept();
handleClient(clientSocket);
}
}
}
private static void handleClient(Socket socket) {
try (BufferedReader in = new BufferedReader(
new InputStreamReader(socket.getInputStream()));
PrintWriter out = new PrintWriter(
socket.getOutputStream(), true)) {
String line;
while ((line = in.readLine()) != null) {
System.out.println("收到消息: " + line);
out.println("服务器已收到: " + line);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
// 客户端
public class SimpleClient {
public static void main(String[] args) {
try (Socket socket = new Socket("localhost", 8080);
PrintWriter out = new PrintWriter(
socket.getOutputStream(), true);
BufferedReader in = new BufferedReader(
new InputStreamReader(socket.getInputStream()))) {
out.println("Hello, Server!");
System.out.println("服务器响应: " + in.readLine());
} catch (IOException e) {
e.printStackTrace();
}
}
}
UDP Socket编程
java
public class UDPExample {
public static void main(String[] args) throws IOException {
// UDP服务器
DatagramSocket serverSocket = new DatagramSocket(9000);
byte[] receiveData = new byte[1024];
while (true) {
DatagramPacket receivePacket =
new DatagramPacket(receiveData, receiveData.length);
serverSocket.receive(receivePacket);
String message = new String(
receivePacket.getData(), 0, receivePacket.getLength());
System.out.println("收到消息: " + message);
}
}
}
二、HTTP客户端实现
使用HttpURLConnection
java
public class HttpClientExample {
public String sendGet(String url) throws IOException {
HttpURLConnection connection = (HttpURLConnection)
new URL(url).openConnection();
connection.setRequestMethod("GET");
connection.setConnectTimeout(5000);
connection.setReadTimeout(5000);
try (BufferedReader reader = new BufferedReader(
new InputStreamReader(connection.getInputStream()))) {
StringBuilder response = new StringBuilder();
String line;
while ((line = reader.readLine()) != null) {
response.append(line);
}
return response.toString();
} finally {
connection.disconnect();
}
}
public String sendPost(String url, String body) throws IOException {
HttpURLConnection connection = (HttpURLConnection)
new URL(url).openConnection();
connection.setRequestMethod("POST");
connection.setDoOutput(true);
connection.setRequestProperty("Content-Type", "application/json");
try (OutputStream os = connection.getOutputStream()) {
os.write(body.getBytes());
}
try (BufferedReader reader = new BufferedReader(
new InputStreamReader(connection.getInputStream()))) {
StringBuilder response = new StringBuilder();
String line;
while ((line = reader.readLine()) != null) {
response.append(line);
}
return response.toString();
}
}
}
三、URL和URLConnection详解
java
public class URLExample {
public static void main(String[] args) throws Exception {
URL url = new URL("https://api.example.com/data?id=123");
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());
URLConnection connection = url.openConnection();
connection.setConnectTimeout(5000);
connection.setReadTimeout(5000);
// 设置请求属性
connection.setRequestProperty("User-Agent", "Mozilla/5.0");
// 获取响应头
for (Map.Entry<String, List<String>> header :
connection.getHeaderFields().entrySet()) {
System.out.println(header.getKey() + ": " + header.getValue());
}
}
}
四、网络IO模型
1. 阻塞IO模型
java
// 传统阻塞IO
ServerSocket serverSocket = new ServerSocket(8080);
Socket socket = serverSocket.accept(); // 阻塞直到连接建立
InputStream in = socket.getInputStream();
byte[] buffer = new byte[1024];
int read = in.read(buffer); // 阻塞直到有数据可读
2. 非阻塞IO模型
java
public class NonBlockingServer {
public static void main(String[] args) throws IOException {
Selector selector = Selector.open();
ServerSocketChannel serverChannel = ServerSocketChannel.open();
serverChannel.bind(new InetSocketAddress(8080));
serverChannel.configureBlocking(false);
serverChannel.register(selector, SelectionKey.OP_ACCEPT);
while (true) {
selector.select();
Set<SelectionKey> selectedKeys = selector.selectedKeys();
Iterator<SelectionKey> iter = selectedKeys.iterator();
while (iter.hasNext()) {
SelectionKey key = iter.next();
if (key.isAcceptable()) {
handleAccept(serverChannel, selector);
} else if (key.isReadable()) {
handleRead(key);
}
iter.remove();
}
}
}
}
五、实用案例:简单的HTTP服务器
java
public class SimpleHttpServer {
public static void main(String[] args) throws IOException {
ServerSocket server = new ServerSocket(8080);
System.out.println("HTTP服务器启动,监听端口8080...");
while (true) {
Socket clientSocket = server.accept();
new Thread(() -> handleRequest(clientSocket)).start();
}
}
private static void handleRequest(Socket socket) {
try (BufferedReader in = new BufferedReader(
new InputStreamReader(socket.getInputStream()));
PrintWriter out = new PrintWriter(socket.getOutputStream(), true)) {
// 读取HTTP请求
String requestLine = in.readLine();
System.out.println("收到请求: " + requestLine);
// 发送HTTP响应
out.println("HTTP/1.1 200 OK");
out.println("Content-Type: text/html; charset=utf-8");
out.println();
out.println("<html><body><h1>Hello, World!</h1></body></html>");
} catch (IOException e) {
e.printStackTrace();
}
}
}
最佳实践建议 💡
- 连接管理
- 使用连接池管理连接
- 设置合适的超时时间
- 正确关闭资源
- 错误处理
- 实现重试机制
- 记录详细的错误日志
- 性能优化
- 使用非阻塞IO处理高并发
- 实现数据压缩
- 使用缓冲区提高效率
常见陷阱提醒 ⚠️
- 资源泄露
java
// 错误:未关闭资源
Socket socket = new Socket("localhost", 8080);
socket.getOutputStream().write(data);
// 应该使用try-with-resources
// 正确做法
try (Socket socket = new Socket("localhost", 8080)) {
socket.getOutputStream().write(data);
}
- 超时处理
java
// 错误:未设置超时
URLConnection conn = url.openConnection();
InputStream in = conn.getInputStream(); // 可能永远阻塞
// 正确做法
URLConnection conn = url.openConnection();
conn.setConnectTimeout(5000);
conn.setReadTimeout(5000);
- 并发处理
java
// 错误:每个连接创建新线程
while (true) {
Socket socket = serverSocket.accept();
new Thread(() -> handleRequest(socket)).start();
}
// 正确:使用线程池
ExecutorService executor = Executors.newFixedThreadPool(10);
while (true) {
Socket socket = serverSocket.accept();
executor.submit(() -> handleRequest(socket));
}
网络编程是现代应用程序的重要组成部分。掌握这些基础知识对于构建可靠的网络应用至关重要。记住要注意资源管理、错误处理和性能优化。