Java UDP 组播实现

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/chen_shiqiang/article/details/51766836

源代码: https://git.oschina.net/chesian/Chat-UDP


UDP(User Datagram Protocol,用户数据报协议)是传输层的另一种协议,它比TCP具有更快传输速度,但是不可靠

UDP发送的数据单元称为UDP数据报,当网络传输UDP数据报时,无法保证数据报一定到达目的地,也无法保证各个数据报按发送的顺序到达目的地

当发送方先发送包含字符串“hello“的数据报,再发送包含字符串”everyone“的数据报,接收方有可能先接收到字符串”everyone“,再接收到字符串”hello“;也有可能什么数据也没有接收到,因为发送方发送的数据有可能在传输途中都丢失了。

在Java中,java.net.DatagramSocket负责接收和发送UDP数据报,java.net.DatagrampPacket表示UDP数据报。每个DatagramSocket与一个本地地址(包括本地主机的IP地址和本地UDP端口)绑定,每个DatagramSocket可以把UDP数据报发送给任意一个远程DatagaramSocket,也可以接收来自任意一个远程DatagramSocket的UDP数据报。在UDP数据报中包含了目的地址的信息,DatagramSocket更具该信息把数据报发送到目的地。

UDP协议是无连接的协议,客户端的DatagramSocket与服务器端的DatagramSocket不存在一一对应关系,两者无需建立连接,就能交换数据报。

DatagramSocket提供了接收和发送数据报的方法

public void receive(DatagramPacket dst)throws IOException  //接收数据报

public void send(DatagramPacket src)throws IOException  //发送数据报。

DtatgamPacket表示数据报,它的构造方法可以分为两类:

一类构造方法创建的DatagamPacket对象用来接收数据

还有一类构造方法创建的DatagramPacket对象用来发送数据

两类构造方法的主要区别是,用来发送数据的构造方法需要设定数据报到达的目的地址,而用于接收矩的构造方法无需设定地址。

n 用于接收数据的构造方法包括:

public DatagramPacket(byte[] data,int length)

public DatagramPacket(byte[] data,int offset, int length)

n 以上data参数用来存放接收到的数据,参数length指定要接收的字节数,参数offset指定在data中存放数据的起始位置,即data[offset]。如果没有设定参数offset,那么起始位置为data[0]。

n 用于发送数据的构造方法包括:

n public DatagramPacket(byte[] data,int offset, int length,InetAddress address,int port)

n public DatagramPacket(byte[] data,int offset,int length,SocketAddress address)

n public DatagramPacket(byte[] data,int length,InetAddress address,int port)

n public DatagramPacket(byte[] data,int length,SocketAddress address)

n 以上data参数中存放了要发送的数据,参数length指定要发送的字节数,参数offset指定要发送的数据在data中的起始置,即data[offset]。如果没有设定参数offset,那么起始位置为data[0]。

n 选择数据报大小的通用原则是:

n 如果网络非常不可靠,如分组无线电网络,则要选择较小的数据报,以减少传输中遭破坏的可能性

n 如果网络非常可靠,而且传输速度很快,就应当尽可能使用大的数据报。

n 对于多数网络,8K是一个很好的折衷方案

数据报中只能存放字节形式的数据。在发送方,需要把其他格式的数据转换为字节序列。可以利用byteArrayOutputStream和DataOutputStream来把其他格式的数据转换为字节序列。

例如以下longToByte()方法把long型数据转换为字节序列,存放在一个字节数组中,再将其返回:

public byte[] longToByte(long data) throws Exception {

      ByteOutputStream dos = new ByteArrayOutputStream();

   DataOutputStream dos = new DataOutputStream(dos);

   dos.writeLong(data);

dos.close();

return dao.toByteArray();

}

在接收方,需要把字节序列转换为原来格式的数据。可以利用ByteArrayInputSream和DataInputStream来把字节序列转换为原来格式的数据。

public long byteToLong(byte[] data) throws Exception {

      ByteArrayInputStream bai = new ByteArrayInputStream(data);

      for (int i=0;i<8;i++) {

           result[i] = dis.readLong();

}

return result;

}

n 同一个DatagramPacket对象可以被重用,用来多次发送或接收数据。

n DatagramSocket负责接收和发送数据报。

