IP:设备在网络中的地址,是唯一的标识
IPv4是目前的主流方案,最多只能有2^32个IP,目前已经使用完了,为了解决这个问题而出现的IPv6最多有2^128个IP,
特殊IP地址: 127.0.0.1,也可以是localhost:是回送地址也称本地回环地址,也称本机IP,永远只会寻找当前所在本机
端口号:应用程序在设备中唯一的标识,一个端口号只能被一个应用程序使用
由两个字节表示的整数,取值范围:0~65535,其中0~1023之间的端口号用于一些知名的网络服务或者应用
协议是指数据在网络传输中的规则,由于网络通信是非常复杂的事,如果使用一个协议来约定所有的网络通信细节,就会导致这个协议非常庞大且复杂,所以把这个大的协议拆分成多个小的协议,让每一个小的协议专注于解决一个问题,再让这些协议之间相互配合。协议分层就是把功能定位类似的协议,放到同一层中,并且约定好层与层之间的交互关系,上层协议调用下层协议,下层协议给上层提供服务
协议分层的优点:
应用层:是最接近用户的一层,为用户提供各种网络应用服务
传输层:提供端到端的通信服务,确保数据可靠、有序地从源端传输到目的端,只关注网络通信中的“起点和终点”
网络层:主要功能是进行寻址和路由选择
数据链路层:针对上述规划好的路径进行具体的实施
物理层:描述的是硬件设备需要满足的条件
上述过程就相当于公司中董事长(传输层)指定公司的目标,高管(网络层)对目标进行规划,基层员工(数据链路层)按照规划具体实施,办公设备(物理层)
相较于 TCP/IP 五层协议多出了表示层和会话层,TCP/IP 五层协议是把表示层和会话层和应用层融合到一起了

网络设备所在分层:
假如需要通过 QQ 来发送 Hello 给另一个人,在发送方这里需要经过:
接收方视角:
传输层只关注起点和终点,中间过程的操作可能存在很多的交换机和路由器来完成数据转发的过程
交换机:封装分用到数据链用层,就可以决定数据是转发还是丢弃了(不再分用)(二层转发)
路由器:封装分用到网络层(三层转发)
TCP 和 UDP 的区别:
回显服务器:客户端发送什么请求,服务器就返回什么相应(不包含任何逻辑处理)
对于服务器端来说,需要在 socket 对象创建的时候,就指定一个端口号,作为构造方法的参数,后续服务器运行之后,操作系统就会把端口号和该进程关联起来
对于同一个系统来说,同一时刻,一个端口号只能被一个进程绑定,但是一个进程可以绑定多个端口号(创建多个 socket 对象),端口号就是为了区分进程
创建 socket 对象之后开始接收客户端的请求:
//获取用户请求并解析
DatagramPacket requestPacket = new DatagramPacket(new byte[4096],4096);
//接收请求
socket.receive(requestPacket);
String request = new String(requestPacket.getData(),0,requestPacket.getLength());
String response = process(request);接收到用户的请求之后,服务器端需要作出响应,发送到客户端
//把响应写回客户端
DatagramPacket responsePacket = new DatagramPacket(response.getBytes(),response.getBytes().length);
socket.send(responsePacket);这里把相应的内容写回客户端时,由于原来是一个字符串,直接获取长度获取的是字符的个数,所以需要获取字节数组之后再获取字节数组的长度
还有就是,由于 UDP 是无连接的(没有保存通信双方的 ip 和端口),DatagramSocket 这个类中不持有对方的信息,进行 send 的时候就需要在 send 的数据包里,把要发给谁这样的信息写进去
//把响应写回客户端
DatagramPacket responsePacket = new DatagramPacket(response.getBytes(),
response.getBytes().length,requestPacket.getSocketAddress());
socket.send(responsePacket);服务端整体逻辑:
public class UdpEchoServer {
private DatagramSocket socket = null;
public UdpEchoServer(int port) throws SocketException {
//对于服务器端来说,需要在 socket 对象创建的时候,就指定一个端口号,作为构造方法的参数
//后续服务器运行之后,操作系统就会把端口号和该进程关联起来
socket = new DatagramSocket(port);
}
public void start() throws IOException {
System.out.println("服务器启动...");
while (true){
//获取用户请求并解析
DatagramPacket requestPacket = new DatagramPacket(new byte[4096],4096);
//接收请求
socket.receive(requestPacket);
String request = new String(requestPacket.getData(),0,requestPacket.getLength());
String response = process(request);
//把响应写回客户端
DatagramPacket responsePacket = new DatagramPacket(response.getBytes(),
response.getBytes().length,requestPacket.getSocketAddress());
socket.send(responsePacket);
//打印信息
System.out.printf("[%s:%d] req=%s, resp=%s\n",requestPacket.getAddress(),requestPacket.getPort(),
request,response);
}
}
//根据请求确定响应
private String process(String request){
return request;
}
public static void main(String[] args) throws IOException {
UdpEchoServer udpEchoServer = new UdpEchoServer(8090);
udpEchoServer.start();
}
}接下来看客户端:

由于是客户端主动发送的请求,所以需要知道服务端的端口号才能找到服务器,所以客户端不需要指定端口号,但不是客户端不需要端口号,而是系统随机分配了一个端口给客户端
此外,如果客户端指定了端口之后,由于客户端是在用户的电脑上运行的,所指定的端口就可能和现有的端口冲突
构造方法里需要拿到服务端的 IP 和端口号
public UdpEchoClient(String serverIP,int serverPort) throws SocketException {
socket = new DatagramSocket();
this.serverIP = serverIP;
this.serverPort = serverPort;
}接下来创建请求数据报时,传入请求内容的字节数组和长度,再传入服务器的 IP 和端口号,但是发现报错了

因为原来定义的 IP 是字符串类型的,所以需要转化一下:
DatagramPacket requestPacket = new DatagramPacket(request.getBytes(),request.getBytes().length,
InetAddress.getByName(this.serverIP),this.serverPort);public class UdpEchoClient {
private DatagramSocket socket = null;
private String serverIP;
private int serverPort;
public UdpEchoClient(String serverIP,int serverPort) throws SocketException {
socket = new DatagramSocket();
this.serverIP = serverIP;
this.serverPort = serverPort;
}
public void start() throws IOException {
System.out.println("客户端开始运行...");
Scanner scanner = new Scanner(System.in);
while (true){
System.out.print("->");
String request = scanner.next();
//构造出 UDP 请求
DatagramPacket requestPacket = new DatagramPacket(request.getBytes(),request.getBytes().length,
InetAddress.getByName(this.serverIP),this.serverPort);
socket.send(requestPacket);
//从服务端读取到响应
DatagramPacket responsePacket = new DatagramPacket(new byte[4096],4096);
socket.receive(responsePacket);
//打印响应到的内容
String response = new String(responsePacket.getData(),0, responsePacket.getLength());
System.out.println(response);
}
}
public static void main(String[] args) throws IOException {
UdpEchoClient udpEchoClient = new UdpEchoClient("127.0.0.1",8090);
udpEchoClient.start();
}
}