2019独角兽企业重金招聘Python工程师标准>>>

创建一个DatagramSocket实例,并将该对象绑定到指定IP地址、指定端口。

通过上面三个构造器中的任意一个构造器即可创建一个DatagramSocket实例,通常在创建服务器时,创建指定端口的DatagramSocket实例--这样保证其他客户端可以将数据发送到该服务器。一旦得到了DatagramSocket实例之后,就可以通过如下两个方法来接收和发送数据。

receive(DatagramPacket p):从该DatagramSocket中接收数据报。

send(DatagramPacket p):以该DatagramSocket对象向外发送数据报。

从上面两个方法可以看出,使用DatagramSocket发送数据报时,DatagramSocket并不知道将该数据报发送到哪里,而是由DatagramPacket自身决定数据报的目的地。就像码头并不知道每个集装箱的目的地,码头只是将这些集装箱发送出去,而集装箱本身包含了该集装箱的目的地。

下面看一下DatagramPacket的构造器。

DatagramPacket(byte[] buf,int length):以一个空数组来创建DatagramPacket对象,该对象的作用是接收DatagramSocket中的数据。

DatagramPacket(byte[] buf, int length, InetAddress addr, int port):以一个包含数据的数组来创建DatagramPacket对象,创建该DatagramPacket对象时还指定了IP地址和端口--这就决定了该数据报的目的地。

DatagramPacket(byte[] buf, int offset, int length):以一个空数组来创建DatagramPacket对象,并指定接收到的数据放入buf数组中时从offset开始,最多放length个字节。

DatagramPacket(byte[] buf, int offset, int length, InetAddress address, int port):创建一个用于发送的DatagramPacket对象,指定发送buf数组中从offset开始,总共length个字节。

当Client/Server程序使用UDP协议时,实际上并没有明显的服务器端和客户端,因为两方都需要先建立一个DatagramSocket对象,用来接收或发送数据报,然后使用DatagramPacket对象作为传输数据的载体。通常固定IP地址、固定端口的DatagramSocket对象所在的程序被称为服务器,因为该DatagramSocket可以主动接收客户端数据。

在接收数据之前,应该采用上面的第一个或第三个构造器生成一个DatagramPacket对象,给出接收数据的字节数组及其长度。然后调用DatagramSocket 的receive()方法等待数据报的到来,receive()将一直等待(该方法会阻塞调用该方法的线程),直到收到一个数据报为止。如下代码所示:

// 创建一个接收数据的DatagramPacket对象
DatagramPacket packet=new DatagramPacket(buf, 256);
// 接收数据报
socket.receive(packet);

在发送数据之前,调用第二个或第四个构造器创建DatagramPacket对象,此时的字节数组里存放了想发送的数据。除此之外,还要给出完整的目的地址,包括IP地址和端口号。发送数据是通过DatagramSocket的send()方法实现的,send()方法根据数据报的目的地址来寻径以传送数据报。如下代码所示:

// 创建一个发送数据的DatagramPacket对象
DatagramPacket packet = new DatagramPacket(buf, length, address, port);
// 发送数据报
socket.send(packet);

使用DatagramPacket接收数据时,会感觉DatagramPacket设计得过于烦琐。开发者只关心该DatagramPacket能放多少数据,而DatagramPacket是否采用字节数组来存储数据完全不想关心。但Java要求创建接收数据用的DatagramPacket时,必须传入一个空的字节数组,该数组的长度决定了该DatagramPacket能放多少数据,这实际上暴露了DatagramPacket的实现细节。接着DatagramPacket又提供了一个getData()方法,该方法又可以返回Datagram Packet对象里封装的字节数组,该方法更显得有些多余--如果程序需要获取DatagramPacket里封装的字节数组,直接访问传给 DatagramPacket构造器的字节数组实参即可,无须调用该方法。

当服务器端(也可以是客户端)接收到一个DatagramPacket对象后,如果想向该数据报的发送者"反馈"一些信息,但由于UDP协议是面向非连接的,所以接收者并不知道每个数据报由谁发送过来,但程序可以调用DatagramPacket的如下3个方法来获取发送者的IP地址和端口。

