O网络编程.java

前言:在当今数字化时代,网络编程已成为软件开发者必备的一项技能。
目录
一、网络编程基础概念
在开始我们的网络编程之旅前,先了解一下网络编程的基础概念。网络通信主要基于 TCP/IP 协议,它包含多个层次的协议:
-
传输层协议 :TCP(面向连接、可靠传输)和 UDP(无连接、不可靠传输但低延迟)是最关键的两个协议。TCP 好比快递专车,先规划好路线,每送一个包裹都会确认对方收到,保证数据安全准确到达;UDP 则像群发短信,只管发送,不确认对方是否收到,虽然可靠性不高,但速度快,适合对实时性要求高的场景,如直播、在线游戏。
-
应用层协议 :HTTP、HTTPS 等是我们日常开发中常用的协议。HTTP 是浏览器和服务器之间进行数据传输的语言,HTTPS 是加密版的 HTTP,更安全。
在网络通信中,有两个主要角色:服务端和客户端。服务端就像一个商店,负责提供服务,放在那里等待顾客上门;客户端就是顾客,主动联系服务端寻求服务,比如我们用手机打开网页,手机就是客户端,联系服务器请求网页内容。
二、简单的 TCP 客户端 - 服务端通信示例
服务端代码
import java.io.*;
import java.net.*;
public class SimpleTCPServer {
public static void main(String[] args) {
try {
// 创建服务端套接字,监听 12345 端口,这相当于服务端商店拉了一面大旗,上面写着 “我在这儿等你,端口是 12345”
ServerSocket serverSocket = new ServerSocket(12345);
System.out.println("服务端已启动,等待客户端连接...");
// 阻塞等待客户端连接,服务端就一直在这儿等着,直到有客户端来敲门
Socket socket = serverSocket.accept();
System.out.println("客户端已连接");
// 获取输入流,读取客户端消息,服务端伸出手接收客户端传来的消息
BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
String clientMsg = in.readLine();
System.out.println("收到客户端消息:" + clientMsg);
// 获取输出流,向客户端发送消息,服务端也给客户端回复消息
PrintWriter out = new PrintWriter(socket.getOutputStream(), true);
out.println("服务端已收到消息,欢迎连接!");
// 关闭资源,用完的东西要收拾好,不然会占用空间
in.close();
out.close();
socket.close();
serverSocket.close();
} catch (IOException e) {
// 如果过程中有啥问题,就直接打印错误信息,方便我们检查问题出在哪
e.printStackTrace();
}
}
}
客户端代码
import java.io.*;
import java.net.*;
public class SimpleTCPClient {
public static void main(String[] args) {
try {
// 创建客户端套接字,连接到本地主机的 12345 端口,客户端就是要找服务端要东西的人,根据服务端的地址和端口号去找服务端
Socket socket = new Socket("localhost", 12345);
// 获取输出流,向服务端发送消息,客户端说 “服务端啊,我有个消息要给你”
PrintWriter out = new PrintWriter(socket.getOutputStream(), true);
out.println("你好,服务端!");
// 获取输入流,读取服务端响应,客户端静静等待服务端回话
BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
String serverMsg = in.readLine();
System.out.println("收到服务端消息:" + serverMsg);
// 关闭资源,用完后要把资源释放掉
in.close();
out.close();
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
在这个简单示例中,我们成功实现了基于 TCP 协议的服务端和客户端通信。服务端启动后耐心聆听,客户端主动联系,双方通过输入输出流就像写信一样进行文字交流。
三、简单的 UDP 通信示例
UDP 是一种无连接的通信方式,适用于对实时性要求较高的场景。
服务端代码
import java.net.*;
public class SimpleUDPServer {
public static void main(String[] args) {
try {
// 创建服务端套接字,绑定到 12345 端口,服务端就在这儿等着接收消息
DatagramSocket serverSocket = new DatagramSocket(12345);
System.out.println("UDP 服务端已启动,等待接收数据...");
// 创建缓冲区,用于接收数据,这就像服务端准备了一个大袋子,用来装收到的消息
byte[] buffer = new byte[1024];
DatagramPacket receivePacket = new DatagramPacket(buffer, buffer.length);
// 接收数据,服务端就等着消息飞过来,装到袋子里
serverSocket.receive(receivePacket);
String clientMsg = new String(buffer, 0, receivePacket.getLength());
System.out.println("收到客户端消息:" + clientMsg);
// 获取客户端地址和端口,用于回传数据,服务端记下是谁发来的消息,从哪发来的,好给人家回信
InetAddress clientAddress = receivePacket.getAddress();
int clientPort = receivePacket.getPort();
// 回传数据给客户端,服务端也把消息发回去
String responseMsg = "UDP 服务端已收到消息";
byte[] responseBuffer = responseMsg.getBytes();
DatagramPacket sendPacket = new DatagramPacket(responseBuffer, responseBuffer.length, clientAddress, clientPort);
serverSocket.send(sendPacket);
// 关闭套接字,用完之后要关闭,不然会一直占着这个端口,别人就没法用了
serverSocket.close();
} catch (SocketException e) {
System.out.println("Socket 创建失败");
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
客户端代码
import java.net.*;
public class SimpleUDPClient {
public static void main(String[] args) {
try {
// 创建客户端套接字,客户端准备发消息啦
DatagramSocket clientSocket = new DatagramSocket();
// 准备要发送的数据,客户端把想说的话写好
String sendMsg = "你好,UDP 服务端!";
byte[] sendBuffer = sendMsg.getBytes();
// 创建数据报包,包含要发送的数据、目标地址和端口,把消息装进信封,写上地址和门牌号(端口号)
InetAddress serverAddress = InetAddress.getLocalHost();
DatagramPacket sendPacket = new DatagramPacket(sendBuffer, sendBuffer.length, serverAddress, 12345);
// 发送数据,把信封扔进邮箱,等着邮递员(网络)去送
clientSocket.send(sendPacket);
// 准备接收服务端返回的数据,客户端也准备一个袋子,等着服务端回的消息
byte[] receiveBuffer = new byte[1024];
DatagramPacket receivePacket = new DatagramPacket(receiveBuffer, receiveBuffer.length);
// 接收数据,看看服务端回了啥
clientSocket.receive(receivePacket);
String responseMsg = new String(receiveBuffer, 0, receivePacket.getLength());
System.out.println("收到服务端消息:" + responseMsg);
// 关闭套接字,用完之后关闭,不然资源会被占着
clientSocket.close();
} catch (SocketException e) {
System.out.println("Socket 创建失败");
e.printStackTrace();
} catch (UnknownHostException e) {
System.out.println("未知主机");
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
UDP 通信就像朋友之间互相扔纸飞机,不需要提前建立稳定路线,直接把消息写在飞机上,扔过去就完事儿了,速度快,但偶尔会丢消息。
四、多线程 TCP 服务端
在实际应用中,服务端通常需要同时处理多个客户端连接。通过多线程技术,可以实现这一功能。
import java.io.*;
import java.net.*;
public class MultiThreadTCPServer {
public static void main(String[] args) {
try {
// 创建服务端套接字,监听 12345 端口,服务端开张营业
ServerSocket serverSocket = new ServerSocket(12345);
System.out.println("多线程服务端已启动,等待客户端连接...");
while (true) {
// 阻塞等待客户端连接,服务端就一直在这儿等着,来一个客户接待一个
Socket socket = serverSocket.accept();
System.out.println("一个客户端已连接");
// 为每个客户端创建一个线程,服务端给每个客户安排一个服务员(线程),专门负责这个客户
ClientHandler clientHandler = new ClientHandler(socket);
new Thread(clientHandler).start();
}
} catch (IOException e) {
e.printStackTrace();
}
}
// 客户端处理类
static class ClientHandler implements Runnable {
private Socket socket;
public ClientHandler(Socket socket) {
this.socket = socket;
}
@Override
public void run() {
try {
// 获取输入流,读取客户端消息,服务员仔细听客户说的话
BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
String clientMsg = in.readLine();
System.out.println("收到客户端消息:" + clientMsg);
// 获取输出流,向客户端发送消息,服务员给客户回复消息
PrintWriter out = new PrintWriter(socket.getOutputStream(), true);
out.println("服务端已收到消息,欢迎连接!");
// 关闭资源,招待完客户之后,要收拾好桌子(释放资源)
in.close();
out.close();
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
这个多线程服务端就像一个餐厅,可以同时服务多位客户,每个客户都有一个专属服务员(线程),保障服务不断。
五、Java NIO 非阻塞 IO
Java NIO(New IO)从 Java 1.4 版本开始引入,提供了非阻塞 IO 和多路复用的功能,极大地提高了网络编程的效率和可扩展性。
简单的非阻塞 TCP 服务端示例
import java.io.*;
import java.net.*;
import java.nio.*;
import java.nio.channels.*;
import java.util.*;
public class NonBlockingTCPServer {
public static void main(String[] args) {
try {
// 创建选择器,这就像一个超级管家,能同时盯着多个门口,看有没有客户来
Selector selector = Selector.open();
// 创建服务端通道,绑定到 12345 端口,服务端开了一扇门,门牌号是 12345
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
serverSocketChannel.bind(new InetSocketAddress(12345));
serverSocketChannel.configureBlocking(false);
// 将服务端通道注册到选择器,监听接受事件,告诉管家,这扇门要是有人敲,就提醒我
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
System.out.println("非阻塞服务端已启动,等待客户端连接...");
while (true) {
// 选择一组键集,管家开始巡逻,查看有哪些事儿需要处理
selector.select();
// 获取选择键集合的迭代器,这就像管家拿着一摞需要处理的事务清单
Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
while (iterator.hasNext()) {
SelectionKey key = iterator.next();
// 判断是否是可接受事件,看看是不是新客户敲门了
if (key.isAcceptable()) {
// 接受新的客户端连接,开门迎接新客户
ServerSocketChannel server = (ServerSocketChannel) key.channel();
SocketChannel socketChannel = server.accept();
socketChannel.configureBlocking(false);
// 注册到选择器,监听读事件,把客户安排好,告诉管家,要是这个客户有消息发过来,你也提醒我
socketChannel.register(selector, SelectionKey.OP_READ);
System.out.println("一个客户端已连接");
}
// 判断是否是可读事件,看看是不是客户发消息过来了
else if (key.isReadable()) {
// 读取数据,查看客户发了啥消息
SocketChannel socketChannel = (SocketChannel) key.channel();
ByteBuffer buffer = ByteBuffer.allocate(1024);
int bytesRead = socketChannel.read(buffer);
if (bytesRead > 0) {
buffer.flip();
byte[] data = new byte[buffer.limit()];
buffer.get(data);
String clientMsg = new String(data).trim();
System.out.println("收到客户端消息:" + clientMsg);
// 发送响应,回复客户消息
String response = "服务端已收到消息";
ByteBuffer outBuffer = ByteBuffer.wrap(response.getBytes());
socketChannel.write(outBuffer);
}
// 如果读取到 -1,表示连接已关闭,客户走了,把相关东西收拾一下
else if (bytesRead == -1) {
socketChannel.close();
}
}
// 移除已处理的键,把处理完的事务从清单上划掉
iterator.remove();
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
总结
Java 网络编程就像在数字世界里搭建通信桥梁,从基础的 TCP/UDP 通信到多线程服务端,再到 Java NIO 的非阻塞 IO,为开发者提供了丰富的工具和方法。通过本文的示例和讲解,希望能帮助你更好地理解和掌握 Java 网络编程,这样你就能在网络世界的海洋里自由航行啦。
更多推荐
所有评论(0)