UDP
Java的UDP实现分为两个类:DatagramPacket类和DatagramSocket类。DatagramPacket类将数据字节填充到UDP包中,这称为数据报,由你来解析接收到的数据报。DatagramSocket类则是可以收发数据报。当发送数据的时候,要将数据放到DatagramPacket中,使用DatagramSocket来发送这个数据报。当接收数据的时候,可以从DatagramSocket中接收一个DatagramPacket对象,然后检查该对象的内容
1、DatagramPacket类
1.1、构造函数
public DatagramPacket(byte buf[], int length)
public DatagramPacket(byte buf[], int offset, int length)
public DatagramPacket(byte buf[], int length,InetAddress address, int port)
public DatagramPacket(byte buf[], int length, SocketAddress address)
public DatagramPacket(byte buf[], int offset, int length,InetAddress address, int port)
public DatagramPacket(byte buf[], int offset, int length, SocketAddress address)
DatagramPacket是UDP使用的数据包,它会包含要发送或者接受的数据,以及相应的地址和端口信息。上面6个构造函数,前两个只传入byte数组,表示要发送的数据或者要接受数据的数组。后四个构造函数除了填入数据外,还填入数据报发往的地址和端口。接受数据一般用前两个构造函数,发送数据一般来说用后四个构造函数:
byte[] data = "hello world".getBytes("UTF-8");
InetAddress ia = InetAddress.getByName("www.example.com");
int port = 22;
DatagramPacket dp = new DatagramPacket(data,data.length,ia,port);
//....发送数据
1.2、获取数据报信息
a)、public synchronized InetAddress getAddress()
这个方法返回一个InetAddress对象。如果数据报是远程服务器发过来的,则这个InetAddress对象包含远程服务器地址,如果这个数据报是本地创建的,则InetAddress表示要发往的远程服务器的地址。
b)、public synchronized int getPort()
这个方法返回一个整数,表示远程端口。如果数据报是远程服务器发过来的,这这个端口是远程服务器的端口,如果这个数据报是本地创建的,则这个端口表示要发往的远程服务器的端口。
c)、public synchronized SocketAddress getSocketAddress()
这个方法返回一个SocketAddress对象,其中包含了远程服务器的IP和端口。如果数据报是远程服务器发过来的,则这个SocketAddress对象包含远程服务器地址和端口,如果这个数据报是本地创建的,则SocketAddress表示要发往的远程服务器的地址和端口。
d)、public synchronized byte[] getData()
这个方法返回一个byte数组其中包含数据报中的数据。如果数据报是远程服务器发过来的,则这些数据是远程服务器的响应数据,如果这个数据报是本地创建的,则这些数据是本地对远程服务器的请求数据。
e)、public synchronized int getLength()
这个方法返回数据报中数据的字节数。它和getData().length不一定相等,因为getData()返回的字节数组,可能会在末尾被填充一些初始化数据,真正的有效数据的长度以本方法返回的为准。
f)、public synchronized int getOffset()
这个方法返回数据报中有效数据的开始位置。所以,获取数据报中的有效数据应该如下:
DatagramPacket dp = new DatagramPacket(new byte[1024],1024);
socket.receive(dp);
String result = new String(dp.getData(),dp.getOffset(),dp.getLength(),"UTF-8");
1.3、设置数据报信息
a)、public synchronized void setAddress(InetAddress iaddr)
这个方法用来修改数据报发往的地址。当你要发给多个主机的时候,可以创建一个DatagramPacket,然后修改地址后发送,而不是创建多个DatagramPacket。
b)、public synchronized void setData(byte[] buf)
该方法用来改变数据内容,比如对同一个远程主机发送两个不同的数据,则可以创建一个DatagramPacket,然后修改内容后发送。
c)、public synchronized void setData(byte[] buf, int offset, int length)
这个方法指定一个数据中的部分范围的数据,作为数据报的发送内容。
d)、public synchronized void setLength(int length)
这个方法会改变数据报中实际需要发送或接受的数据长度,超过数据长度的数据不会被处理。
e)、public synchronized void setPort(int iport)
这个方法用于修改数据报要发往的端口。
f)、public synchronized void setSocketAddress(SocketAddress address)
这个方法用于同时修改数据报要发往的地址和端口。
2、DatagramSocket类
要发送DatagramPacket,必须打开一个DatagramSocket,这个socket会绑定到一个本地端口,在这个端口上监听入站数据。这个端口也会放置在出站数据报的首部中。也就是说,DatagramSocket不区分客户端和服务端,它既监听数据,也发送数据。
2.1、构造函数
public DatagramSocket() throws SocketException
public DatagramSocket(int port) throws SocketException
public DatagramSocket(int port, InetAddress laddr) throws SocketException
public DatagramSocket(SocketAddress bindaddr) throws SocketException
第一个构造函数在匿名本地端口打开一个socket。第二个构造函数在指定端口打开一个socket。后两个构造函数在指定的本地地址和端口打开socket,只有当本地有多个ip的时候才会用这种构造函数。注意,UDP的端口和TCP的端口是不冲突的,一个被TCP使用的端口,同时也可以被UDP使用。
2.2、发送数据
public void send(DatagramPacket p) throws IOException
一旦创建了DatagramSocket,就可以创建一个DatagramPacket,然后用send方法发送,根本不需要建立连接。
byte[] data = "hello world".getBytes("UTF-8");
InetAddress ia = InetAddress.getByName("www.example.com");
int port = 22;//远程服务器的端口
DatagramPacket dp = new DatagramPacket(data,data.length,ia,port);
DatagramSocket socket = new DatagramSocket();
socket.send(dp);
2.3、接受数据
public synchronized void receive(DatagramPacket p) throws IOException
一旦创建了DatagramSocket,就可以接受数据,这个方法是个阻塞方法,直到数据到达才会响应。
DatagramPacket dp = new DatagramPacket(new byte[1024],1024);
//监听本地22端口的UDP数据报
DatagramSocket server = new DatagramSocket(22);
//接收数据报
server.receive(dp);
System.out.println(new String(dp.getData(),dp.getOffset(),dp.getLength(),"UTF-8"));
2.4、socket管理
public void connect(InetAddress address, int port)
public void connect(SocketAddress addr) throws SocketException
public void disconnect()
UDP是不存在连接的,但是DatagramSocket任然有connect方法,这个方法并不是用来建立连接,而是告诉底层,本客户端只向指定的远程服务器发送数据,其他服务器一概不处理。而disconnect方法则是取消这种限制。
2.5、关闭
public void close()
public boolean isClosed()DatagramSocket
不管是客户端还是服务端,都通过close方法关闭,要检查是否已经关闭,可以使用isClosed方法。
组播
TCP和UDP都是点对点的收发数据,组播是指将一个数据一次发给一个组,所有在这个组里的机器都可以获取这个请求。如果一台机器需要获取数据,首先应该加入这个组,如果不再感兴趣,则可以退出这个组。组播在架构上有点类似于监听者模式,组播地址类似于主题(topic),加入组播的机器类似于监听者,客户端首先发送数据给组播地址的路由器,然后路由器将数据发送给加入这个组的所有机器。组播在Java中以MulticastSocket类的形式存在,它继承了DatagramSocket,因此也是一种基于数据报的无连接的协议。
MulticastSocket
1、构造函数
public MulticastSocket() throws IOException
public MulticastSocket(int port) throws IOException
public MulticastSocket(SocketAddress bindaddr) throws IOException
这三个构造函数用于绑定本地地址和端口,从而创建一个socket。它和DatagramSocket并没有什么区别。
2、加入组
public void joinGroup(InetAddress mcastaddr) throws IOException
public void joinGroup(SocketAddress mcastaddr, NetworkInterface netIf)throws IOException
要加入一个组,必须传入一个InetAddress或者SocketAddress,而且这个地址必须是一个合法的组播地址,从224.0.0.0到239.255.255.255,如果不合法则会抛出一个IOException。一台机器可以加入多个组,所以这两个方法可以多次调用。下面举个例子:
MulticastSocket ms = new MulticastSocket(4322);
InetAddress ma1 = InetAddress.getByName("224.2.2.3");
InetAddress ma2 = InetAddress.getByName("224.2.2.4");
ms.joinGroup(ma1);
ms.joinGroup(ma2);
3、离开组
public void leaveGroup(InetAddress mcastaddr) throws IOException
public void leaveGroup(SocketAddress mcastaddr, NetworkInterface netIf)throws IOException
有加入组,就有离开组,上面两个方法需要传入特定的组播地址,从而离开一个特定的组,MulticastSocket没有提供离开所有组的方法,因此需要保存加入的组。
4、收发数据
由于MulticastSocket继承了DatagramSocket,因此它的收发数据本质上是依靠DatagramSocket的收发方法,在此不做讨论。
5、关闭
由于MulticastSocket继承了DatagramSocket,因此它的关闭和DatagramSocket一样。