InetAddress getAddress():当程序准备发送此数据报时,该方法返回此数据报的目标机器的IP地址;当程序刚接收到一个数据报时,该方法返回该数据报的发送主机的IP地址。

int getPort():当程序准备发送此数据报时,该方法返回此数据报的目标机器的端口;当程序刚接收到一个数据报时,该方法返回该数据报的发送主机的端口。

SocketAddress getSocketAddress():当程序准备发送此数据报时,该方法返回此数据报的目标SocketAddress;当程序刚接收到一个数据报时,该方法返回该数据报的发送主机的SocketAddress。

getSocketAddress()方法的返回值是一个SocketAddress对象,该对象实际上就是一个IP地址和一个端口号。也就是说,SocketAddress对象封装了一个InetAddress对象和一个代表端口的整数,所以使用SocketAddress对象可以同时代表IP地址和端口。

http://book.51cto.com/art/201203/322542.htm

17.4.2 使用DatagramSocket发送、接收数据(2)

下面程序使用DatagramSocket实现了Server/Client结构的网络通信。本程序的服务器端使用循环1000次来读取DatagramSocket中的数据报,每当读取到内容之后便向该数据报的发送者送回一条信息。服务器端程序代码如下。

程序清单:codes\17\17.4\UdpServer.java

public class UdpServer{public static final int PORT = 30000;// 定义每个数据报的最大大小为4KBprivate static final int DATA_LEN = 4096;// 定义接收网络数据的字节数组    byte[] inBuff = new byte[DATA_LEN];// 以指定字节数组创建准备接收数据的DatagramPacket对象private DatagramPacket inPacket =new DatagramPacket(inBuff , inBuff.length);// 定义一个用于发送的DatagramPacket对象private DatagramPacket outPacket;// 定义一个字符串数组,服务器端发送该数组的元素String[] books = new String[]{"疯狂Java讲义","轻量级Java EE企业应用实战","疯狂Android讲义","疯狂Ajax讲义"};public void init()throws IOException{try(// 创建DatagramSocket对象DatagramSocket socket = new DatagramSocket(PORT)){// 采用循环接收数据for (int i = 0; i < 1000 ; i++ ){// 读取Socket中的数据,读到的数据放入inPacket封装的数组里socket.receive(inPacket);// 判断inPacket.getData()和inBuff是否是同一个数组System.out.println(inBuff == inPacket.getData());// 将接收到的内容转换成字符串后输出System.out.println(new String(inBuff, 0 , inPacket.getLength()));// 从字符串数组中取出一个元素作为发送数据byte[] sendData = books[i % 4].getBytes();// 以指定的字节数组作为发送数据,以刚接收到的DatagramPacket的// 源SocketAddress作为目标SocketAddress创建DatagramPacketoutPacket = new DatagramPacket(sendData, sendData.length , inPacket.getSocketAddress());// 发送数据socket.send(outPacket);}}}public static void main(String[] args)throws IOException{new UdpServer().init();}
}

上面程序中的粗体字代码就是使用DatagramSocket发送、接收DatagramPacket的关键代码,该程序可以接收1000个客户端发送过来的数据。

客户端程序代码也与此类似,客户端采用循环不断地读取用户键盘输入,每当读取到用户输入的内容后就将该内容封装成DatagramPacket数据报,再将该数据报发送出去;接着把DatagramSocket中的数据读入接收用的DatagramPacket中(实际上是读入该DatagramPacket所封装的字节数组中)。客户端程序代码如下。

17.4.2 使用DatagramSocket发送、接收数据(3)

程序清单:codes\17\17.4\UdpClient.java

