黑马全套Java教程(九):网络编程(二)+https://developer.aliyun.com/article/1556497
37 网络编程
网络编程可以让程序与网络上的其他设备中的程序进行数据交互
常见的通信模式:B/S,C/S
37.1 网络通信的三要素
IP地址:设备在网络中的地址,是唯一的标识
端口:应用程序在设备中唯一的标识
协议:数据在网络中传输的规则,常见的协议有UDP协议和TCP协议
要素一:IP地址
package d1_inetAddress; import java.net.InetAddress; /* *目标:InetAddress类概述 * 一个该类的对象就代表一个IP地址对象 * * * InetAddress类的方法: * static InetAddress getLocalHost() * * 获取本地主机IP地址对象 * static InetAddress getByName(String host) * * 根据IP地址字符串或主机名获得对应的IP地址对象 * String getHostName() * * 获得主机名 * String getHostAddress() * * 获得IP地址字符串 */ public class InetAddressDemo01 { public static void main(String[] args) throws Exception{ //1. 获取本机地址对象 InetAddress ip1 = InetAddress.getLocalHost(); System.out.println(ip1.getHostName()); //主机名 System.out.println(ip1.getHostAddress()); //ip //2. 获取域名ip对象 InetAddress ip2 = InetAddress.getByName("www.baidu.com"); System.out.println(ip2.getHostName()); System.out.println(ip2.getHostAddress()); //3. 获取公网IP对象 InetAddress ip3 = InetAddress.getByName("110.242.68.4"); System.out.println(ip3.getHostName()); System.out.println(ip3.getHostAddress()); //4. 判断是否能通:ping 5s之前测试是否可通 System.out.println(ip3.isReachable(5000)); } }
LAPTOP-7NLRI4B2 192.168.111.1 www.baidu.com 110.242.68.3 110.242.68.4 110.242.68.4 true
要素二:端口号
要素三:协议
37.2 UDP通信
- UDP是一种无连接、不可靠传输的协议
- 将数据源IP、目的地IP和端口封装成数据包,不需要建立连接
- 每个数据包的大小限制在64KB内
- 发送不管对方是否准备好,接收方收到也不确认,故是不可靠的
- 可以广播发送,发送数据结束时无需释放资源,开销小,速度快
- 使用场景:语音通话,视频会话
例:UDP实现一发一收
ServerDemo2.java
package d2_udp1; import java.net.DatagramPacket; import java.net.DatagramSocket; //接收端 public class ServerDemo2 { public static void main(String[] args) throws Exception{ System.out.println("=====服务端启动====="); //1. 创建接收端对象,注册端口 DatagramSocket socket = new DatagramSocket(8888); //2. 创建一个数据包对象接收数据(韭菜盘子) byte[] buffer = new byte[1024 * 64]; DatagramPacket packet = new DatagramPacket(buffer, buffer.length); //3. 等待,接受数据 socket.receive(packet); //4. 取出数据即可 //读取多少倒出多少 int len = packet.getLength(); String rs = new String(buffer, 0, len); System.out.println("收到了:" + rs); //获取发送端的ip和端口 String ip = packet.getSocketAddress().toString(); System.out.println("对方地址:" + ip); int port = packet.getPort(); System.out.println("对方端口:" + port); socket.close(); } }
ClientDemo1.java
package d2_udp1; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.InetAddress; //发送端 一发一收 public class ClientDemo1 { public static void main(String[] args) throws Exception{ System.out.println("=====客户端启动====="); //1. 创建发送端对象 发送端自带默认的端口号 DatagramSocket socket = new DatagramSocket(6666); //可指定可不指定端口 //2. 创建一个数据报对象封装数据 /* * public DatagramPacket(byte buf[], int length, * InetAddress address, int port) * 参数一:封装要发送的数据 *参数二:发送的数据大小 * 参数三:服务端的主机IP地址 * 参数四:服务端的端口 * */ byte[] buffer = "我是一个快乐的韭菜".getBytes(); DatagramPacket packet = new DatagramPacket(buffer, buffer.length, InetAddress.getLocalHost(), 8888); //3. 发送数据出去 socket.send(packet); socket.close(); } }
例:UDP实现多发多收
ServerDemo2.java
package d3_udp2; import java.net.DatagramPacket; import java.net.DatagramSocket; //接收端 public class ServerDemo2 { public static void main(String[] args) throws Exception{ System.out.println("=====服务端启动====="); //1. 创建接收端对象,注册端口 DatagramSocket socket = new DatagramSocket(8888); //2. 创建一个数据包对象接收数据(韭菜盘子) byte[] buffer = new byte[1024 * 64]; DatagramPacket packet = new DatagramPacket(buffer, buffer.length); while (true) { //3. 等待,接受数据 socket.receive(packet); //4. 取出数据即可 //读取多少倒出多少 int len = packet.getLength(); String rs = new String(buffer, 0, len); System.out.println("收到了来自:" + packet.getAddress() + ",对方端口:" + packet.getPort() + "的消息:" + rs); } } }
ClientDemo1.java
package d3_udp2; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.InetAddress; import java.util.Scanner; //发送端 多发多收 public class ClientDemo1 { public static void main(String[] args) throws Exception{ System.out.println("=====客户端启动====="); //1. 创建发送端对象 发送端自带默认的端口号 DatagramSocket socket = new DatagramSocket(6666); Scanner sc = new Scanner(System.in); while (true) { System.out.println("请说:"); String msg = sc.nextLine(); if("exit".equals(msg)){ System.out.println("离线成功!"); socket.close(); break; } //2. 创建一个数据报对象封装数据 byte[] buffer = msg.getBytes(); DatagramPacket packet = new DatagramPacket(buffer, buffer.length, InetAddress.getLocalHost(), 8888); //3. 发送数据出去 socket.send(packet); } } }
UDP实现广播:
- 发送端发送的数据包的目的地写的是广播地址,且指定端口。(255.255.255.255,9999)
- 本机所在网段的其他主机的程序只要匹配端口成功就可以收到消息了。(9999)
ClientDemo1.java
package d4_udp3_broadcast; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.InetAddress; import java.util.Scanner; //发送端 多发多收 public class ClientDemo1 { public static void main(String[] args) throws Exception{ System.out.println("=====客户端启动====="); //1. 创建发送端对象 发送端自带默认的端口号 DatagramSocket socket = new DatagramSocket(6666); Scanner sc = new Scanner(System.in); while (true) { System.out.println("请说:"); String msg = sc.nextLine(); if("exit".equals(msg)){ System.out.println("离线成功!"); socket.close(); break; } //2. 创建一个数据报对象封装数据 byte[] buffer = msg.getBytes(); //填入接收方的IP和端口 DatagramPacket packet = new DatagramPacket(buffer, buffer.length, InetAddress.getByName("255.255.255.255"), 9999); //3. 发送数据出去 socket.send(packet); } } }
ServerDemo2.java
package d4_udp3_broadcast; import java.net.DatagramPacket; import java.net.DatagramSocket; //接收端 public class ServerDemo2 { public static void main(String[] args) throws Exception{ System.out.println("=====服务端启动====="); //1. 创建接收端对象,注册端口 DatagramSocket socket = new DatagramSocket(9999); //2. 创建一个数据包对象接收数据(韭菜盘子) byte[] buffer = new byte[1024 * 64]; DatagramPacket packet = new DatagramPacket(buffer, buffer.length); while (true) { //3. 等待,接受数据 socket.receive(packet); //4. 取出数据即可 //读取多少倒出多少 int len = packet.getLength(); String rs = new String(buffer, 0, len); System.out.println("收到了来自:" + packet.getAddress() + ",对方端口:" + packet.getPort() + "的消息:" + rs); } } }
UDP实现组播:
- 发送端的数据包的目的地是组播IP。(例如:224.0.1.1,端口:9999)
- 接收端必须绑定该IP(224.0.1.1),端口还要对应发送端的目的端口9999,这样即可接收该组播消息
- DatagramSocket的子类MulticastSocket可以在接收端绑定组播IP
ServerDemo2.java
package d5_udp4_multicast; import java.net.*; //接收端 public class ServerDemo2 { public static void main(String[] args) throws Exception{ System.out.println("=====服务端启动====="); //1. 创建接收端对象,注册端口 MulticastSocket socket = new MulticastSocket(9999); //把当前接收端加入到一个组播组当中,绑定对应的组播消息的组播IP socket.joinGroup(InetAddress.getByName("224.0.1.1")); //两个方法都可以 //Socket.joinGroup(new InetSocketAddress(InetAddress.getByName("224.0.1.1"), 9999), // NetworkInterface.getByInetAddress(InetAddress.getLocalHost())); //2. 创建一个数据包对象接收数据(韭菜盘子) byte[] buffer = new byte[1024 * 64]; DatagramPacket packet = new DatagramPacket(buffer, buffer.length); while (true) { //3. 等待,接受数据 socket.receive(packet); //4. 取出数据即可 //读取多少倒出多少 int len = packet.getLength(); String rs = new String(buffer, 0, len); System.out.println("收到了来自:" + packet.getAddress() + ",对方端口:" + packet.getPort() + "的消息:" + rs); } } }
ClientDemo1.java
package d5_udp4_multicast; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.InetAddress; import java.util.Scanner; //发送端 多发多收 public class ClientDemo1 { public static void main(String[] args) throws Exception{ System.out.println("=====客户端启动====="); //1. 创建发送端对象 发送端自带默认的端口号 DatagramSocket socket = new DatagramSocket(6666); Scanner sc = new Scanner(System.in); while (true) { System.out.println("请说:"); String msg = sc.nextLine(); if("exit".equals(msg)){ System.out.println("离线成功!"); socket.close(); break; } //2. 创建一个数据报对象封装数据 byte[] buffer = msg.getBytes(); //填入接收方的IP和端口 DatagramPacket packet = new DatagramPacket(buffer, buffer.length, InetAddress.getByName("224.0.1.1"), 9999); //3. 发送数据出去 socket.send(packet); } } }
37.3 TCP通信
例:TCP实现一发一收
ServerDemo2.java
package d5_socket1; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.net.ServerSocket; import java.net.Socket; //目标:开发socket网络编程入门的艾玛的服务端,实现接收消息 public class ServerDemo2 { public static void main(String[] args) { try { System.out.println("=====服务端启动成功====="); //1. 注册端口 ServerSocket serverSocket = new ServerSocket(7777); //2. 必须调用accept方法,等待接收客户端的Socket连接请求,建立Socket通信管道 Socket socket = serverSocket.accept(); //3. 从socket通信管道中得到一个字节输入流 InputStream is = socket.getInputStream(); //4. 把字节输入流包装成缓冲字符输入流进行消息的接收 BufferedReader br = new BufferedReader(new InputStreamReader(is)); //5. 按照行读取消息 String msg; if((msg = br.readLine()) != null){ System.out.println(socket.getRemoteSocketAddress() + "说了:" + msg); } } catch (IOException e) { e.printStackTrace(); } } }
ClientDemo1.java
package d5_socket1; import java.io.IOException; import java.io.OutputStream; import java.io.PrintStream; import java.net.Socket; //目标:完成Socket网络编程入门案例的客户端开发,实现1发1收 public class ClientDemo1 { public static void main(String[] args) { try { System.out.println("=====客户端启动成功====="); //1. 创建Socket通信管道请求有服务端的连接 //public Socket(String host, int port) //参数一:服务器的IP //参数二:服务端的端口 Socket socket = new Socket("127.0.0.1",7777); //2. 从socket通信管道中得到一个字节输出流,负责发送数据 OutputStream os = socket.getOutputStream(); //3. 把低级的字节流包装成打印流 PrintStream ps = new PrintStream(os); //4. 发送消息 ps.println("我是TCP的客户端"); ps.flush(); //关闭资源 //socket.close(); } catch (IOException e) { e.printStackTrace(); } } }
例:TCP实现多发多收
ServerDemo2.java
package d6_socket2; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.net.ServerSocket; import java.net.Socket; //目标:开发socket网络编程入门的艾玛的服务端,实现接收消息 public class ServerDemo2 { public static void main(String[] args) { try { System.out.println("=====服务端启动成功====="); //1. 注册端口 ServerSocket serverSocket = new ServerSocket(7777); //2. 必须调用accept方法,等待接收客户端的Socket连接请求,建立Socket通信管道 Socket socket = serverSocket.accept(); //3. 从socket通信管道中得到一个字节输入流 InputStream is = socket.getInputStream(); //4. 把字节输入流包装成缓冲字符输入流进行消息的接收 BufferedReader br = new BufferedReader(new InputStreamReader(is)); //5. 按照行读取消息 String msg; while((msg = br.readLine()) != null){ System.out.println(socket.getRemoteSocketAddress() + "说了:" + msg); } } catch (IOException e) { e.printStackTrace(); } } }
ClientDemo1.java
package d6_socket2; import java.io.IOException; import java.io.OutputStream; import java.io.PrintStream; import java.net.Socket; import java.util.Scanner; //目标:完成Socket网络编程入门案例的客户端开发,实现多发多收 public class ClientDemo1 { public static void main(String[] args) { try { System.out.println("=====客户端启动成功====="); //1. 创建Socket通信管道请求有服务端的连接 //public Socket(String host, int port) //参数一:服务器的IP //参数二:服务端的端口 Socket socket = new Socket("127.0.0.1",7777); //2. 从socket通信管道中得到一个字节输出流,负责发送数据 OutputStream os = socket.getOutputStream(); //3. 把低级的字节流包装成打印流 PrintStream ps = new PrintStream(os); Scanner sc = new Scanner(System.in); while (true) { //4. 发送消息 System.out.println("请说:"); String msg = sc.nextLine(); ps.println(msg); ps.flush(); } } catch (IOException e) { e.printStackTrace(); } } }
例:实现服务端接收多个客户端
ServerDemo2.java
package d7_socket3; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.net.ServerSocket; import java.net.Socket; //目标:实现服务端可以同时处理多个客户端的消息 public class ServerDemo2 { public static void main(String[] args) { try { System.out.println("=====服务端启动成功====="); //1. 注册端口 ServerSocket serverSocket = new ServerSocket(7777); //定义一个死循环由主线程负责不断的接收客户端的Socket管道连接 while (true) { //2. 每接收到一个客户端的Socket管道,交给一个独立的子线程负责读取消息 Socket socket = serverSocket.accept(); System.out.println(socket.getRemoteSocketAddress() + ":它来了,上线了!"); //3. 开始创建独立线程处理哦socket new ServerReaderThread(socket).start(); } } catch (IOException e) { e.printStackTrace(); } } }
ClientDemo1.java
package d7_socket3; import java.io.IOException; import java.io.OutputStream; import java.io.PrintStream; import java.net.Socket; import java.util.Scanner; //目标:完成Socket网络编程入门案例的客户端开发,实现多发多收 public class ClientDemo1 { public static void main(String[] args) { try { System.out.println("=====客户端启动成功====="); //1. 创建Socket通信管道请求有服务端的连接 //public Socket(String host, int port) //参数一:服务器的IP //参数二:服务端的端口 Socket socket = new Socket("127.0.0.1",7777); //2. 从socket通信管道中得到一个字节输出流,负责发送数据 OutputStream os = socket.getOutputStream(); //3. 把低级的字节流包装成打印流 PrintStream ps = new PrintStream(os); Scanner sc = new Scanner(System.in); while (true) { //4. 发送消息 System.out.println("请说:"); String msg = sc.nextLine(); ps.println(msg); ps.flush(); } } catch (IOException e) { e.printStackTrace(); } } }
ServerReaderThread.java
package d7_socket3; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.net.Socket; public class ServerReaderThread extends Thread{ private Socket socket; public ServerReaderThread(Socket socket){ this.socket = socket; } @Override public void run(){ try { //3. 从socket通信管道中得到一个字节输入流 InputStream is = socket.getInputStream(); //4. 把字节输入流包装成缓冲字符输入流进行消息的接收 BufferedReader br = new BufferedReader(new InputStreamReader(is)); //5. 按照行读取消息 String msg; while((msg = br.readLine()) != null){ System.out.println(socket.getRemoteSocketAddress() + "说了:" + msg); } } catch (IOException e) { System.out.println(socket.getRemoteSocketAddress() + ":下线了!"); } } }
黑马全套Java教程(九):网络编程(四)+https://developer.aliyun.com/article/1556510