• 在Java中实现UDP协议编程(DatagramSocket/DatagramPacket)


    1.什么是UDP协议?
    UDP( User Datagram Protocol )协议是用户数据报,在网络中它与TCP协议一样用于处理数据包。在OSI模型中,在第四层——传输层,处于IP协议的上一层。
    UDP是一种无连接的协议,每个数据报都是一个独立的信息,包括完整的源或目的地址,它在网络上以任何可能的路径传往目的地,因此能否到达目的地,到达目的地的时间以及内容的正确性都是不能被保证的。  
    2.为什么要使用UDP?

    在网络质量令人不十分满意的环境下,UDP协议数据包丢失会比较严重。但是由于UDP的特性:它不属于连接型协议,因而具有资源消耗小,处理速度快的优点,所以通常音频、视频和普通数据在传送时使用UDP较多,因为它们即使偶尔丢失一两个数据包,也不会对接收结果产生太大影响。比如聊天用的ICQ和OICQ就是使用的UDP协议。

    3.在Java中操纵UDP 使用位于JDK中Java.net包下的DatagramSocket和DatagramPacket类,可以非常方便地控制用户数据报文。

    3.1 DatagramSocket类:创建接收和发送UDP的Socket实例
    DatagramSocket():创建实例。通常用于客户端编程,它并没有特定监听的端口,仅仅使用一个临时的。 
    DatagramSocket(int port):创建实例,并固定监听Port端口的报文。 
    DatagramSocket(int port, InetAddress localAddr):这是个非常有用的构建器,当一台机器拥有多于一个IP地址的时候,由它创建的实例仅仅接收来自LocalAddr的报文

    receive(DatagramPacket d):接收数据报文到d中。receive方法产生一个“阻塞”。 
    send(DatagramPacket d):发送报文d到目的地。 
    setSoTimeout(int timeout):设置超时时间,单位为毫秒。 
    close():关闭DatagramSocket。在应用程序退出的时候,通常会主动释放资源,关闭Socket,但是由于异常地退出可能造成资源无法回收。所以,应该在程序完成时,主动使用此方法关闭Socket,或在捕获到异常抛出后关闭Sock

    注意:1.在创建DatagramSocket类实例时,如果端口已经被使用,会产生一个SocketException的异常抛出,并导致程序非法终止,这个异常应该注意捕获。 

    2.“阻塞”是一个专业名词,它会产生一个内部循环,使程序暂停在这个地方,直到一个条件触发。 


    3.2 DatagramPacket:用于处理报文,将byte数组、目标地址、目标端口等数据包装成报文或者将报文拆卸成byte数组。 
    DatagramPacket(byte[] buf, int length, InetAddress addr, int port):从buf数组中,取出length长的数据创建数据包对象,目标是addr地址,port端口。 
    DatagramPacket(byte[] buf, int offset, int length, InetAddress address, int port):从buf数组中,取出offset开始的、length长的数据创建数据包对象,目标是addr地址,port端口。 
    DatagramPacket(byte[] buf, int offset, int length):将数据包中从offset开始、length长的数据装进buf数组。
    DatagramPacket(byte[] buf, int length):将数据包中length长的数据装进buf数组。 
    getData():它从实例中取得报文的byte数组编码。 


    4.编写程序演示使用UDP协议数据报的发送和接受分析

      发送端
    1. 建立udpsocket服务端点。该端点建立,系统会随机分配一个端口。如果不想随机配置,可以手动指定。 DatagramSocket ds = new DatagramSocket(9002);

    2. 将数据进行packet包的封装,必须要指定目的地地址和端口。  byte[] buf = "hi 红军".getBytes(); DatagramPacket dp =new DatagramPacket(buf,buf.length,InetAddress.getByName("192.168.1.254"),9001);

    3. 通过socket服务的send方法将该包发出。 ds.send(dp);

    4. 将socket服务关闭。主要是关闭资源。 ds.close();

    接收端
    1. 建立udp的socket服务。要监听一个端口。 DatagramSocket ds = new DatagramSocket(9001);
    2. 定义一个缓冲区,将该缓冲区封装到packet包中。 byte[] buf = new byte[1024]; DatagramPacket dp = new DatagramPacket(buf,buf.length);
    3. 通过socket的receive方法将数据存入数据包中。 ds.receive(dp);
    4. 通过数据包dp的方法getData()、getAddress()、getPort()等方法获取包中的指定信息。
    5. 关闭socket。 ds.close();

    5. 案例代码实现

        案例说明:发送者发送数据到接受者那端,然后接受者那端再发送数据到发送者那端的小型案例

    1. package net;  
    2.   
    3. import java.io.IOException;  
    4. import java.net.DatagramPacket;  
    5. import java.net.DatagramSocket;  
    6. import java.net.InetAddress;  
    7. import java.net.SocketException;  
    8.   
    9. public class Demo02 {  
    10.   
    11.     // 发送者--->客户端 客户端--->发送者  
    12.     // 发送者发给客户端数据,客户端返回数据给发送者  
    13.     public static void send() {  
    14.         System.out.println("---send----");  
    15.         // 发送端  
    16.         try {  
    17.             // 创建发送方的套接字 对象 采用9004默认端口号  
    18.             DatagramSocket socket = new DatagramSocket(9004);  
    19.             // 发送的内容  
    20.             String text = "hi 红军!";  
    21.             byte[] buf = text.getBytes();  
    22.             // 构造数据报包,用来将长度为 length 的包发送到指定主机上的指定端口号。  
    23.             DatagramPacket packet = new DatagramPacket(buf, buf.length,  
    24.                     InetAddress.getByName("172.22.67.6"), 9001);  
    25.             // 从此套接字发送数据报包  
    26.             socket.send(packet);  
    27.             // 接收,接收者返回的数据  
    28.             displayReciveInfo(socket);  
    29.             // 关闭此数据报套接字。  
    30.             socket.close();  
    31.         } catch (SocketException e) {  
    32.             e.printStackTrace();  
    33.         } catch (IOException e) {  
    34.             // TODO Auto-generated catch block  
    35.             e.printStackTrace();  
    36.         }  
    37.     }  
    38.   
    39.     public static void recive() {  
    40.         System.out.println("---recive---");  
    41.         // 接收端  
    42.         try {  
    43.             //创建接收方的套接字 对象  并与send方法中DatagramPacket的ip地址与端口号一致  
    44.             DatagramSocket socket = new DatagramSocket(9001,  
    45.                     InetAddress.getByName("172.22.67.6"));  
    46.             //接收数据的buf数组并指定大小  
    47.             byte[] buf = new byte[1024];  
    48.             //创建接收数据包,存储在buf中  
    49.             DatagramPacket packet = new DatagramPacket(buf, buf.length);  
    50.             //接收操作  
    51.             socket.receive(packet);  
    52.             byte data[] = packet.getData();// 接收的数据  
    53.             InetAddress address = packet.getAddress();// 接收的地址  
    54.             System.out.println("接收的文本:::" + new String(data));  
    55.             System.out.println("接收的ip地址:::" + address.toString());  
    56.             System.out.println("接收的端口::" + packet.getPort()); // 9004  
    57.   
    58.             // 告诉发送者 我接收完毕了  
    59.             String temp = "我接收完毕了";  
    60.             byte buffer[] = temp.getBytes();  
    61.             //创建数据报,指定发送给 发送者的socketaddress地址  
    62.             DatagramPacket packet2 = new DatagramPacket(buffer, buffer.length,  
    63.                     packet.getSocketAddress());  
    64.             //发送  
    65.             socket.send(packet2);  
    66.             //关闭  
    67.             socket.close();  
    68.         } catch (SocketException e) {  
    69.             e.printStackTrace();  
    70.         } catch (IOException e) {  
    71.             // TODO Auto-generated catch block  
    72.             e.printStackTrace();  
    73.         }  
    74.     }  
    75.   
    76.     /** 
    77.      * 接收数据并打印出来 
    78.      *  
    79.      * @param socket 
    80.      * @throws IOException 
    81.      */  
    82.     public static void displayReciveInfo(DatagramSocket socket)  
    83.             throws IOException {  
    84.         byte[] buffer = new byte[1024];  
    85.         DatagramPacket packet = new DatagramPacket(buffer, buffer.length);  
    86.         socket.receive(packet);  
    87.   
    88.         byte data[] = packet.getData();// 接收的数据  
    89.         InetAddress address = packet.getAddress();// 接收的地址  
    90.         System.out.println("接收的文本:::" + new String(data));  
    91.         System.out.println("接收的ip地址:::" + address.toString());  
    92.         System.out.println("接收的端口::" + packet.getPort()); // 9004  
    93.     }  
    94.   
    95.     public static void main(String[] args) {  
    96.         new Thread() {  
    97.             @Override  
    98.             public void run() {  
    99.                 recive();  
    100.             }  
    101.         }.start();  
    102.   
    103.         new Thread() {  
    104.             @Override  
    105.             public void run() {  
    106.                 send();  
    107.             }  
    108.         }.start();  
    109.   
    110.     }  
    111. }  

    思考 

    1.为啥用线程去启动recive()方法和send()方法

    2.如果不用线程启动会有什么样的效果,请解释?

    3.如果把recive方法和send分别写在两个类中(一个发送者的类,一个接受者的类),应该先运行那个类?

    下面是按照思考3封装到两个类中一个是发送者,另一个是接受者考虑3的问题:

    1.发送者

    1. package net;  
    2.   
    3. import java.io.IOException;  
    4. import java.net.DatagramPacket;  
    5. import java.net.DatagramSocket;  
    6. import java.net.InetAddress;  
    7. import java.net.SocketException;  
    8.   
    9. public class Sender {  
    10.   
    11.     // 发送者--->客户端 客户端--->发送者  
    12.     // 发送者发给客户端数据,客户端返回数据给发送者  
    13.     public static void send() {  
    14.         System.out.println("---send----");  
    15.         // 发送端  
    16.         try {  
    17.             // 创建发送方的套接字 对象 采用9004默认端口号  
    18.             DatagramSocket socket = new DatagramSocket(9004);  
    19.             // 发送的内容  
    20.             String text = "hi 红军!";  
    21.             byte[] buf = text.getBytes();  
    22.             // 构造数据报包,用来将长度为 length 的包发送到指定主机上的指定端口号。  
    23.             DatagramPacket packet = new DatagramPacket(buf, buf.length,  
    24.                     InetAddress.getByName("172.22.67.6"), 9001);  
    25.             // 从此套接字发送数据报包  
    26.             socket.send(packet);  
    27.             // 接收,接收者返回的数据  
    28.             displayReciveInfo(socket);  
    29.             // 关闭此数据报套接字。  
    30.             socket.close();  
    31.         } catch (SocketException e) {  
    32.             e.printStackTrace();  
    33.         } catch (IOException e) {  
    34.             // TODO Auto-generated catch block  
    35.             e.printStackTrace();  
    36.         }  
    37.     }  
    38.   
    39.     /** 
    40.      * 接收数据并打印出来 
    41.      *  
    42.      * @param socket 
    43.      * @throws IOException 
    44.      */  
    45.     public static void displayReciveInfo(DatagramSocket socket)  
    46.             throws IOException {  
    47.         byte[] buffer = new byte[1024];  
    48.         DatagramPacket packet = new DatagramPacket(buffer, buffer.length);  
    49.         socket.receive(packet);  
    50.   
    51.         byte data[] = packet.getData();// 接收的数据  
    52.         InetAddress address = packet.getAddress();// 接收的地址  
    53.         System.out.println("接收的文本:::" + new String(data));  
    54.         System.out.println("接收的ip地址:::" + address.toString());  
    55.         System.out.println("接收的端口::" + packet.getPort()); // 9004  
    56.     }  
    57.   
    58.     public static void main(String[] args) {  
    59.         send();  
    60.     }  
    61. }  


    2.接受者

      1. package net;  
      2.   
      3. import java.io.IOException;  
      4. import java.net.DatagramPacket;  
      5. import java.net.DatagramSocket;  
      6. import java.net.InetAddress;  
      7. import java.net.SocketException;  
      8.   
      9. public class Receiver {  
      10.   
      11.     public static void recive() {  
      12.         System.out.println("---recive---");  
      13.         // 接收端  
      14.         try {  
      15.             //创建接收方的套接字 对象  并与send方法中DatagramPacket的ip地址与端口号一致  
      16.             DatagramSocket socket = new DatagramSocket(9001,  
      17.                     InetAddress.getByName("172.22.67.6"));  
      18.             //接收数据的buf数组并指定大小  
      19.             byte[] buf = new byte[1024];  
      20.             //创建接收数据包,存储在buf中  
      21.             DatagramPacket packet = new DatagramPacket(buf, buf.length);  
      22.             //接收操作  
      23.             socket.receive(packet);  
      24.             byte data[] = packet.getData();// 接收的数据  
      25.             InetAddress address = packet.getAddress();// 接收的地址  
      26.             System.out.println("接收的文本:::" + new String(data));  
      27.             System.out.println("接收的ip地址:::" + address.toString());  
      28.             System.out.println("接收的端口::" + packet.getPort()); // 9004  
      29.   
      30.             // 告诉发送者 我接收完毕了  
      31.             String temp = "我接收完毕了";  
      32.             byte buffer[] = temp.getBytes();  
      33.             //创建数据报,指定发送给 发送者的socketaddress地址  
      34.             DatagramPacket packet2 = new DatagramPacket(buffer, buffer.length,  
      35.                     packet.getSocketAddress());  
      36.             //发送  
      37.             socket.send(packet2);  
      38.             //关闭  
      39.             socket.close();  
      40.         } catch (SocketException e) {  
      41.             e.printStackTrace();  
      42.         } catch (IOException e) {  
      43.             // TODO Auto-generated catch block  
      44.             e.printStackTrace();  
      45.         }  
      46.     }  
      47.   
      48.     public static void main(String[] args) {  
      49.         recive();  
      50.     }  
      51. }  
  • 相关阅读:
    编写规范
    Springboot自定义starter
    H5 新增标签
    vagrant virtualbox docker
    h5 sessionStorage localStorage
    mysql 出现You can't specify target table for update in FROM clause错误的解决方法
    linux下利用nohup后台运行jar文件包程序
    JVM内存分配 Xms128m Xmx512m XX:PermSize=128m XX:MaxPermSize=512m
    Spring Boot:内置tomcat启动和外部tomcat部署总结
    Mysql DELETE 不能使用别名? 是我不会用!
  • 原文地址:https://www.cnblogs.com/Free-Thinker/p/7562062.html
Copyright © 2020-2023  润新知