Java IO与NIO的UDP开发
来源:互联网 发布:淘宝九块九包邮在哪里 编辑:程序博客网 时间:2024/05/29 17:26
UDP协议与TCP的协议
UDP优点:速度快
--这里不作过多的累赘,估计都了略了解一二
先说说IO中的UDP:
1、java.util.DatagramSocket:负责接收和发送UDP数据报。
2、java.util.DatagramPacket:表示UDP数据报。
作为服务端:DatagramSocket必须与本地主机的ip和端口进行绑定,同时都可以接收任意远程的UDP数据,在DatagramPacket中它一定包含了远的主机ip和端口信息。
作为客户端:只是少了服务端的bind(int port),使用无参构造函数时,则就像TCP中使用任意一个空闲的UDP端口进行发送DatagramPacket。
DatagramPacket类包括以下属性:
data:表示数据报的数据缓冲区。
offset:表示数据报的数据缓冲区的起始位置。
length:表示数据报的长度。
address:对于用于发送的数据报,address属性表示数据报的目标地址。对于用于接收的数据报,address属性表示发送者的地址。
port:对于用于发送的数据报,address属性表示数据报的目标UDP端口。对于用于接收的数据报,port属性表示接收者的UDP端口。
它们的关系如下图:
3、UDP协议是无连接的协议
客户端的DatagramSocket与服务器端的DatagramSocket不存在一一对应关系,两者无需建立连接,就能交换数据报。
DatagramSocket提供了接收和发送数据报的方法:
public void receive(DatagramPacket dst)throws IOException //接收数据报
public void send(DatagramPacket src)throws IOException //发送数据报
4、Java IO的UDP服务端核心代码:
/** * 接收 */ @Test public void testRec() { try { InetSocketAddress address = new InetSocketAddress(InetAddress.getLocalHost(), port); DatagramSocket ds = new DatagramSocket(address); /** * 大多数的udp传输都是8k */ byte[] buffer = new byte[8 * 1024]; DatagramPacket dp = new DatagramPacket(buffer, buffer.length); //如果在接收端指定datagrampacket的address则意思是只接收指定ip和端口的udp数据报文 //DatagramPacket dp = new DatagramPacket(buffer, buffer.length, new InetSocketAddress("127.0.0.1", 4444)); while (true) { ds.receive(dp); System.out.println("dp.getLength:" + dp.getLength()); System.out.println(new String(dp.getData(),0, dp.getLength())); } } catch (UnknownHostException e) { e.printStackTrace(); } catch (SocketException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } }
5、Java IO的UDP客户端核心代码:
/** * 指定发送ip */ @Test public void testSendSpecifyIp() { try { // 指定目标地址 InetAddress address = InetAddress.getLocalHost(); // 无参构造函数,使用随机端口发送 DatagramSocket ds = new DatagramSocket(); String content = "Hi udp sever, testSendSpecifyIp!"; // 数据报文 DatagramPacket datagramPacket = new DatagramPacket(content.getBytes(), content.length(), address, port); System.out.println(ds.getLocalAddress()); // 发送 ds.send(datagramPacket); } catch (UnknownHostException e) { e.printStackTrace(); } catch (SocketException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } }
6、如果需要广播的发送则在指定目标ip中改成广播地址即可:
/** * 本地广播发送 */ @Test public void testSendLocalBrocast(){ // 本网段的广播地址 InetSocketAddress address = new InetSocketAddress("172.16.27.255",port); try { // 匿名udp socket DatagramSocket ds = new DatagramSocket(); String content = "Test sending local broacast... msg=testSendLocalBrocast !"; // 广播报文 DatagramPacket dp = new DatagramPacket(content.getBytes(), content.length(), address); ds.send(dp); } catch (Exception e) { e.printStackTrace(); } }
附:广播地址的计算(广播地址B与IP地址I,子网掩码M的关系为:B = (I & M)|~M)
第一步:获得网段(I&M)
第二步:将子网俺码按位取反
第三步:将1、2步进行相或
最简单的例子:比如宿舍中本机ip为192.168.1.100,子网掩码:255.255.255.0,求广播地址。如下图(为了直观图中使用十进制,实际需要转为二进制进行计算)
7、组播MulticastSocket
它继承DatagramSocket,所以它们是比较相像的,用法基本相同,不过需要在发送前加入组,不需要后需要离开组。
组播的概念,通过对比简单解释一下,避免过多的书面语:
(1)单播:即是基于一对一的思想进行传播,发送端需要指定ip与端口,接收端同样需要指定需要接收的ip,port(一对一,在DatagramPackage中指定);
(2)单播:即是基于多对一的思想进行传播,(不错依然是单播)多个发送端口需要指定ip与port,接收端不需要指定ip,port(多对一)。
(3)广播:即是基于一对多r的思想进行传播,先说说大大家都清楚的广播,顾名思义广播即是对网内所有的ip进行发送数据,网络对其中每一台主机发出的信号都进行无条件复制并转发,所有主机都可以接收到所有信息(不管你是否需要),缺点浪费资源。
(4)多播:只能说在需求状况下,它基于单播与广播之间,属于按需分配,只有都指定了组播地址和端口才能发送与接收。如果偏要说它属于“一对多还,多对多,抑者是一对一”,只能说它的思想还是(多对一对多)在电子商务领域有一个叫c2b2c的概念,中间那个“一”就是组播ip,发送端,根据组播ip发送,接收端根据组播ip接收。
附:http://www.360doc.com/content/07/0801/22/38435_648534.shtml
(5)看代码说话:
package com.jasic;import org.junit.Test;import java.net.*;/** * User: Jasic * Date: 12-12-5 */public class MultiUdpIo { /** * 组播的地址是保留的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上的。 */ public static String mul_ip1 = "239.0.0.1"; public static int mul_port1 = 4441; public static String mul_ip2 = "239.0.0.2"; //这里可以指定不同的端口,改的同时如果还需要接收到消息2,则需要改testMultiRec中multicastSocket的端口咯 public static int mul_port2 = mul_port1; /** * 组播发送测试 */ @Test public void testMultiSend(){ try { // 组播ip1 InetAddress a1 = InetAddress.getByName(mul_ip1); // 组播ip2 InetAddress a2 =InetAddress.getByName(mul_ip2); // socket组(随意绑定一个空闲udp端,这里也可以使用匿名构造函数) MulticastSocket multicastSocket = new MulticastSocket(); multicastSocket.joinGroup(a1); multicastSocket.joinGroup(a2); String content1 = "a1:你在哪里,亲。。。?"; String content2 = "a2:你死哪里去了,亲。。。?"; DatagramPacket packet1 = new DatagramPacket(content1.getBytes(),content1.getBytes().length,a1,mul_port1); DatagramPacket packet2 = new DatagramPacket(content2.getBytes(),content2.getBytes().length,a2,mul_port2); multicastSocket.send(packet1); System.out.println("数据[" + content1 + "]发送给组播地址:" + a1 + ":" + mul_port1 + ", 接收端需指定这个ip与端口"); multicastSocket.send(packet2); System.out.println("数据[" + content2 + "]发送给组播地址:" + a2 + ":" + mul_port2 + ", 接收端需指定这个ip与端口"); } catch (Exception e) { e.printStackTrace(); } } /** * 组播接收测试 */ @Test public void testMultiRec(){ try { // 组播ip1 InetAddress a1 = InetAddress.getByName(mul_ip1); // 组播ip2 InetAddress a2 =InetAddress.getByName(mul_ip2); // 一个MulticastSocket只能监听一个udp端口喔 // socket组(随意绑定一个空闲udp端,这里也可以使用匿名构造函数) MulticastSocket multicastSocket = new MulticastSocket(mul_port1); // 同一个端口监听两个组网地址 multicastSocket.joinGroup(a1); multicastSocket.joinGroup(a2); byte[] buffer = new byte[8*1024]; DatagramPacket packet = new DatagramPacket(buffer,buffer.length); while (true){ multicastSocket.receive(packet); System.out.println("接收到长度:" + packet.getLength()); System.out.println("接收内容:" + new String(packet.getData(),0,packet.getLength())); } } catch (Exception e) { e.printStackTrace(); } }}
8、Java NIO的UDP接收
public class UdpNio { private static int port = 4444; @Test public void rec() { Selector selector = null; DatagramChannel dc = null; try { selector = Selector.open(); dc = DatagramChannel.open(); InetSocketAddress address = new InetSocketAddress(port); dc.bind(address); dc.configureBlocking(false); dc.register(selector, SelectionKey.OP_READ); while (true) { int count = selector.select(); if (count <= 0) { continue; } Iterator<SelectionKey> it = selector.selectedKeys().iterator(); while (it.hasNext()) { SelectionKey key = it.next(); it.remove(); if (key.isReadable()) { DatagramChannel datagramChannel = (DatagramChannel) key.channel(); ByteBuffer temp = ByteBuffer.allocate(10); int l = 0; System.out.println("通道是否打开:" + datagramChannel.isOpen()); System.out.println("通道是否连接:" +datagramChannel.isConnected()); ByteBuffer bu = ByteBuffer.allocate(8 * 1024); System.out.println("数据包源地址" + datagramChannel.receive(bu)); int pos = bu.position(); bu.flip(); System.out.println(new String(bu.array(),0, pos)); //记录NotYetConnectedException System.out.println("这里不能使用read(temp),否则会抛NotYetConnectedException"); // 在这里我直接copy,TCP的测试代码,后来出错才发觉,UDP不能使用read,因为它根本不存连接概念。 //while ((l = datagramChannel.read(temp)) != -1 && l!=0) {// } } } } } catch (IOException e) { e.printStackTrace(); } }}
9、 其实在UDP的NIO中与TCP的基本相同,但记住一点,它是没连接的概念的。
DatagramChannel的write()与send()方法的区别在于:
(1)write()方法要求DatagramChannel已经建立连接,也就是说,程序在调用DatagramChannel的write()方法之前,要求先调用connect()方法使通道与特定的远程接收方连接。而send()方法则没有这一限制。
(2)在非阻塞模式下,write()方法不保证把ByteBuffer内的所有剩余数据作为一个数据报发送。假如ByteBuffer的剩余数据为r,实际发送的字节数为n,那么0<=n<=r。而send()方法总是把ByteBuffer内的所有剩余数据作为一个数据报发送。
DatagramChannel的read()与receive()方法的区别在于:
read()方法要求DatagramChannel已经建立连接,也就是说,程序在调用DatagramChannel的read()方法之前,要求先调用connect()方法使通道与特定的远程发送方连接。
而receive()方法则没有这一限制。
- Java IO与NIO的UDP开发
- Java NIO:IO与NIO的区别
- Java NIO:IO与NIO的区别
- Java NIO与IO
- Java NIO与IO
- Java NIO与IO
- Java NIO与IO
- Java NIO与IO
- Java NIO 与 IO
- Java NIO与IO
- Java NIO与IO
- Java NIO与IO
- java IO与NIO
- Java IO与NIO
- java NIO与IO
- Java NIO与IO
- Java NIO 与 IO
- Java NIO与IO
- Linux文件遍历
- 文件的上传、下载及删除方法
- Js操作表格-对表格TR的添加/删除/拷贝
- [objective-c] ARC 补充
- PHP5 在调用 JAVA WebService 时遇到的各种问题及解决方法(二)
- Java IO与NIO的UDP开发
- Linux Shell 通配符、元字符、转义符使用实例介绍
- Computer Vision重要期刊论文
- LDD3-HELLO world.ko
- Myeclipse : 修改jsp模板
- object-c 选择结构和基本的C语言特征 第九天
- 研究生应该收藏的十八大搜索引擎
- C语言实现urlencode和decode
- ORACLE sql执行过程