背景
进行网络编程,就需要传输层为应用层提供的api,称为socket api,其中传输层提供了两种协议,TCP和UDP,其所对应的socket api也是不同的,下面介绍的是UDP协议
1.特点
1.无连接(就像发微信、QQ一样无需对方回应,建立连接,消息也能发过去,而相对应的有连接则是像电话这样的,对方需要接电话,建立连接才能通信)
2.不可靠传输(不知道对方有无收到消息,就像微信一样,对方有没有看见自己的消息是不知道的)
3.面向数据报(以数据报为传输的基本单位)
4.全双工(支持双向通信,可以在A给B通信的同时,B给A通信)
2.核心类
UDP的socket api提供的两大核心类分别为DatagramSocket类和DatagramPacket类
1.DatagramSocket 叫作socket类,本质上是一个文件,在系统中,还有一种特殊的socket文件,对应于网卡设备,进行网络通信,就需要打开一个内核中的socket文件,可以构造DatagramSocket对象来打开这个文件
2.DatagramPacket类,表示一个UDP数据报,传输数据,就是以DatagramPacket为基本单位
3.还有一个重要的类InetSocketAddress,是用来创建Socket地址,包含IP地址和端口号。
3.回显服务器的实现
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;
import java.nio.charset.StandardCharsets;
public class UdpEchoServer {
//要创建UDP服务器,需要先打开一个socket文件
private DatagramSocket socket = null;
public UdpEchoServer(int port) throws SocketException {
socket = new DatagramSocket(port);//绑定一个端口,将进程和端口号关联起来,程序如果要进行网络通信就需要一个端口号,端口号相当于网络上区分进程的身份标识符,端口号范围是0到65535
}
//启动服务器
public void start() throws IOException {
System.out.println("服务器启动");
while(true){
//1.读取客户端发来的请求
DatagramPacket requestPacket = new DatagramPacket(new byte[4096],4096);//分配了一段长度为4096的缓冲区(内存空间)
socket.receive(requestPacket);//若客户端没发请求,receive就会阻塞等待,直到有请求,才会读取数据并且返回到输出型参数
//2.对请求进行解析,把DatagramPacket(本质上是字节数组)转成一个String
String request = new String(requestPacket.getData(),0,requestPacket.getLength());//将缓冲区中的字节数组取出来,转成字符串
//3.根据请求处理响应
String response = process(request);
//4.把响应构造成DatagramPacket对象,用字符串里面的字节数组来构造内容,指定长度,还要将响应返回给客户端,getSocketAddress()是用了requestPacket存储的客户端的IP地址和端口号
DatagramPacket responsePacket = new DatagramPacket(response.getBytes(),response.getBytes().length,requestPacket.getSocketAddress());
//5.把这个DatagramPacket对象返回给客户端
socket.send(responsePacket);
System.out.println("客户端IP"+requestPacket.getAddress().toString()+"端口号"+requestPacket.getPort()+"请求"+request+"响应"+response);
}
}
//通过这个方法,实现根据请求处理响应,根据自己的实际情况来编写代码,此处是回显服务器,发来什么返回什么。
private String process(String request) {
return request;
}
//启动服务器
public static void main(String[] args) throws IOException {
UdpEchoServer udpEchoServer = new UdpEchoServer(6888);//端口号一般不能取1024以下的端口,是系统保留的端口,特殊端口。一般取1024到65535
udpEchoServer.start();
}
}
4.客户端的实现
import java.awt.*;
import java.io.IOException;
import java.net.*;
import java.nio.charset.StandardCharsets;
import java.util.Scanner;
public class UdpEchoClient {
private DatagramSocket socket = null;
public UdpEchoClient() throws SocketException {
//服务器得有固定端口客户端才好访问,客户端程序安装在用户电脑上,用户电脑上的端口号是不可控的,若指定端口号很有可能会重复
socket = new DatagramSocket();//客户端端口号一般是由操作系统自动分配
}
public void start() throws IOException {
Scanner scanner = new Scanner(System.in);
while (true) {
//1.让客户端从控制台读取一个数据
String request = scanner.next();
//2.把这个请求发送给服务器,构造DatagramPacket,需要包含请求的数据内容和服务器的IP地址和端口号,127.0.0.1是环回IP,表示当前主机。
DatagramPacket requestPacket = new DatagramPacket(request.getBytes(),request.getBytes().length, InetAddress.getByName("127.0.0.1"),6888);
//3.把数据发送给服务器
socket.send(requestPacket);
//4.从服务器读取响应数据
DatagramPacket responsePacket = new DatagramPacket(new byte[4096],4096);
socket.receive(responsePacket);
//5.转换响应的数据变成字符串
String response = new String(responsePacket.getData(),0,responsePacket.getLength());
System.out.println("request "+request+ " response "+response);
}
}
public static void main(String[] args) throws IOException {
UdpEchoClient udpEchoClient = new UdpEchoClient();
udpEchoClient.start();
}
}
把这个√上就可以同时启动多个客户端。
4. close方法
上述构造了一个DatagramSocket对象,相当于打开了一个特殊文件,但没有关闭文件是因为socket对象生命周期是伴随整个进程的,而这个进程是始终没有结束的,故不能提前关闭socket对象,而当进程结束的时候,对应的PCB也就没有了,文件描述符也没有了,也就相当于关闭了文件。