目录
1.UDP协议介绍
2.UDP协议在Java中的类
2.1DatagramSocket类
2.2DatagramPacket
3.回显服务器
3.1Sever端
3.2Client端
1.UDP协议介绍
UDP协议是一种网络协议,它是无连接的,全双工,并且是面向数据报,不可靠的一种协议。常用于在线视频播放,游戏这种实时性要求比较高的应用。或者无需可靠传输的应用,如DNS查询 SNMP等。一次UDP数据报报文传输的数据最大为64kb,实际上,UDP因为头部占用八个字节。所以可传输的精准大小为64kb-8b = 65507字节。
2.UDP协议在Java中的类
UDP协议作为传输层协议,在Java中我们无需面对原生UDP协议,我们只需要调用Java封装好的类来使用UDP协议来传输和接受数据即可。在Java中有两个类,分别是DatagramSocket类和DatagramPacket类。
2.1DatagramSocket类
DatagramSocket用于创建Socket,绑定到端口号。它将网卡抽象成文件来方便程序猿来来操作。
DatagramSocket类的构造方法:
DatagramSocket类里的方法:
方法签名 | 方法说明 |
void receive(DatagramPacket p) |
从此套接字接收数据报(如果没有接收到数据报,该方法会阻 塞等待) |
void send(DatagramPacket p) |
从此套接字发送数据报包(不会阻塞等待,直接发送) |
void close() | 关闭此数据报套接字 |
2.2DatagramPacket
DatagramPacket是UDP Socket发送和接收的数据报
DatagramPacket的构造方法:
方法签名 | 方法说明 |
DatagramPacket(byte[] buf, int length) |
构造一个DatagramPacket以用来接收数据报,接收的数据保存在 字节数组(第一个参数buf)中,接收指定长度(第二个参数 length) |
DatagramPacket(byte[] buf, int offset, int length, SocketAddress address) |
构造一个DatagramPacket以用来发送数据报,发送的数据为字节 数组(第一个参数buf)中,从0到指定长度(第二个参数 length)。address指定目的主机的IP和端口号 |
DatagramPacket的方法:
方法签名 | 方法说明 |
InetAddress getAddress() |
从接收的数据报中,获取发送端主机IP地址;或从发送的数据报中,获取 接收端主机IP地址 |
int getPort() | 从接收的数据报中,获取发送端主机的端口号;或从发送的数据报中,获 取接收端主机端口号 |
byte[] getData() | 获取数据报中的数据 |
3.回显服务器
为了方便大家理解和更好的使用UDP协议中的这两个类来实现网络编程,跨主机通信。
我们来写个简单的客户端-服务器程序-回显服务器,客户端将数据发送到服务器上,服务器在返回给客户端这个数据并且打印下来。主要目的是为了方便大家理解。
3.1Sever端
在Sever端写之前,我们先讲明白几件事,首先就是我们的构造方法要不要指定端口号。答案是肯定的,因为这是服务器端,客户端想要访问它,肯定要有一个固定的地址,不然如何去访问,所以在构造方法中,我们直接从1024-65535这些端口号中选择一个给绑定上。
还有就是我们的主要逻辑过程:
1.接受数据并解析
2.将这个数据进行服务器的操作,即根据请求计算响应。
3.将响应返回给客户端。
明白了这些以后,我们开始编写代码
import java.io.IOException; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.SocketException; import java.nio.charset.StandardCharsets; public class UdpEchoSever { DatagramSocket socket = null; public UdpEchoSever (int sort) throws SocketException { socket = new DatagramSocket(sort);//因为是服务器程序,所以我们要指定端口号 } public void start() throws IOException { while (true){ //这里的while(true)是因为会有很多客户端来访问这个服务器,所以我们的服务器是7*24小时运行的 System.out.println("服务器启动"); byte[] bytes = new byte[1024]; DatagramPacket packet = new DatagramPacket(bytes,0,bytes.length); //创建一个数据报用来接收,这是一个输出型参数 socket.receive(packet); //如果没有客户端进行访问,就会进行阻塞等待 String request = new String(packet.getData(),0, packet.getLength()); //将这个数据报解析成字符串的形式 String response = fun(request); DatagramPacket responsePacket = new DatagramPacket(response.getBytes(),response.getBytes().length,packet.getSocketAddress()); //在构造的时候要将数据报转化为字符数组,并且指定IP地址和端口号发送,这里我们的IP地址和端口号,已经被存入到,packet.getSocketAddress()里面了 System.out.printf("[%s :%d] req:%s,resp : %s ",responsePacket.getAddress().toString(),responsePacket.getPort(),request,response); socket.send(responsePacket); } } private String fun(String request) { return request; } public static void main(String[] args) throws IOException { UdpEchoSever udpEchoSever = new UdpEchoSever(9090); udpEchoSever.start(); } }
3.2Client端
至于客户端,我们的逻辑就是,在构造方法里,要传入服务器的IP地址和端口号,以方便后续使用。
至于主方法,我们的大概思路分为以下几步
1.用户通过控制台输入,并且构造成DatagramPacket数据报,此时这个类里面应该有我们的数据和IP地址以及端口号
2.将这个数据报发送给服务器
3.从服务器拿到响应并且解析
4.将解析的响应打印出来
现在我们开始编程:
import javax.xml.ws.soap.Addressing; import java.io.IOException; import java.net.*; import java.util.Scanner; public class UdpEchoClient { DatagramSocket socket = null; int severport; String severIp; public UdpEchoClient(String severIp,int severport) throws SocketException { //在构造方法中传入服务器的IP地址和端口号 socket = new DatagramSocket(); this.severIp = severIp; this.severport = severport; } public void start() throws IOException { Scanner scanner = new Scanner(System.in); System.out.println("客户端启动"); while (true){ System.out.print("->"); if(!scanner.hasNext()){ //用户输入到回车等这些空字符以后就结束 break; } String request = scanner.next(); DatagramPacket requestPacket = new DatagramPacket(request.getBytes(),request.getBytes().length, InetAddress.getByName(severIp),severport); //将数据以及IP地址(这里的IP地址是字符串, // 所以我们需要调用InetAddress.getByName())以及端口号也写进去 socket.send(requestPacket);//发送到服务器 byte[] bytes = new byte[1024]; DatagramPacket responsPacket = new DatagramPacket(bytes,bytes.length);//同样是输出型参数,所以要先有一个空的字符数组 socket.receive(responsPacket);//接收服务器传来的响应 String respons = new String(responsPacket.getData(),0,responsPacket.getLength());//将响应转化为字符串 System.out.println(respons);//打印下来 } } public static void main(String[] args) throws IOException { UdpEchoClient udpEchoClient = new UdpEchoClient("127.0.0.1",9090); udpEchoClient.start(); } }
我们来看看运行结果
可以看到客户端没有问题。我们再来看看服务器端的控制台:
也和我们预期的结果一样