public class UdpClient{    // 定义发送数据报的目的地public static final int DEST_PORT = 30000;public static final String DEST_IP = "127.0.0.1";// 定义每个数据报的最大大小为4KBprivate static final int DATA_LEN = 4096;// 定义接收网络数据的字节数组byte[] inBuff = new byte[DATA_LEN];// 以指定的字节数组创建准备接收数据的DatagramPacket对象private DatagramPacket inPacket =new DatagramPacket(inBuff , inBuff.length);// 定义一个用于发送的DatagramPacket对象private DatagramPacket outPacket = null;public void init()throws IOException{try(// 创建一个客户端DatagramSocket,使用随机端口DatagramSocket socket = new DatagramSocket()){// 初始化发送用的DatagramSocket,它包含一个长度为0的字节数组outPacket = new DatagramPacket(new byte[0] , 0, InetAddress.getByName(DEST_IP) , DEST_PORT);// 创建键盘输入流Scanner scan = new Scanner(System.in);// 不断地读取键盘输入while(scan.hasNextLine()){// 将键盘输入的一行字符串转换成字节数组byte[] buff = scan.nextLine().getBytes();// 设置发送用的DatagramPacket中的字节数据outPacket.setData(buff);// 发送数据报socket.send(outPacket);// 读取Socket中的数据,读到的数据放在inPacket所封装的字节数组中socket.receive(inPacket);System.out.println(new String(inBuff , 0, inPacket.getLength()));}}}public static void main(String[] args)throws IOException{new UdpClient().init();}
}

上面程序中的粗体字代码同样也是使用DatagramSocket发送、接收DatagramPacket的关键代码,这些代码与服务器端代码基本相似。而客户端与服务器端的唯一区别在于:服务器端的IP地址、端口是固定的,所以客户端可以直接将该数据报发送给服务器端,而服务器端则需要根据接收到的数据报来决定"反馈"数据报的目的地。

读者可能会发现,使用DatagramSocket进行网络通信时,服务器端无须也无法保存每个客户端的状态,客户端把数据报发送到服务器端后,完全有可能立即退出。但不管客户端是否退出,服务器端都无法知道客户端的状态。

当使用UDP协议时,如果想让一个客户端发送的聊天信息被转发到其他所有的客户端则比较困难,可以考虑在服务器端使用Set集合来保存所有的客户端信息,每当接收到一个客户端的数据报之后,程序检查该数据报的源SocketAddress是否在Set集合中,如果不在就将该SocketAddress添加到该Set集合中。这样又涉及一个问题:可能有些客户端发送一个数据报之后永久性地退出了程序,但服务器端还将该客户端的SocketAddress保存在Set集合中……总之,这种方式需要处理的问题比较多,编程比较烦琐。幸好Java为UDP协议提供了MulticastSocket类,通过该类可以轻松地实现多点广播。

Socket之UDP套接字

UDP套接字:UDP套接字的使用是通过DatagramPacket类和DatagramSocket类,客户端和服务器端都是用DatagramPacket类来接收数据,使用DatagramSocket类来发送数据。

UDP客户端:也是主要执行三个步骤。

1.创建DatagramSocket实例;

2.使用DatagramSocket类的send()和receive()方法发送和接收DatagramPacket实例;

3.最后使用DatagramSocket类的close()方法销毁该套接字。

下面是例子,它主要执行三个步骤,

1.向服务器发送信息;

2.在receive()方法上最多阻塞等待3秒钟,在超时前若没有收到响应,则重发请求(最多重发5次);

3.关闭客户端。

//UDPEchoClientTimeout.java  
import java.net.DatagramSocket;  
import java.net.DatagramPacket;  
import java.net.InetAddress;  
import java.io.IOException;  
import java.io.InterruptedIOException;  
  
public class UDPEchoClientTimeout {  
  
    private static final int TIMEOUT = 3000;   // 设置超时为3秒  
    private static final int MAXTRIES = 5;     // 最大重发次数5次  
      
