13.1 网络的相关概念

13.2 InetAddress

InetAddressJavaIP地址的封装,几乎所有的Java网络相关的类都和它有关系。

它有两个子类,分别为Inet4AddressInet6Address

13.2.1 相关方法

  1. - getLocalHost(): 获取本地主机的主机名和ip地址
  2. - getByName(String): 根据主机名/域名获取InetAddress对象
  3. - getHostName(): 获取InetAddress对象的主机名/域名,到底获取哪个取决于你创建时用谁创建的
  4. - getHostAddress(): 获取InetAddressip地址
  5. public class api_ {
  6. public static void main(String[] args) throws UnknownHostException {
  7. InetAddress localHost = InetAddress.getLocalHost();
  8. System.out.println(localHost); // macbook_pro_16/127.0.0.1
  9. InetAddress host2 = InetAddress.getByName("macbook_pro_16");
  10. System.out.println(host2); // macbook_pro_16/127.0.0.1
  11. InetAddress host3 = InetAddress.getByName("www.baidu.com");
  12. System.out.println(host3); // www.baidu.com/110.242.68.3
  13. String host3Name = host3.getHostName();
  14. System.out.println(host3Name); // www.baidu.com
  15. String host3Address = host3.getHostAddress();
  16. System.out.println(host3Address); // 110.242.68.3
  17. }
  18. }

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”,然后退出;服务端接收到客户端的消息输出到控制台后退出。

  1. // server
  2. public class SocketTCP01Server {
  3. @SuppressWarnings("all")
  4. public static void main(String[] args) throws IOException {
  5. ServerSocket serverSocket = new ServerSocket(9999);
  6. System.out.println("服务端在9999端口监听,等待连接...");
  7. Socket socket = serverSocket.accept();
  8. System.out.println("服务端socket=" + socket.getClass());
  9. InputStream inputStream = socket.getInputStream();
  10. byte[] buf = new byte[1024];
  11. int readLen = 0;
  12. while ((readLen = inputStream.read(buf)) != -1) {
  13. System.out.println(new String(buf, 0, readLen));
  14. }
  15. inputStream.close();
  16. socket.close();
  17. serverSocket.close();
  18. }
  19. }
  20. 代码分析: serverSocket对象用于监听某个端口,当调用accept后,它就会一直等待连接,直到有请求过来并连接成功后,才会返回一个socket对象。
  1. // client
  2. public class SocketTCP01Client {
  3. public static void main(String[] args) throws IOException {
  4. // 1. 获取服务器的socket
  5. Socket socket = new Socket(InetAddress.getLocalHost(), 9999);
  6. System.out.println("客户端socket返回=" + socket.getClass());
  7. // 2. 和服务端连接成功后,生成Socket对象,通过socket.getOutputStream()得到与该socket相关的输出流对象
  8. OutputStream outputStream = socket.getOutputStream();
  9. // 3. 通过这个输出流写入数据到通道中
  10. outputStream.write("hello, server".getBytes());
  11. // 4. 关闭流和对象
  12. outputStream.close();
  13. socket.close();
  14. System.out.println("客户端退出");
  15. }
  16. }
  17. 代码分析: 6new Socket时,底层代码就在请求与服务端的连接了,连接成功后,才会返回一个socket对象。

13.3.2.2 案例二(使用字节流)

