网络编程基础了解
视频链接 特别提醒 mac使用的查询ip是ifconfig
三要素
IP:设备在设备在网络中的地址,是唯一的标识。
- IPv4:目前主流方案,但数量有限(2^32),已经不够用了
- IPv4的分类:公网地址和私有地址:192.168开头的是私有地址,节省ip
- IPv6:为了解决 IPv4 不够用而出现,数量极多(2^128)
- 特殊 IP:127.0.0.1(也称 localhost)
- 本地回环地址(Loopback)
- 数据不会经过网卡,只在本机内部流转
- 常用于本机程序之间的测试
常用的终端命令
- 查看本机IP地址 win:ipconfig:,mac:ifconfig,mac会出现所有网络接口的清单,不只是你正在用的那个,所以看起来特别多。
- ping:检查网络是否联通
调用InetAddress
//1.获取InetAddress的对象
//表示Ip的对象一台电脑的对象
InetAddress address=InetAddress.getLocalHost();
System.out.println(address);
String name=address.getHostName();
System.out.println(name);
String ip=address.getHostAddress();
System.out.println(ip);//127.0.0.1
端口号:应用程序在设备中的唯一标识
由两个字节表示的整数,取值是0-65535
协议
-
数据在网络中传输的规则。
常见协议: UDP TCP HTTP HTTPS FTP 计算机网络中,连接和通信的规则被称为网络通信协议。 -
OSI 参考模型:世界互联协议标准、全球通信规范。单模型过于理想化,未能在因特网上进行广泛推广。
-
TCP/IP 参考模型(或 TCP/IP 协议):事实上的国际标准。
UDP 协议
- 用户数据报协议(User Datagram Protocol)
- UDP 是面向无连接的通信协议。
- 特点:
- 速度快
- 有大小限制,一次最多发送 64K
- 数据不安全
- 容易丢失数据
TCP 协议
- 传输控制协议(Transmission Control Protocol)
- TCP 协议是面向连接的通信协议。
- 特点:
- 速度相对慢
- 没有大小限制
- 数据安全、可靠
UDP协议(发送数据)
//1.创建对象
//细节
//绑定端口,以后我们就是通过这个端口往外发送
//空参构造方法,系统随机分配端口
//有参构造方法,可以指定端口
DatagramSocket ds=new DatagramSocket();
//打包数据
String str="你好啊!!!";
byte[] bytes = str.getBytes();
InetAddress address=InetAddress.getByName("127.0.0.1");
int port=10086;
DatagramPacket dp=new DatagramPacket(bytes,bytes.length,address,port);
//3.发送数据
ds.send(dp);
//4释放资源
ds.close();
UDP协议(接受数据)
//接收数据
//1.创建DatagramSocket对象(快递公司)
//细节:
//在接收的时候,一定要绑定端口
//而且绑定的端口一定要和发送的端口保持一致
DatagramSocket ds=new DatagramSocket(10086);
//接收数据包
byte[] buf=new byte[1024];
DatagramPacket dp=new DatagramPacket(buf,buf.length);
ds.receive(dp);
//3.解析
byte[] data = dp.getData();
int len=dp.getLength();
InetAddress address=dp.getAddress();
int port=dp.getPort();
System.out.println("接收到的数据"+new String(data,0,len));
System.out.println("数据是从"+address+"这台电脑中的"+port+"这个端口发出的");
ds.close();
对上面两个协议的代码讲解
-
DatagramSocket 我们把DatagramSocke想象成是一个快递站,而输出和和输出理解为发送和接收快递,任何的快递都要通过DatagramSocke作为载体和发生空间来操作
-
DatagramPacket UDP的数据包
-
数据(byte[])
-
目标 IP + 目标端口(发送时需要)
-
发送方 IP + 发送方端口(接收后可以取到)
byte[] buf=new byte[1024];
DatagramPacket dp=new DatagramPacket(buf,buf.length);
// `bytes`:要发的内容
//`bytes.length`:内容长度
//`address`:目标 IP
//`port`:目标端口(10086)
- 接受端是通过
byte[] buf = new byte[1024];
DatagramPacket dp = new DatagramPacket(buf, buf.length);
//`buf`:接收缓冲区(箱子容量)
//`receive(dp)` 后,数据就被填进这个 `buf` 里
InetAddress:IP 地址对象- 发送端
InetAddress address = InetAddress.getByName("127.0.0.1");
而在接受端
InetAddress address = dp.getAddress();
总结
//接收数据
ds.receive(dp);
//发送数据
ds.send(dp);
通过快递站的类比我们发现输入和输出也就是对于整个DatagramPacket(快递)的操作是必须在快递站(DatagramSocket)执行的,DatagramPacket而里面的属性包括buf(内容),address(地址),以及port端口号的操作是在DatagramPacket执行的,这些相当于快递箱子里面的货品拆开和包装是对于DatagramPacket执行的不是DatagramSocket
-
DatagramSocket 负责「通信行为」
- 发送数据:ds.send(dp)
- 接收数据:ds.receive(dp)
-
DatagramPacket 负责「数据本身」
- 包含数据内容(buf)
- 目标或来源 IP(address)
- 目标或来源端口(port)
可以将 DatagramSocket 类比为「快递站」, DatagramPacket 类比为「快递包裹」。
所有的发送与接收操作,必须通过 DatagramSocket 完成; 而数据的封装、解析、地址和端口信息,都由 DatagramPacket 负责。
UDP的三种通信方式
一、单播(Unicast)
之前写的就是单播(指针对 特定的一台电脑 进行通信,这里不详细说明)。
- 通信方式:一对一
- 特点:
- 需要明确指定目标 IP 地址 + 端口号
- 数据只会发送到指定的那一台主机
- 最常见、最基础的 UDP 使用方式
二、多播
多播是 一对多通信,发送端只发送 一份数据,
加入同一个多播组的所有接收端都可以收到。
-
通信方式:一对多(组播)
-
特点:
-
使用 多播 IP 地址
-
IPv4 多播地址范围:
224.0.0.0 ~ 239.255.255.255 -
接收端必须 主动加入多播组
-
相比广播更节省网络带宽
-
我们在输出端(SendMessageDemo)当中更改address为224.0.0.1
MulticastSocket ms= new MulticastSocket();
//打包数据
String str="你好啊!!!";
byte[] bytes = str.getBytes();
InetAddress address=InetAddress.getByName("224.0.0.1");
int port=10000;
DatagramPacket dp=new DatagramPacket(bytes,bytes.length,address,port);
//3.发送数据
ms.send(dp);
//4释放资源
ms.close();
而我们在输入端(ReceiveMessageDemo1,ReceiveMessageDemo2,ReceiveMessageDemo3) 分别
//1.创建对象DatagramSocket的对象
MulticastSocket ms = new MulticastSocket(10000);
//2.将当前本机添加到224.0.0.1的这一组当中
InetAddress address = InetAddress.getByName("224.0.0.1");
ms.joinGroup(address);
归位到一个指定的组当中,将当前主机加入到指定的多播组中
三、广播(Broadcast)
广播是 一对所有通信,
发送的数据会被同一局域网内的所有主机接收到。
-
通信方式:一对所有
-
特点:
-
使用广播地址
-
常见广播地址:
255.255.255.255 -
接收端无需提前加入
-
容易造成网络负载
-
InetAddress address = InetAddress.getByName("255.255.255.255");
DatagramPacket dp =
new DatagramPacket(bytes, bytes.length, address, 10086);
TCP
-
TCP 是可靠、面向连接的传输层协议(先建立连接,再传数据)
-
客户端使用
Socket主动连接服务器;服务器使用ServerSocket监听端口,并通过accept()得到用于通信的Socket -
Socket上通过getInputStream()/getOutputStream()获取 IO 流进行收发数据
1. 客户端流程(Socket)
目标:连接服务器 → 获取输出流 → 写数据 → 释放资源
步骤
-
创建客户端 Socket 并连接指定服务器
Socket(String host, int port)
-
获取输出流并写数据
OutputStream getOutputStream()
-
释放资源
close()
//TCP协议,发送数据
//1.创建Socket对象(客户端)
//细节:在创建对象的同时会连接服务端
//如果连不上,代码会报错
Socket socket = new Socket("127.0.0.1", 10000);
System.out.println("Client connected: " + socket.getRemoteSocketAddress());
//2.可以从连接通道中获取输出流
OutputStream os=socket.getOutputStream();
//写出数据
os.write("aaa".getBytes());
// 关键:告诉Server数据发送完毕
socket.shutdownOutput(); // 发送结束标志
// 然后可以关闭资源
socket.close();
-
shutdownOutput()会发送 FIN(半关闭),告诉对方“我不再发送数据了”。 -
这样服务端
read()才会最终返回-1,退出 我们发现我们需要在客户端写数据明确端口也就是我这个客户端要写什么数据:
//2.可以从连接通道中获取输出流
OutputStream os=socket.getOutputStream();
//写出数据
os.write("aaa".getBytes());
以及我们要发送到哪个端口
//2.可以从连接通道中获取输出流
Socket socket = new Socket("127.0.0.1", 10000);//设备地址:本机的回环地址,端口号10000
2. 服务器端流程(ServerSocket)
目标:监听端口 → 接收连接 → 获取输入流 → 读数据 → 释放资源
步骤
-
创建服务器端 ServerSocket(绑定端口)
ServerSocket(int port)
-
监听并接收客户端连接(阻塞等待)
Socket accept()
-
获取输入流读取数据并处理
InputStream getInputStream()
-
释放资源
close()
ServerSocket ss = new ServerSocket(10000);
System.out.println("Server listening 10000...");
Socket socket = ss.accept();
System.out.println("Client connected: " + socket.getRemoteSocketAddress());
InputStream is = socket.getInputStream();
byte[] buf = new byte[1024];
int len;
while ((len = is.read(buf)) != -1) {
System.out.println("recv len=" + len + " data=" +
new String(buf, 0, len, java.nio.charset.StandardCharsets.UTF_8));
}
System.out.println("Client closed output.");
socket.close();
ss.close();
- 1 我们首先创建服务器端 ServerSocket(绑定端口10000)
- 2 监听并接收客户端连接(阻塞等待)
//2.可以从连接通道中获取输出流
Socket socket = ss.accept();//这一段就是监听和接收客户端的连接
- 3 最后我们读取监听到的内容
socket.getRemoteSocketAddress());
InputStream is = socket.getInputStream();
byte[] buf = new byte[1024];
int len;
while ((len = is.read(buf)) != -1) {
System.out.println("recv len=" + len + " data=" +
new String(buf, 0, len, java.nio.charset.StandardCharsets.UTF_8));
}