InetAddress
Java中表示IP地址的类,常用方法:
InetAddress ip = InetAddress.getByName("www.example.com");
System.out.println(ip.getHostName()); // 主机名
System.out.println(ip.getHostAddress()); // IP地址UDP通信
无连接协议,速度快但不可靠
一发一收
发送端:
DatagramSocket socket = new DatagramSocket();
byte[] data = "Hello".getBytes();
DatagramPacket packet = new DatagramPacket(
data, data.length, InetAddress.getLocalHost(), 6666);
socket.send(packet);接收端:
DatagramSocket socket = new DatagramSocket(6666);
byte[] buffer = new byte[1024];
DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
socket.receive(packet);
System.out.println(new String(packet.getData(), 0, packet.getLength()));多发多收
只需在循环中重复发送/接收逻辑即可
TCP通信
面向连接的可靠协议
一发一收
客户端:
Socket socket = new Socket("127.0.0.1", 8888);
OutputStream os = socket.getOutputStream();
os.write("Hello".getBytes());服务端:
ServerSocket server = new ServerSocket(8888);
Socket socket = server.accept();
InputStream is = socket.getInputStream();
byte[] buffer = new byte[1024];
int len = is.read(buffer);
System.out.println(new String(buffer, 0, len));多发多收
使用循环处理多次通信,使用flush()刷新管道
支持多个客户端消息
服务端主线程只负责接收连接,每个客户端交给新线程处理
- 单独的
ClientHandler线程类处理每个客户端连接 - 服务端主循环持续接收新连接
- 每个连接都有独立的线程处理通信
// 客户端处理线程
class ClientHandler implements Runnable {
private Socket socket;
public ClientHandler(Socket socket) {
this.socket = socket;
}
@Override
public void run() {
try (InputStream is = socket.getInputStream()) {
byte[] buffer = new byte[1024];
int len;
while ((len = is.read(buffer)) != -1) {
System.out.println(Thread.currentThread().getName()
+ "收到消息:" + new String(buffer, 0, len));
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
// 服务端主程序
ServerSocket server = new ServerSocket(8888);
while (true) {
Socket socket = server.accept(); // 接收新连接
new Thread(new ClientHandler(socket)).start(); // 为每个客户端创建新线程
}使用线程池优化
public class Server {
public static void main(String[] args) throws Exception {
// 目标:实现TCP通信下多发多收:服务端开发。支持多个客户端开发。
System.out.println("服务端启动了...");
// 1. 创建一个服务器ServerSocket,指定端口
ServerSocket ss = new ServerSocket(8888);
// 创建线程池
ExecutorService es = new ThreadPoolExecutor(5, 10, 1000, TimeUnit.MILLISECONDS, new ArrayBlockingQueue<Runnable>(5), new ThreadPoolExecutor.DiscardPolicy());
while (true) {
// 2. 调用accept()监听,等待客户端连接
Socket s = ss.accept();
System.out.println("一个客户端连接了:" + s.getInetAddress().getHostAddress());
// 3. 使用线程池优化
es.execute(new ServerReader(s));
}
}
}public class ServerReader implements Runnable {
private Socket socket;
public ServerReader(Socket socket) {
this.socket = socket;
}
@Override
public void run() {
try {
while (true) {
// 给当前对应的浏览器管道响应一个网页数据回去。
OutputStream os = socket.getOutputStream();
// 通过字节输出流包装写出去数据给浏览器
// 包装为打印流
PrintWriter pw = new PrintWriter(os);
pw.write("HTTP/1.1 200 OK\r\n");
pw.write("Content-Type: text/html\r\n");
pw.write("\r\n");
pw.write("<html><body><h1>Hello World</h1></body></html>");
pw.close();
socket.close();
}
} catch (Exception e) {
System.out.println("客户端下线了:"+socket.getInetAddress().getHostAddress());
}
}
}