n 每个DatagramSocket对象都会与一个本地端口绑定,在此端口监听发送过来的数据报。

n 在客户程序中,一般由操作系统为DatagramSocket分配本地端口,这种端口也称为匿名端口;在服务器程序中,一般由程序显式的为DatagramSocket指定本地端口。

 

n DatagramSocket的send()方法负责发送一个数据报,该方法的定义如下:

public void send(DatagramPacket dp)throws IOException

n 值得注意的是,UDP协议提供不可靠的传输,如果数据报没有到达目的地,send()方法不会抛出任何异常,因此发送方程序无法知道数据报是否被接收方接收到,除非双方通过应用层的特定协议来确保接收方未收到数据报时,发送方能重发数据报。

n DatagramSocket的receive()方法负责接收一个数据报,该方法的定义如下:

public void receive(DatagramPacket datagramPacket)throws IOException

n 此方法从网络上接收一个数据报。如果网络上没有数据报,执行该方法的线程会进入阻塞状态,直到收到数据报为止。

管理连接

n 两个TCP Socket之间存在固定的连接关系,而一个DatagramSocket可以与其他任意一个DatagramSocket交换数据报。

n 在某些场合,一个DatagramSocket可能只希望与固定的另一个远程DatagramSocket通信。例如NFS客户只接收来自与之通信的服务器的数据报,再例如在网络游戏中,一个游戏网家只接收他的游戏搭档的数据报。

n 从JDK1.2开始,DatagramSocket添加了一些方法,利用这些方法,可以使一个DatagramSocket只能与另一个固定的DatagramSocket交换数据报:

n (1)public void connect(InetAddress host,int port)

n (2)public void disconnect()

n (3)public int getPort()

n (4)public InetAddress getInetAddress()

n (5)publicSocketAddress getRemoteSocketAddress()

 

网络数据传播按照接收者的数量,可分为以下三种方式:

单播: 提供点对点的通信

广播: 发送者每次发送的数据可以被广播范围内的所有接受者接收。

组播:发送者每次发送的数据可以被小组内的所有接收者接收。

 

组播组内的所有主机共享同一个地址,这种地址称为组播地址,组播地址是范围在224.0.0.0~239.255.255.255之间的IP地址。此范围内的所有地址的前4个二进制为都是“1110“。组播地址也被称为D类IP地址,与其它的A类、B类和C类地址相区别。组播组是开放的,主机可以在任何时候进入或离开组。

IANA(Internet Assigned Numbers Authority)组织负责分发永久组播地址。

组播与单播UDP的区别在于,前者必须考虑TTL(Time to live)值,它用IP数据包的头部的一个字节表示。

TTL通过限制IP包被丢弃前通过的路由器数目,来决定IP包的生存时间。IP包每通过一个路由器TTL就减一,当TTL变为0,这个包就被丢弃

TTL的一个作用是防止配置有误的路由器把包在路由器之间无限的来回传递,还有一个作用是限制组播的地理范围

java.net.MulticastSocket具有组播的功能,它是DatagramSocket的子类:

public class MulitcastSocket extends DatagramSocket和DataSocket一样,MulticastSocket业余DatagramPacket搭配使用,DatagramPacket用来接收和发送的组播数据报。

如果要接收组播数据报,只需要创建一个MulticastSocket,把它加入到组播组,就能接收发送到该组的组播数据

发送组播数据报与发送单播数据报非常相似,只需创建一个MulticastSocket,无需把它加入组播组(当然也可以把它加入到组播组),就能向一个组播组发送数据

 

与组播通信

MulticastSocket支持以下四种操作:

1)         加入到组播组:joinGroup方法

2)         想组中成员发送数据报:send()方法

3)         接收发送到组播组的数据报:receive()方法

4)         离开组播组: leaveGroup()方法

5)         MuliticastSocket类提供了两组用于设置和读取网络接口的方法。MuliticastSocket只会对该网络接口中的组收发组播数据:

//第1组方法

public void setInterface(InetAddress address)throws SocketException

public InetAddress getInterface()throws SocketException

//第2组方法

public void setNetworkInterface(NetworkInterface interface)throws SocketException

public NetworkInterface getNetworkInterface() throws SocketException

展开阅读全文

没有更多推荐了,返回首页