编写一个客户端和一个服务端,服务端在9999端口监听,客户端连接到服务端,发送”hello, server”,并接受服务端返回的”hello, client”后退出,服务端接收到客户端发送的信息,输出”hello, client”后退出。

  1. // server
  2. public class SocketTCP02Server {
  3. @SuppressWarnings("all")
  4. public static void main(String[] args) throws IOException {
  5. ServerSocket serverSocket = new ServerSocket(9999);
  6. System.out.println("服务端在9999端口监听,等待连接...");
  7. Socket socket = serverSocket.accept();
  8. System.out.println("服务端socket=" + socket.getClass());
  9. InputStream inputStream = socket.getInputStream();
  10. byte[] buf = new byte[1024];
  11. int readLen = 0;
  12. while ((readLen = inputStream.read(buf)) != -1) {
  13. System.out.println(new String(buf, 0, readLen));
  14. }
  15. OutputStream outputStream = socket.getOutputStream();
  16. outputStream.write("hello, client".getBytes());
  17. socket.shutdownOutput();
  18. outputStream.close();
  19. inputStream.close();
  20. socket.close();
  21. serverSocket.close();
  22. }
  23. }
  1. // client
  2. public class SocketTCP02Client {
  3. public static void main(String[] args) throws IOException {
  4. Socket socket = new Socket(InetAddress.getLocalHost(), 9999);
  5. System.out.println("客户端socket返回=" + socket.getClass());
  6. OutputStream outputStream = socket.getOutputStream();
  7. outputStream.write("hello, server".getBytes());
  8. socket.shutdownOutput();
  9. InputStream inputStream = socket.getInputStream();
  10. byte[] buf = new byte[1024];
  11. int readLen = 0;
  12. while ((readLen = inputStream.read(buf)) != -1) {
  13. System.out.println(new String(buf, 0, readLen));
  14. }
  15. inputStream.close();
  16. outputStream.close();
  17. socket.close();
  18. System.out.println("客户端退出");
  19. }
  20. }

13.4 UDP网络编程

13.4.1 基本介绍

  1. DatagramSocketDatagramPacket实现了基于UDP协议网络程序
  2. UDP数据报通过数据报套接字DatagramSocket进行传输,系统不保证UDP数据报是否能到达以及何时到达
  3. DatagramPacket封装了UDP数据报,在数据报中包含了发送端和接收端的ip地址以及端口号,因此无需提前建立连接

13.4.2 案例

  1. 编写一个接收端A和一个发送端B
  2. 接收端在9999端口等待接收数据
  3. 发送端B向接收端A发送消息 “hello,明天吃火锅”
  4. 接收端A收到B的消息后回复 “好的,明天见”
  5. 发送端接收回复的数据再退出
  1. // receiver A
  2. public class UDPReceiverA {
  3. public static void main(String[] args) throws IOException {
  4. // 1. 创建一个DatagramSocket对象,准备在9999接收数据
  5. DatagramSocket datagramSocket = new DatagramSocket(9999);
  6. // 2. 构建一个DatagramPacket对象,准备接收数据
  7. byte[] buf = new byte[1024];
  8. DatagramPacket packet = new DatagramPacket(buf, buf.length);
  9. System.out.println("接收端A等待接收数据...");
  10. datagramSocket.receive(packet); // 阻塞方法
  11. // 可以把packet进行拆包,取出数据
  12. int length = packet.getLength();
  13. byte[] data = packet.getData();
  14. String s = new String(data, 0, length);
  15. System.out.println(s);
  16. // 回复信息给B
  17. data = "好的,明天见".getBytes();
  18. packet = new DatagramPacket(data, data.length, InetAddress.getLocalHost(), 9998);
  19. datagramSocket.send(packet);
  20. // 关闭资源
  21. datagramSocket.close();
  22. System.out.println("接收端退出");
  23. }
  24. }
  1. // sender B
  2. public class UDPSenderB {
  3. public static void main(String[] args) throws IOException {
  4. DatagramSocket socket = new DatagramSocket(9998);
  5. // 发送信息
  6. byte[] data = "hello, 明天吃火锅!".getBytes();
  7. DatagramPacket packet = new DatagramPacket(data, data.length, InetAddress.getLocalHost(),
  8. 9999);
  9. socket.send(packet);
  10. //接收A回复的消息
  11. byte[] buf = new byte[1024];
  12. packet = new DatagramPacket(buf, buf.length);
  13. socket.receive(packet);
  14. // 拆包
  15. int length = packet.getLength();
  16. data = packet.getData();
  17. String s = new String(data, 0, length);
  18. System.out.println(s);
  19. //关闭资源
  20. socket.close();
  21. System.out.println("发送方退出");
  22. }

有个疑问:当传输的数据为中文时,它在进行拆包是将packet拆成了字节数组的形式,然后构建出一个String。那么为什么可以支持