使用DatagramSocket发送接收数据
DatagramSocket本身只是码头不维护状态不能产生IO流它的唯一作用就是接收和发送数据报Java使用DatagramPacket来代表数据报DatagramSocket接收和发送的数据都是通过DatagramPacket对象完成的
先看一下DatagramSocket的构造器
DatagramSocket()创建一个DatagramSocket实例并将该对象绑定到本机默认IP地址本机所有可用端口中随机选择的某个端口
DatagramSocket(int prot)创建一个DatagramSocket实例并将该对象绑定到本机默认IP地址指定端口
DatagramSocket(int port InetAddress laddr)创建一个DatagramSocket实例并将该对象绑定到指定IP地址指定端口
通过上面三个构造器中任意一个构造器即可创建一个DatagramSocket实例通常在创建服务器时我们创建指定端口的DatagramSocket实例这样保证其他客户端可以将数据发送到该服务器一旦得到了DatagramSocket实例之后就可以通过如下两个方法来接收和发送数据
receive(DatagramPacket p)从该DatagramSocket中接收数据报
send(DatagramPacket p)以该DatagramSocket对象向外发送数据报
从上面两个方法可以看出使用DatagramSocket发送数据报时DatagramSocket并不知道将该数据报发送到哪里而是由DatagramPacket自身决定数据报的目的就像码头并不知道每个集装箱的目的地码头只是将这些集装箱发送出去而集装箱本身包含了该集装箱的目的地
当Client/Server程序使用UDP协议时实际上并没有明显的服务器和客户端因为两方都需要先建立一个DatagramSocket对象用来接收或发送数据报然后使用DatagramPacket对象作为传输数据的载体通常固定IP固定端口的DatagramSocket对象所在的程序被称为服务器因为该DatagramSocket可以主动接收客户端数据
下面看一下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对象也多指定了一个offset参数
在接收数据前应该采用上面的第一个或第三个构造器生成一个DatagramPacket对象给出接收数据的字节数组及其长度然后调用DatagramSocket 的方法receive()等待数据报的到来receive()将一直等待(也就是说会阻塞调用该方法的线程)直到收到一个数据报为止如下代码所示
//创建接受数据的DatagramPacket对象
DatagramPacket packet=new DatagramPacket(buf )
//接收数据
socketreceive(packet)
发送数据之前调用第二个或第四个构造器创建DatagramPacket对象此时的字节数组里存放了想发送的数据除此之外还要给出完整的目的地址包括IP地址和端口号发送数据是通过DatagramSocket的方法send()实现的send()根据数据报的目的地址来寻径以传递数据报如下代码所示
//创建一个发送数据的DatagramPacket对象
DatagramPacket packet = new DatagramPacket(buf length address port)
//发送数据报
socketsend(packet)
当我们使用DatagramPacket来接收数据时会感觉DatagramPacket设计得过于烦琐对于开发者而言只关心该DatagramPacket能放多少数据而DatagramPacket是否采用字节数组来存储数据完全不想关心但Java要求创建接收数据用的DatagramPacket时必须传入一个空的字节数组该数组的长度决定了该DatagramPacket能放多少数据这实际上暴露了DatagramPacket的实现细节接着DatagramPacket又提供了一个getData()方法该方法又可以返回DatagramPacket对象里封装的字节数组该方法更显得有些多余如果程序需要获取DatagramPacket里封装的字节数组直接访问传给 DatagramPacket构造器的字节数组实参即可无须调用该方法
当服务器(也可以客户端)接收到一个DatagramPacket对象后如果想向该数据报的发送者反馈一些信息但由于UDP是面向非连接的所以接收者并不知道每个数据报由谁发送过来但程序可以调用DatagramPacket的如下三个方法来获取发送者的IP和端口
InetAddress getAddress()返回某台机器的 IP 地址当程序准备发送次数据报时该方法返回此数据报的目标机器的IP地址当程序刚刚接收到一个数据报时该方法返回该数据报的发送主机的IP地址
int getPort()返回某台机器的端口当程序准备发送此数据报时该方法返回此数据报的目标机器的端口当程序刚刚接收到一个数据报时该方法返回该数据报的发送主机的端口
SocketAddress getSocketAddress()返回完整SocketAddress通常由IP地址和端口组成当程序准备发送此数据报时该方法返回此数据报的目标SocketAddress;当程序刚刚接收到一个数据报时该方法返回该数据报是源SocketAddress
上面getSocketAddress方法的返回值是一个SocketAddress对象该对象实际上就是一个IP地址和一个端口号也就是说SocketAddress对象封装了一个InetAddress对象和一个代表端口的整数所以使用SocketAddress对象可以同时代表IP地址和端口
下面程序使用DatagramSocket实现Server/Client结构的网络通信程序本程序的服务器端使用循环次来读取DatagramSocket中的数据报每当读到内容之后便向该数据报的发送者送回一条信息服务器端代码如下
程序清单codes///UdpServerjava
public class UdpServer
{
public static final int PORT = ;
//定义每个数据报的最大大小为K
private static final int DATA_LEN = ;
//定义该服务器使用的DatagramSocket
private DatagramSocket socket = null;
//定义接收网络数据的字节数组
byte[] inBuff = new byte[DATA_LEN];
//以指定字节数组创建准备接受数据的DatagramPacket对象
private DatagramPacket inPacket =
new DatagramPacket(inBuff inBufflength)
//定义一个用于发送的DatagramPacket对象
private DatagramPacket outPacket;
//定义一个字符串数组服务器发送该数组的的元素
String[] books = new String[]
{
轻量级JEE企业应用实战
基于JEE的Ajax宝典
Struts权威指南
ROR敏捷开发最佳实践
};
public void init()throws IOException
{
try
{
//创建DatagramSocket对象
socket = new DatagramSocket(PORT)
//采用循环接受数据
for (int i = ; i < ; i++ )
{
//读取Socket中的数据读到的数据放在inPacket所封装的字节数组里
socketreceive(inPacket)
//判断inPacketgetData()和inBuff是否是同一个数组
Systemoutprintln(inBuff == inPacketgetData())
//将接收到的内容转成字符串后输出
Systemoutprintln(new String(inBuff
inPacketgetLength()))
//从字符串数组中取出一个元素作为发送的数据
byte[] sendData = books[i % ]getBytes()
//以指定字节数组作为发送数据以刚接受到的DatagramPacket的
//源SocketAddress作为目标SocketAddress创建DatagramPacket
outPacket = new DatagramPacket(sendData
sendDatalength inPacketgetSocketAddress())
//发送数据
socketsend(outPacket)
}
}
//使用finally块保证关闭资源
finally
{
if (socket != null)
{
socketclose()
}
}
}
public static void main(String[] args)
throws IOException
{
new UdpServer()init()
}
}
上面程序中粗体字代码就是使用DatagramSocket发送接收DatagramPacket的关键代码该程序可以接受个客户端发送过来的数据
[] []