您好,欢迎来电子发烧友网! ,新用户?[免费注册]

您的位置:电子发烧友网>电子百科>通信技术>

基于Android完整UDP通信模块的实现

2017年10月17日 15:32 作者: 用户评论(0

  TCP与UDP有什么差异?Android 设备上,一个手机通过热点连接另一个手机。这种场景下,完整的 UDP 通信模块应该考虑哪些方面,又应该如何优化。本文将围绕这些问题展开描述。

  TCP与UDP差异对比分析,UDP 在 Java 中的使用

v

  我们都知道,开发一个 Android 应用程序,目前大多数还是使用的是 Java 语言。在 Java 语言中怎么去使用 UDP 协议呢?

  其实 Socket 可以理解为对 TCP、UDP 协议在程序使用层面的封装,提供出一些 api 来供程序员调用开发,这就是 Socket 最表层的含义。

  在 Java 中,与 UDP 相关的类有 DatagramSocket、DatagramPacket 等,关于他们的使用,这里不着重介绍。

  好了,假设大家对他们的使用都已大概了解,可以正式开始本文的内容了。

  初始化一个 UDPSocket

  首先创建一个叫 UDPSocket 的类。

基于Android完整UDP通信模块的实现

  在构造方法里,我们进行下一些初始化操作,简单来说就是创建一个线程池,记录一下当前时间毫秒值,至于他们有什么用,再往下看:

 基于Android完整UDP通信模块的实现

  这里我们首先创建了一个 DatagramSocket 作为“客户端”,其实 UDP 本身没有客户端和服务端的概念,只有发送方和接收方的概念,我们把发送方暂时当成是一个客户端吧。

  创建 DatagramSocket 对象时,传入了一个端口号,这个端口号可以在一个范围内自己定义,表示这个 DatagramSocket 在此端口上监听数据。

  然后又创建了一个 DatagramPacket 对象,作为数据的接收包。

  最后调用 startSocketThread 启动发送和接收数据的线程。

基于Android完整UDP通信模块的实现

  首先 clientThread 线程的目的是调用 DatagramSocket receive 方法,因为 receive 方法是阻塞的,不能放在主线程,所以自然开启一个子线程了。receiveMessage 就是处理接受到的 UDP 数据报,我们先不看接受数据的这个方法,毕竟还没人发消息呢,自然就谈不上收了。

  心跳包保持“长连接”

  来到本文的第一个重点,我们都知道 UDP 本身没有连接的概念。在 Android 端应用 UDP 和 TCP 的场景是一个手机连接另一个手机的热点,二者处在同一局域网中。在二者并不知道对方的存在时,怎么才能发现彼此呢?

  通过心跳包的方式,双方都每隔一段时间发一个 UDP 包,如果对方接收到了,那就能知道对方的 ip,建立起通信了。

基于Android完整UDP通信模块的实现

  这段心跳的目的就是每隔十秒通过 sendMessage 发送一个消息,看看对方能不能收到。若对方收到消息,则刷新下 lastReceiveTime 的时间。

  这里我每隔十秒向对方发送了一个字符串。

基于Android完整UDP通信模块的实现

  这里就是发送一个消息的代码。最初在填写 DatagramPacket 的参数之时,我有一个疑问,那个 targetAddress 其实是自己的 ip 地址。问题来了,我填写了自己的 ip 地址和对方的端口,怎么可能找得到对方呢?你可能有一个疑惑 “192.168.43.255” 这个自己的 ip 地址是怎么来的,为什么要这么定义?

  首先 android 手机开启热点,可以理解成一个网关,有一个默认的 ip 地址:“192.168.43.1”

  这个 ip 地址不是我瞎编的一个,在 Android 源码之中,就是这么定义的:

  WifiStateMachine

 基于Android完整UDP通信模块的实现

  所以我是知道所谓打开热点一方的 ip 地址,而 UDP 发送消息时还有一个特性,就是发出去的消息,处在整个网关的设备是都可以接收到的,所以我自己的 ip 地址就定为了 “192.168.43.255”,所以这个 ip 地址和 “192.168.43.1” 在同一网关中,你发送的消息,它是可以收到的。

  至于怎么判断两个 ip 地址是否处在同一网段中:

  判断两个IP大小及是否在同一个网段中

  来做一个阶段总结:

  首先我们创建了一个发送端 DatagramSocket,启动了一个心跳程序,每间隔一段时间发送一个心跳包。

  因为我知道热点方的 ip 地址是默认的 “192.168.43.1”,并且 UDP 的特性就是发送的消息同一网段的设备都可以收到。所以发送方的 ip 地址定为了与热点一方处在同一网段的 “192.168.43.255”。

  事件与数据

  事件与数据这两个模块与业务就紧密相关了。

  先来说数据,双方发送的数据格式你们可以随意定义,当然我觉得还是定义成常规的 Json 格式就好。其中可以包含一些关键的事件字段:比如广播心跳包、收到心跳包给对方上线的应答包、超时的下线包、以及各种业务相关的数据等等。

  当然发送数据时是转换成二进制数组发送的。发送中文字符、图片等都没有问题,但是可能有一些细节需要注意,随时 google 一下就好了。

  再来说下事件:

  与业务无关的事件有哪些?

  比如:

  DatagramSocket.send 方法之后就是发送数据成功的事件;

  DatagramSocket.receive 方法之后是数据接收成功的事件;

  在心跳包发送一段时间,仍没有接到回信时,是连接超时的事件;

  与业务相关的事件就和我们上文提到的数据类型有关了,设备上线,心跳包回应等等。

  事件又如何发送出去,通知到各个页面呢?用 Listener、或者其他事件总线的三方库都没问题,看你自己选择了。

  处理接收的消息

 基于Android完整UDP通信模块的实现

  理接收消息时,有几个值得注意的点:

  receive 方法是阻塞的,没收到数据包时会一直阻塞,所以要放到子线程中;

  每次接收到消息之后,重新调用 receivePacket.setLength;

  收到消息刷新lastReceiveTime的值,暂停心跳包的发送;

  处理收到的数据具体在业务上就是刚才我们谈的发送数据的问题,视业务而定。

  “用户”的概念

  上文已经谈过了 UDP 的特性,假如一个手机已经开启了热点,若多个手机与他相连接,则多个手机发送的消息它都可以收到。如果发送方的端口与接收方的端口相同的话,甚至自己发的消息,自己都可以收到。这就很尴尬了,也就是说我们既要剔除自己发给自己的消息,也得区分不同手机发来的消息,这个时候就理应有一个“用户”的概念。

  创建 User 对象,有哪些属性可以看自己的业务,本文的例子就有 ip、imei、以及 softversion。

基于Android完整UDP通信模块的实现

  这里就不将所有的代码展开来看了。如果有了手机的 imei 号,那很容易就可以来做身份的区分,你既可以区分不同的发送方,也可以剔除掉自己发给自己的消息。当然如果需要更多的信息,可以按照自己的业务区分,将这些信息作为发送的 messge,通过 Socket 发送。

非常好我支持^.^

(0) 0%

不好我反对

(0) 0%

( 发表人:黄昊宇 )

      发表评论

      用户评论
      评价:好评中评差评

      发表评论,获取积分! 请遵守相关规定!