java UDP实现局域网广播

发布日期:2013-04-03 14:19:03

使用UDP协议进行信息的传输之前不需要建议连接。换句话说就是客户端向服务器发送信息,客户端只需要给出服务器的ip地址和端口号,然后将信息封装到一个待发送的报文中并且发送出去。至于服务器端是否存在,或者能否收到该报文,客户端根本不用管。

通常我们讨论的udp的程序都是一对一的单播程序。本章将讨论一对多的服务:广播(broadcast)、多播(multicast)。对于广播,网络中的所有主机都会接收一份数据副本。对于多播,消息只是发送到一个多播地址,网络知识将数据分发给哪些表示想要接收发送到该多播地址的数据的主机。总得来说,只有UDP套接字允许广播或多播。

UDP广播

广播UDP与单播UDP的区别就是IP地址不同,广播使用广播地址255.255.255.255,将消息发送到在同一广播网络上的每个主机。值得强调的是:本地广播信息是不会被路由器转发。当然这是十分容易理解的,因为如果路由器转发了广播信息,那么势必会引起网络瘫痪。这也是为什么IP协议的设计者故意没有定义互联网范围的广播机制。

广播地址通常用于在网络游戏中处于同一本地网络的玩家之间交流状态信息等。广播就不在写演示程序了,读者可以将ECHO程序的ip地址改为广播地址即可。

其实广播顾名思义,就是想局域网内所有的人说话,但是广播还是要指明接收者的端口号的,因为不可能接受者的所有端口都来收听广播。

UDP多播

同样的UDP多播也要指明接受者的端口号,而且与广播相似的是多播与单播之间的区别还在于地址。ipv4中的多播地址范围是:224.0.0.0到239.255.255.255。在JAVA中,多播一样十分好实现,要实现多播,就要用到MulticastSocket类,其实该类就是DatagramSocket的子类,在使用时除了多播自己的一些特性外,把它当做DatagramSocket类使用就可以了。


使用java 的UDP进行广播,要分两步走,首先要加入到广播组地址,其次要建立套接字传输信息

关于广播,涉及到MulticastSocket,他用于接收广播的信息,前提是要将它加入到广播组,组播的地址是保留的D类地址从224.0.0.0—239.255.255.255,而且一些地址有特定的用处如,224.0.0.0—244.0.0.255只能用于局域网中路由器是不会转发的,并且224.0.0.1是所有主机的地址,224.0.0.2所有路由器的地址,224.0.0.5所有ospf路由器的地址,224.0.13事PIMv2路由器的地址;239.0.0.0—239.255.255.255是私有地址(如192.168.x..x);224.0.1.0—238.255.255.255可以用与Internet上的。这里我们就选取230.0.0.1作为我们的广播地址。

此软件类似于飞鸽,用于局域网通信,每个客户端上线都要寻找局域网其他主机并把自己的信息和所有主机交换,以此模式更新。。。


import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.MulticastSocket;


/**   
 * <p>项目名称:UDPMulticastDemo </p>  
 * <p>类名称:lanDemo </p>
 * <p>类描述:   </p>
 * <p>创建人:wuzq,zhongqianhit@163.com </p> 
 * <p>创建时间:2012-6-7 上午10:21:06 </p>
 * <p>修改人:wuzq,zhongqianhit@163.com </p>  
 * <p>修改时间:2012-6-7 上午10:21:06 </p>
 * <p>修改备注: </p>
 * @version    
 **/