    public static void main(String[] args) throws IOException {  
      
        if ((args.length < 2) || (args.length > 3)) { // Test for correct # of args  
          throw new IllegalArgumentException("Parameter(s): <Server> <Word> [<Port>]");  
        }  
        InetAddress serverAddress = InetAddress.getByName(args[0]); // 服务器地址  
        // Convert the argument String to bytes using the default encoding  
        //发送的信息  
        byte[] bytesToSend = args[1].getBytes();  
      
        int servPort = (args.length == 3) ? Integer.parseInt(args[2]) : 7;  
      
        DatagramSocket socket = new DatagramSocket();  
      
        socket.setSoTimeout(TIMEOUT); // 设置阻塞时间  
      
        DatagramPacket sendPacket = new DatagramPacket(bytesToSend, // 相当于将发送的信息打包  
            bytesToSend.length, serverAddress, servPort);  
      
        DatagramPacket receivePacket =                              // 相当于空的接收包  
            new DatagramPacket(new byte[bytesToSend.length], bytesToSend.length);  
      
        int tries = 0;      // Packets may be lost, so we have to keep trying  
        boolean receivedResponse = false;  
        do {  
          socket.send(sendPacket);          // 发送信息  
          try {  
            socket.receive(receivePacket); // 接收信息  
      
            if (!receivePacket.getAddress().equals(serverAddress)) {// Check source  
              throw new IOException("Received packet from an unknown source");  
            }  
            receivedResponse = true;  
          } catch (InterruptedIOException e) { // 当receive不到信息或者receive时间超过3秒时,就向服务器重发请求  
            tries += 1;  
            System.out.println("Timed out, " + (MAXTRIES - tries) + " more tries...");  
          }  
        } while ((!receivedResponse) && (tries < MAXTRIES));  
      
        if (receivedResponse) {  
          System.out.println("Received: " + new String(receivePacket.getData()));  
        } else {  
          System.out.println("No response -- giving up.");  
        }  
        socket.close();  
    }  
}  
例子只是简单的向指定的服务器发送信息,并将发送的信息由服务器返回给指定客户端。

UDP服务器端:典型的UDP服务器要执行三个步骤,

1.创建一个指定了本地端口的DatagramSocket实例;

2.使用DatagramSocket的receive()方法接收一个来自客户端的DatagramPacket实例,而这个DatagramPacket实例在客户端创建时就包含了客户端的地址,这样我们就知道回复信息要发送到哪里了;

3.使用DatagramSocket类的send()和receive()方法来发送和接收DatagramPacket实例。

下面是例子

//UDPEchoServer.java  
import java.io.IOException;  
import java.net.DatagramPacket;  
import java.net.DatagramSocket;  
  
public class UDPEchoServer {  
  
    private static final int ECHOMAX = 255; // 发送或接收的信息最大字节数  
      
    public static void main(String[] args) throws IOException {  
      
        if (args.length != 1) { // Test for correct argument list  
          throw new IllegalArgumentException("Parameter(s): <Port>");  
        }  
      
        int servPort = Integer.parseInt(args[0]);  
      
        DatagramSocket socket = new DatagramSocket(servPort);  
        DatagramPacket packet = new DatagramPacket(new byte[ECHOMAX], ECHOMAX);  
      
        while (true) { // 不断接收来自客户端的信息及作出相应的相应  
          socket.receive(packet); // Receive packet from client  
          System.out.println("Handling client at " + packet.getAddress().getHostAddress() + " on port " + packet.getPort());  
          socket.send(packet); // 将客户端发送来的信息返回给客户端  
          packet.setLength(ECHOMAX);   
         // 重置packet的内部长度,因为处理了接收到的信息后,数据包的内部长度将被                                                         
         //设置为刚处理过的信息的长度,而这个长度可能比缓冲区的原始长度还要短,  
         //如果不重置,而且接收到的新信息长于这个内部长度,则超出长度的部分将会被截断,所以这点必须注意到。  
        }  
        /* NOT REACHED */  
    }  
}

例子只是简单地将客户端发送过来的信息再回复给客户端,服务器端会不断地receive来自客户端的信息,如果receive不到任何客户端请求,则将会进入阻塞状态,直到receive到有客户端请求位置。

转载于:https://my.oschina.net/u/2971326/blog/896959

