[Java网络编程三]编写基于UDP的Socket程序

基于 UDP 的数据报和套接字

UDP 是传出层的一种协议,它比 TCP 具有更快的传输速度,但不可靠。UDP 发送的数据单元称为 UDP 数据报。当网络传输时,无法保证数据报一定到达目的地,也无法保证各个数据报按发送顺序依次到达目的地。

在 Java 中,Java.net.DatagramSocket 负责接收和发送数据 UDP 数据报。它提供了接收和发送数据报的方法:

① public void receive(DatagramPacket dst) throws IOException //接收数据
② public void send(DatagramPacket src) thows IOException //发送数据

Java.net.DatagramPacket 表示 UDP 数据报。它的构造方法分为两类,一类用来接收数据,另一类用来发送数据,用于发送数据的构造方法需要设定数据报到达的目的地地址,而接收的不用。

① public DatagramPacket(byte[] data, int length) //接收数据
② public DatagramPacket(byte[] data, int offset, int length, InetAddress address, int port) //发送数据,包含目的地地址。

每个 DatagramSocket 与一个本地地址(包括本地主机的 IP 地址和本地 UDP 端口)绑定,每个 DatagramSocket 可以把 UDP 数据报发送给任意一个远程 DatagramSocket,也可以接收来自任意的一个远程 DatagramSocket的 UDP
数据报。UDP 数据报中包含了目的地址信息,DatagramSocket 根据该信息把数据报发送到目的地。

UDP 协议是无连接协议,客户端 DatagramSocket 和 服务器端的 DatagramSocket 无需建立连接,就能交换数据报。

下面用程序实现数据报发送接收的流程。

客户端实现

① 创建一个 DatagramSocket 对象,并指定一个端口。
② 创建两个 DatagramPacket 数据报对象,分别用于接收和发送数据。
③ 启动一个循环,用于发送数据和接收数据,如果没有接收到服务器的数据,重试五次后退出。发送出去的数据,不保证发送成功。可能此时服务器还没起来。
④ 发送和接收成功后,打印接收到的消息。

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.net.UnknownHostException;
public class UDPClient {
       
       private static final int TIMEOUT = 5000;//指定超时时间
       private static final int RETRY_NUM = 5;//重试次数
       
       public static void main(String[] args) throws SocketException, UnknownHostException {
              
              String msg = "hello, I am Client";//要发送的消息
              
              byte[] buf = new byte[1024];//用于接收消息的数组
              
              DatagramSocket ds = new DatagramSocket(9000);//创建一个Socket,并指定端口 9000
              
              InetAddress loc = InetAddress.getLocalHost();//获取本机地址
              
              DatagramPacket dp_send = new DatagramPacket(msg.getBytes(), msg.length(), loc,3000);//要发送的数据报,指定了目的地址和端口
              
              DatagramPacket dp_rece = new DatagramPacket(buf, 1024);//要接收的数据报
              
              ds.setSoTimeout(TIMEOUT);//设置超时时间
              int retry = 0;
              boolean receiveResponse = false;
              
              while(!receiveResponse && retry <= RETRY_NUM) {
                     try {
                           ds.send(dp_send);//发送数据报
                           ds.receive(dp_rece);//接收数据报
                           if(!dp_rece.getAddress().equals(loc)) {
                                  throw new IOException("receive packet from an unknow source!");
                           }
                           receiveResponse = true;
                     } catch (IOException e) {
                           // TODO Auto-generated catch block
                           e.printStackTrace();
                           retry += 1;
                     }
              }
              if(receiveResponse) {//成功接收,打印消息
                     System.out.println("client received data from server: ");
                     String msg_rece = new String(dp_rece.getData(), 0, dp_rece.getLength());
                     msg_rece += ("from " + dp_rece.getAddress().getHostAddress() + ": "+ dp_rece.getPort());
                     System.out.println(msg_rece);
                     //必须重新设定长度值,因为接收数据后会变为接收到的消息的实际长度值。
                     dp_rece.setLength(1024);
              } else {
                     System.out.println("no resposne");
              }
              ds.close();
       }
}

服务端的程序实现

① 创建一个 DatagramSocket, 用于收发数据报,并指定端口。
② 创建两个 DatagramPacket 数据报对象,分别用于接收和发送数据。
③ 启动一个 while 循环,用于接收数据报,然后再回复一个数据报。

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;
public class UDPServer {
     
     public static void main(String[] args) throws SocketException {
           String str_send = "hello, I am Server";
           
           byte[] buf = new byte[1024];//用于存储收到的数据
           
           DatagramSocket ds = new DatagramSocket(3000);//创建一个Socket对象
           
           DatagramPacket dp_rece = new DatagramPacket(buf, 1024);
           
           System.out.println("server is on, waitting for the client to send data!");
           
           boolean f = true;
           while(f) {
                try {
                     ds.receive(dp_rece);//接收数据,没有数据将会阻塞
                     String str_rece = new String(dp_rece.getData(), 0, dp_rece.getLength());
                     str_rece += ("from " + dp_rece.getAddress().getHostAddress() + ":" + dp_rece.getPort());
                     System.out.println(str_rece);
                     //创建一个数据报,指定目的地地址和端口
                     DatagramPacket dp_send = new DatagramPacket(str_send.getBytes(), str_send.length(), dp_rece.getAddress(), 9000);
                     ds.send(dp_send);//发送一个数据报
                     
                     //必须重新设定长度值,因为接收数据后会变为接收到的消息的实际长度值。
                     dp_rece.setLength(1024);
                } catch (IOException e) {
                     // TODO Auto-generated catch block
                     e.printStackTrace();
                }
           }
           ds.close();
     }
}

运行结果

先启动服务端,他会循环监听是否有数据包到来。接着启动客户端,发送数据到服务端。
① 服务端运行结果如下:

② 客户端运行结果如下:

你可能感兴趣的:([Java网络编程三]编写基于UDP的Socket程序)