class lanSend {
// 广播地址
private static final String BROADCAST_IP = "230.0.0.1";// 广播IP
private static final int BROADCAST_INT_PORT = 40005; // 不同的port对应不同的socket发送端和接收端


MulticastSocket broadSocket;// 用于接收广播信息
DatagramSocket sender;// 数据流套接字 相当于码头,用于发送信息
InetAddress broadAddress;// 广播地址


public lanSend() {
try {


// 初始化
broadSocket = new MulticastSocket(BROADCAST_INT_PORT);
broadAddress = InetAddress.getByName(BROADCAST_IP);

sender = new DatagramSocket();
} catch (Exception e) {
// TODO: handle exception
System.out.println("*****lanSend初始化失败*****" + e.toString());
}
}


void join() {
try {
broadSocket.joinGroup(broadAddress); // 加入到组播地址,这样就能接收到组播信息
new Thread(new GetPacket()).start(); // 新建一个线程,用于循环侦听端口信息
} catch (Exception e) {
// TODO: handle exception
System.out.println("*****加入组播失败*****");
}


}


// 广播发送查找在线用户
void sendGetUserMsg() {
byte[] b = new byte[1024];
DatagramPacket packet; // 数据包,相当于集装箱,封装信息
try {
b = ("find@" + lanDemo.msg).getBytes();
packet = new DatagramPacket(b, b.length, broadAddress,
BROADCAST_INT_PORT); // 广播信息到指定端口
sender.send(packet);
System.out.println("*****已发送请求*****");
} catch (Exception e) {
System.out.println("*****查找出错*****");
}
}


// 当局域网内的在线机子收到广播信息时响应并向发送广播的ip地址主机发送返还信息,达到交换信息的目的
void returnUserMsg(String ip) {
byte[] b = new byte[1024];
DatagramPacket packet;
try {
b = ("retn@" + lanDemo.msg).getBytes();
packet = new DatagramPacket(b, b.length, InetAddress.getByName(ip),
BROADCAST_INT_PORT);
sender.send(packet);
System.out.print("发送信息成功!");
} catch (Exception e) {
// TODO: handle exception
System.out.println("*****发送返还信息失败*****");
}
}


// 当局域网某机子下线是需要广播发送下线通知
void offLine() {
byte[] b = new byte[1024];
DatagramPacket packet;
try {
b = ("offl@" + lanDemo.msg).getBytes();
packet = new DatagramPacket(b, b.length, broadAddress,
BROADCAST_INT_PORT);
sender.send(packet);
System.out.println("*****已离线*****");
} catch (Exception e) {
// TODO: handle exception
System.out.println("*****离线异常*****");
}
}


class GetPacket implements Runnable { // 新建的线程,用于侦听
public void run() {
DatagramPacket inPacket;


String[] message;
while (true) {
try {
inPacket = new DatagramPacket(new byte[1024], 1024);
broadSocket.receive(inPacket); // 接收广播信息并将信息封装到inPacket中
message = new String(inPacket.getData(), 0,
inPacket.getLength()).split("@"); // 获取信息,并切割头部,判断是何种信息(find--上线,retn--回答,offl--下线)


if (message[1].equals(lanDemo.ip))
continue; // 忽略自身
if (message[0].equals("find")) { // 如果是请求信息
System.out.println("新上线主机:" + " ip:" + message[1]
+ " 主机:" + message[2]);
returnUserMsg(message[1]);
} else if (message[0].equals("retn")) { // 如果是返回信息
System.out.println("找到新主机:" + " ip:" + message[1]
+ " 主机:" + message[2]);
} else if (message[0].equals("offl")) { // 如果是离线信息
System.out.println("主机下线:" + " ip:" + message[1]
+ " 主机:" + message[2]);
}


} catch (Exception e) {
// TODO: handle exception
System.out.println("线程出错 " + e);
}
}
}
}
}


public class lanDemo {
// 全局变量
public static String msg;
public static String ip;
public static String hostName;


public static void main(String[] args) { // 程序入口点
lanSend lSend;
try {
InetAddress addr = InetAddress.getLocalHost();
ip = addr.getHostAddress().toString();
hostName = addr.getHostName().toString();
msg = ip + "@" + hostName;
lSend = new lanSend();
lSend.join(); // 加入组播,并创建线程侦听

lSend.sendGetUserMsg(); // 广播信息,寻找上线主机交换信息
Thread.sleep(10000); // 程序睡眠5秒
lSend.offLine(); // 广播下线通知
} catch (Exception e) {
System.out.println("*****获取本地用户信息出错*****");
}
}


}