使用DatagramSocket发送、接收数据(Socket之UDP套接字)相关推荐

  1. socket网络编程套接字TCP/UDP两种方式详解

    目录 准备知识 源IP地址和目的IP地址 端口号与进程ID 传输层协议--TCP 传输层协议--UCP 网络字节序 socket套接字介绍 概念 常见的三种socket socket编程常见API s ...

  2. python套接字编程_Python套接字编程(1)——socket模块与套接字编程

    在Python网络编程系列,我们主要学习以下内容: 5. 常见的Python异步编程框架 6. 协程在Python网络编程中的使用 本文介绍Python下的基本套接字编程,主要基于 socket 模块 ...

  3. 《网络编程》基本 UDP 套接字编程

    在前面文章中介绍了<UDP 协议>和<套接字数据传输>.UDP 协议和 TCP 协议不同,它是一种面向无连接.不可靠的传输层协议.在基于 UDP 套接字编程中,数据传输可用函数 ...

  4. socket:数据报套接字

    数据报套接字 一.什么是数据报套接字 二.基于流套接字的客户/服务器的工作流程 1.服务器 2.客户端 三.套接字接口 1.recvfrom()系统调用 2.sendto()系统调用 四.示例 一.什 ...

  5. 计网实验原理-TCP/UDP套接字编程

    计算机网络自顶向下结构--第7版 第二章实验,套接字编程 代码运行环境:window10,python 3.8.对于书上代码略作修改. 进程与计算机网络之间的接口 多数应用程序是由通信进程队组成的,每 ...

  6. 安卓Socket连接实现连接实现发送接收数据,openwrt wifi转串口连接单片机实现控制...

    安卓Socket连接实现连接实现发送接收数据,openwrt wifi转串口连接单片机实现控制 socket 连接采用流的方式进行发送接收数据,采用thread线程的方式. 什么是线程?  详细代码介 ...

  7. Android发送接收WiFi,安卓Socket连接实现连接实现发送接收数据,openwrt wifi转串口连接单片机实现控制,安卓openwrt...

    安卓Socket连接实现连接实现发送接收数据,openwrt wifi转串口连接单片机实现控制,安卓openwrt 安卓Socket连接实现连接实现发送接收数据,openwrt wifi转串口连接单片 ...

  8. 使用c#实现tcp的连接和发送接收数据

    最近有个小项目,需要调用装置的录波数据,使用tcp模式,在这里整理了下如何使用c#实现tcp连接并实现发送接收数据,分享出来. 我这里使用的tcpclient ,终端是tcpserver模式. 首先自 ...

  9. 求android 中串口的发送接收数据代码

    RT,求高手帮忙! 就是 /dev/ttyS0 和/dev/ttyS1 两个设备的通信问题.. 同求~ 这个是不是需要串口驱动啊?最近正在搞这个串口通信的案子,头疼 同样也没有搞出来,老是报:不能扫描 ...

最新文章

  1. python 删除文件某一行
  2. 论文浅尝 | 用增强学习进行推理:问答与知识库完善(KBC)
  3. c语言 函数的参数传递示例_C-用户定义的函数示例,没有参数,没有返回类型...
  4. 编译原理——实验叁——基于YACC的TINY语法分析器的构建
  5. axios+vue+springboot完成批量删除
  6. 718. 最长重复子数组
  7. hdu2063+hdu1083(最大匹配数)
  8. Android软键盘的删除键和activity返回冲突
  9. LVDS的接口电路设计
  10. ug筋板不能正确覆盖开放轮廓_安徽省六安市第一中学2017届高三上学期第二次月考地理【解析】...
  11. 如何在jsp中写一个弹窗
  12. 使用vue-admin-template搭建简单增删改查导入导出项目及CentOs服务器部署
  13. 手机停机照样可以上网,,教教你。。
  14. 机器周期、振荡周期、时钟周期、状态周期???
  15. html怎么设置下划线形状,科技常识:CSS如何给文字添加下划线样式
  16. java se 14 虚拟机规范
  17. grpc协议_gRPC和协议缓冲区简介
  18. php java集成_PHP和Java 集成开发详解分析 强强联合第1/4页
  19. 软件测试模型,瀑布模型,V模型,W模型
  20. 文件的打开方式(三种)

热门文章

  1. OpenGL画矩形,三角形,点(第一个图形学的小程序)
  2. ICLR 2019论文接收结果揭晓:24篇oral论文有没有你?
  3. 百度2016/2017秋招部分题目解析
  4. jaxb和dozer简介
  5. 应用打包Ubuntu下部署ScriptCase应用一例
  6. Bochs安装FreeDOS与调试
  7. JS的Event属性和方法
  8. JS学习之Node类型
  9. UVa 10359 - Tiling
  10. 分布式经典书籍--深入分布式缓存 从原理到实践