13.1 网络的相关概念
13.2 InetAddress类
InetAddress是Java对IP地址的封装,几乎所有的Java网络相关的类都和它有关系。
它有两个子类,分别为Inet4Address和Inet6Address。
13.2.1 相关方法
- getLocalHost(): 获取本地主机的主机名和ip地址- getByName(String): 根据主机名/域名获取InetAddress对象- getHostName(): 获取InetAddress对象的主机名/域名,到底获取哪个取决于你创建时用谁创建的- getHostAddress(): 获取InetAddress的ip地址public class api_ {public static void main(String[] args) throws UnknownHostException {InetAddress localHost = InetAddress.getLocalHost();System.out.println(localHost); // macbook_pro_16/127.0.0.1InetAddress host2 = InetAddress.getByName("macbook_pro_16");System.out.println(host2); // macbook_pro_16/127.0.0.1InetAddress host3 = InetAddress.getByName("www.baidu.com");System.out.println(host3); // www.baidu.com/110.242.68.3String host3Name = host3.getHostName();System.out.println(host3Name); // www.baidu.comString host3Address = host3.getHostAddress();System.out.println(host3Address); // 110.242.68.3}}
13.3 Socket
socket即ip地址+端口号,是两台主机进行网络通信的端点。网络通信的本质就是socket之间的通信。
socket允许程序把网络连接当成一个流,数据在两个端点间通过IO进行传输。
13.3 TCP网络编程
13.3.1 基本介绍
使用Socket进行网络编程时,本质上就是两个进程之间的网络通信。其中一个进程必须充当服务器端,它会主动监听某个指定的端口,另一个进程必须充当客户端,它必须主动连接服务器的IP地址和指定端口,如果连接成功,服务器端和客户端就成功地建立了一个TCP连接,双方后续就可以随时发送和接收数据。
因此,当Socket连接成功地在服务器端和客户端之间建立后:
- 对服务器端来说,它的
Socket是指定的IP地址和指定的端口号; - 对客户端来说,它的
Socket是它所在计算机的IP地址和一个由操作系统分配的随机端口号。
13.3.2 案例
13.3.2.1 案例一(使用字节流)
本案例编写一个客户端和服务端,服务端在9999端口号进行监听,客户端发起连接到服务端,发送”hello,server”,然后退出;服务端接收到客户端的消息输出到控制台后退出。
// serverpublic class SocketTCP01Server {@SuppressWarnings("all")public static void main(String[] args) throws IOException {ServerSocket serverSocket = new ServerSocket(9999);System.out.println("服务端在9999端口监听,等待连接...");Socket socket = serverSocket.accept();System.out.println("服务端socket=" + socket.getClass());InputStream inputStream = socket.getInputStream();byte[] buf = new byte[1024];int readLen = 0;while ((readLen = inputStream.read(buf)) != -1) {System.out.println(new String(buf, 0, readLen));}inputStream.close();socket.close();serverSocket.close();}}代码分析: serverSocket对象用于监听某个端口,当调用accept后,它就会一直等待连接,直到有请求过来并连接成功后,才会返回一个socket对象。
// clientpublic class SocketTCP01Client {public static void main(String[] args) throws IOException {// 1. 获取服务器的socketSocket socket = new Socket(InetAddress.getLocalHost(), 9999);System.out.println("客户端socket返回=" + socket.getClass());// 2. 和服务端连接成功后,生成Socket对象,通过socket.getOutputStream()得到与该socket相关的输出流对象OutputStream outputStream = socket.getOutputStream();// 3. 通过这个输出流写入数据到通道中outputStream.write("hello, server".getBytes());// 4. 关闭流和对象outputStream.close();socket.close();System.out.println("客户端退出");}}代码分析: 第6行new Socket时,底层代码就在请求与服务端的连接了,连接成功后,才会返回一个socket对象。
13.3.2.2 案例二(使用字节流)
编写一个客户端和一个服务端,服务端在9999端口监听,客户端连接到服务端,发送”hello, server”,并接受服务端返回的”hello, client”后退出,服务端接收到客户端发送的信息,输出”hello, client”后退出。
// serverpublic class SocketTCP02Server {@SuppressWarnings("all")public static void main(String[] args) throws IOException {ServerSocket serverSocket = new ServerSocket(9999);System.out.println("服务端在9999端口监听,等待连接...");Socket socket = serverSocket.accept();System.out.println("服务端socket=" + socket.getClass());InputStream inputStream = socket.getInputStream();byte[] buf = new byte[1024];int readLen = 0;while ((readLen = inputStream.read(buf)) != -1) {System.out.println(new String(buf, 0, readLen));}OutputStream outputStream = socket.getOutputStream();outputStream.write("hello, client".getBytes());socket.shutdownOutput();outputStream.close();inputStream.close();socket.close();serverSocket.close();}}
// clientpublic class SocketTCP02Client {public static void main(String[] args) throws IOException {Socket socket = new Socket(InetAddress.getLocalHost(), 9999);System.out.println("客户端socket返回=" + socket.getClass());OutputStream outputStream = socket.getOutputStream();outputStream.write("hello, server".getBytes());socket.shutdownOutput();InputStream inputStream = socket.getInputStream();byte[] buf = new byte[1024];int readLen = 0;while ((readLen = inputStream.read(buf)) != -1) {System.out.println(new String(buf, 0, readLen));}inputStream.close();outputStream.close();socket.close();System.out.println("客户端退出");}}
13.4 UDP网络编程
13.4.1 基本介绍
DatagramSocket和DatagramPacket实现了基于UDP协议网络程序UDP数据报通过数据报套接字DatagramSocket进行传输,系统不保证UDP数据报是否能到达以及何时到达DatagramPacket封装了UDP数据报,在数据报中包含了发送端和接收端的ip地址以及端口号,因此无需提前建立连接
13.4.2 案例
- 编写一个接收端A和一个发送端B
- 接收端在9999端口等待接收数据
- 发送端B向接收端A发送消息 “hello,明天吃火锅”
- 接收端A收到B的消息后回复 “好的,明天见”
- 发送端接收回复的数据再退出
// receiver Apublic class UDPReceiverA {public static void main(String[] args) throws IOException {// 1. 创建一个DatagramSocket对象,准备在9999接收数据DatagramSocket datagramSocket = new DatagramSocket(9999);// 2. 构建一个DatagramPacket对象,准备接收数据byte[] buf = new byte[1024];DatagramPacket packet = new DatagramPacket(buf, buf.length);System.out.println("接收端A等待接收数据...");datagramSocket.receive(packet); // 阻塞方法// 可以把packet进行拆包,取出数据int length = packet.getLength();byte[] data = packet.getData();String s = new String(data, 0, length);System.out.println(s);// 回复信息给Bdata = "好的,明天见".getBytes();packet = new DatagramPacket(data, data.length, InetAddress.getLocalHost(), 9998);datagramSocket.send(packet);// 关闭资源datagramSocket.close();System.out.println("接收端退出");}}
// sender Bpublic class UDPSenderB {public static void main(String[] args) throws IOException {DatagramSocket socket = new DatagramSocket(9998);// 发送信息byte[] data = "hello, 明天吃火锅!".getBytes();DatagramPacket packet = new DatagramPacket(data, data.length, InetAddress.getLocalHost(),9999);socket.send(packet);//接收A回复的消息byte[] buf = new byte[1024];packet = new DatagramPacket(buf, buf.length);socket.receive(packet);// 拆包int length = packet.getLength();data = packet.getData();String s = new String(data, 0, length);System.out.println(s);//关闭资源socket.close();System.out.println("发送方退出");}
有个疑问:当传输的数据为中文时,它在进行拆包是将packet拆成了字节数组的形式,然后构建出一个String。那么为什么可以支持
