UDP 是User Datagram Protocol的简称, 中文名是用户数据包协议,是 OSI 参考模型中一种无连接的传输层协议,提供面向事务的简单不可靠信息传送服务,IETF RFC 768是UDP的正式规范。
UDP连接编程特点:
UDP 将数据及源和目的封装在数据包中,不需要建立连接,
每个数据包的大小限制在64K内
因无连接,是不可靠协议
不需要建立连接,速度快
聊天、网络视频会议、桌面共享 多用UDP
UDP建立连接的类是:DatagramSocket
发送和接收都需要先建立连接端,也叫做数据报套接字,通过DatagramSocket的实例化来实现,其中接收端在实例化端的时候需要指定一个侦听端口号,这个端口号必须和发送端的数据报包中封装的端口一致,而发送端也可以指定发送端口号,但是因为不是建立通信必须的,所以通常由系统自动分配。
数据报套接字是包投递服务的发送或接收点。每个在数据报套接字上发送或接收的包都是单独编址和路由的。从一台机器发送到另一台机器的多个包可能选择不同的路由,也可能按不同的顺序到达。
在 DatagramSocket 上总是启用 UDP 广播发送。为了接收广播包,应该将 DatagramSocket 绑定到通配符地址。在某些实现中,将 DatagramSocket 绑定到一个更加具体的地址时广播包也可以被接收。
例如:
DatagramSocket us=new DatagramSocket(null); //发送端
DatagramSocket ur=new DatagramSocket(8000);//接收端
数据报包:DatagramPacket 实现无连接的包投递服务
数据报包用来实现无连接包投递服务。每条报文仅根据该包中包含的信息从一台机器路由到另一台机器。从一台机器发送到另一台机器的多个包可能选择不同的路由,也可能按不同的顺序到达。不对包投递做出保证。 通过数据报包,我们可以非常方便的包装
UDP实现通信的三个要素:
IP地址 ,端口号, 数据;数据报包类包含了多个构造方法,其中最为常用的两个是:
DatagramPacket(byte[] buf, int length) 构造 DatagramPacket ,用来接收长度为 length 的数据包。 //接收端用,将接收到的数据包装到指定数组中以便后面的操作(后面的操作首先就是等待DatagramSocket对象调用receive方法,将接收缓冲区的数据接收到指定数据中,然后从该数组中提取相应的数据); |
DatagramPacket(byte[] buf, int length, InetAddress address, int port) 构造数据报包,用来将长度为 length 的包发送到指定主机上的指定端口号。 //发送端用,用于将接收端的IP地址,端口号,以及包含了需要发送数据的数组进行包装,备用(等待DatagramSocket对象调用SEND方法,) |
下面是分别建立的两个类,发送端实现固定发送一段文字,接收端实现接收指定端口上发来的数据,并提取发送端的IP地址和发送的数据显示在控制台上
发送端
package com.io;
import java.io.IOException;
import java.net.*;
public class UDPdemoTrans {
public static void main(String[] args) throws IOException {
DatagramSocket dsk=new DatagramSocket();
byte []data="udp is coming".getBytes();
DatagramPacket dgp=new DatagramPacket(data,data.length,InetAddress.getByName("127.0.0.1"),8000);
dsk.send(dgp);
dsk.close();
}}
接收端
package com.io;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
public class UDPdemoReceive{
public static void main(String[]args) throws IOException{
DatagramSocket ds=new DatagramSocket(8000);
byte []buf=new byte [1024];
DatagramPacket dp=new DatagramPacket(buf,buf.length);
ds.receive(dp);
//通过数据包的方法获取其中的数据
String ip=dp.getAddress().getHostAddress();
String data=new String(dp.getData(),0,dp.getLength());
int port=dp.getPort();
System.out.println(ip+"::"+data+"::"+port);
ds.close();
}}
小结示例:编写一个聊天程序,通过多线程技术实现,有收数据和发数据两个部分,这两部分需要同时执行,一个线程控制收,一个线程控制发送;
分析: 因为收发并不是同时发生,所以需要定义两个run方法,有两种方式可以实现多线程,一种是继承tread类,还有就是实现runanable方法,当只需要使用run方法的时候,显然实现接口更方便,为了逻辑清楚,把这两个run方法要封装到不同的类中。
package com.io;
import java.net.*;
import java.io.*;
public class UDPrun {
/**
* @param args
* @throws SocketException
*/
启动类:
public static void main(String[] args) throws SocketException {
// TODO Auto-generated method stub
DatagramSocket us=new DatagramSocket();
DatagramSocket ur=new DatagramSocket(8000);
new Thread(new UDPsend(us)).start();
new Thread(new UDPreceive(ur)).start();
}
}
发送端
class UDPsend implements Runnable{public DatagramSocket dss;public UDPsend( DatagramSocket dss) {this.dss = dss;}public void run(){try {// DatagramSocket us=new DatagramSocket();BufferedReader bfr=new BufferedReader(new InputStreamReader(System.in));byte[] buf=null;String bufs=null;while(true){bufs = bfr.readLine();buf=bufs.getBytes();if("over".equals(bufs))break;else{DatagramPacket dsp=new DatagramPacket(buf, buf.length, InetAddress.getByName("127.0.0.1"),8000);dss.send(dsp);
}}} catch (Exception e) {}}}
接收端
class UDPreceive implements Runnable{public DatagramSocket ur;public UDPreceive(DatagramSocket ur) {this.ur=ur;}
public void run(){byte[]bufr=new byte[1024];DatagramPacket dsr=new DatagramPacket(bufr, bufr.length);try {while(true){ur.receive(dsr);String ip=dsr.getAddress().getHostAddress();//获取主机IP地址String data=new String(dsr.getData()); //将接收到的数组数据封装成字符串int p=dsr.getPort(); //获取发送端口号System.out.println(ip+":"+data+":"+p);}} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}}}