UDP不适合传输大数据,所以传输要尽量小。
UDP传输中可能会丢包,如果需要可能多次发送同一个包 保证包能安全到达;接收端可以对收到的包进行CRC校验,已确定是否收到同样的包。
package org.sl.udp.beans;
import java.net.DatagramPacket;
/**
* 处理udp请求的接口
* @author shanl
*
*/
public interface IUdpRequestHandler{
/**
* 解析请求数据包
* @param requestPack
*/
void parse(DatagramPacket requestPack);
}
package org.sl.udp.beans;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.PrintStream;
import java.util.Arrays;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
/**
* 传输包格式<br/>
* 具备头校验功能。
* @author shanl
*
*/
public class PacketFormat {
/**请求头,主要用于校验*/
public static final byte[] REQUEST_HEADER = {'S','H','A','N'};
private Properties prop = null;
/**
* 构造一个用于发送请求的包结构
*/
public PacketFormat(){
this.prop = new Properties();
}
/**
* 解析并从请求中加载数据
* @param buff 数据包
* @param offset 偏移量
* @param len 长度
* @return true:解析成功,false:解析失败
*/
public boolean parse(byte[] buff, int offset, int len){
boolean done = false;
ByteArrayInputStream dataCache = null;
ObjectInputStream objectIn = null;
ByteArrayInputStream propCache = null;
try {
dataCache = new ByteArrayInputStream(buff, offset, len);
byte[] d_header = new byte[REQUEST_HEADER.length];
dataCache.read(d_header);
//校验请求头
if(!Arrays.equals(d_header, REQUEST_HEADER)){
return false;
}
objectIn = new ObjectInputStream(dataCache);
//得到数据长度
short dataLen = objectIn.readShort();
byte[] propBys = new byte[dataLen];
objectIn.read(propBys);
propCache = new ByteArrayInputStream(propBys);
//加载数据
this.prop.load(propCache);
done = true;
} catch (Exception e) {
done = false;
}finally{
try{
if(null!=propCache)propCache.close();
}catch(Exception ex){}
try{
if(null!=objectIn)objectIn.close();
}catch(Exception ex){}
try{
if(null!=dataCache)dataCache.close();
}catch(Exception ex){}
}
return done;
}
/**
* 设置数据
* @param key
* @param value
*/
public void setProperty(String key, String value){
this.prop.setProperty(key, value);
}
/**
* 取数据
* @param key
* @return
*/
public String getProperty(String key){
return this.prop.getProperty(key,"");
}
/**
* 返回keyset
* @return
*/
public Set<Object> keySet(){
return this.prop.keySet();
}
/**
* 将内容转换为byte数组
* @return
*/
public byte[] toBytes(){
byte[] dataCacheBys = null;
ByteArrayOutputStream dataCache = null;
ObjectOutputStream dataOut = null;
StringBuilder propContent = new StringBuilder();
byte[] propBys = null;
Set<Map.Entry<Object, Object>>items = this.prop.entrySet();
for(Map.Entry<Object, Object> i: items){
propContent.append((String)i.getKey());
propContent.append("=");
propContent.append((String)i.getValue());
propContent.append("\n");
}
propBys = propContent.toString().getBytes();
try{
dataCache = new ByteArrayOutputStream();
dataCache.write(REQUEST_HEADER);
dataOut = new ObjectOutputStream(dataCache);
//写入数据长度
dataOut.writeShort(propBys.length);
//写入数据
dataOut.write(propBys, 0, propBys.length);
dataOut.flush();
dataCacheBys = dataCache.toByteArray();
}catch(Exception ex){
throw new RuntimeException(ex);
}finally{
try{
if(null!=dataOut) dataOut.close();
}catch(Exception ex){}
try{
if(null!=dataCache) dataCache.close();
}catch(Exception ex){}
}
return dataCacheBys;
}
/**
* 得到请求数据
* @param buff 数据缓存,缓存大小>=128
* @param offset 偏移量
* @return 包长度=请求头长度+2+数据
*/
public int getBytes(byte[] buff, int offset){
int len = 0;
// ByteArrayOutputStream propCache = null;
ByteArrayOutputStream dataCache = null;
ObjectOutputStream dataOut = null;
StringBuilder propContent = new StringBuilder();
byte[] propBys = null;
// try{
// propCache = new ByteArrayOutputStream(buff.length-REQUEST_HEADER.length-2);
// this.prop.list(new PrintStream(propCache));
// propBys = propCache.toByteArray();
// }catch(Exception ex){
// throw new RuntimeException(ex);
// }finally{
// try{
// if(null!=propCache)propCache.close();
// }catch(Exception ex){}
// }
//这段代码与上面这段功能相同,但Properties的list()会写入额外的字节
Set<Map.Entry<Object, Object>>items = this.prop.entrySet();
for(Map.Entry<Object, Object> i: items){
propContent.append((String)i.getKey());
propContent.append("=");
propContent.append((String)i.getValue());
propContent.append("\n");
}
propBys = propContent.toString().getBytes();
try{
//写入头
dataCache = new ByteArrayOutputStream(buff.length);
dataCache.write(REQUEST_HEADER);
len+=REQUEST_HEADER.length;
dataOut = new ObjectOutputStream(dataCache);
//写入数据长度
dataOut.writeShort(propBys.length);
len+=2;
//写入数据
dataOut.write(propBys, 0, propBys.length);
dataOut.flush();
byte[] dataCacheBys = dataCache.toByteArray();
System.arraycopy(dataCacheBys, 0, buff, offset, dataCacheBys.length);
len+=dataCacheBys.length;
}catch(Exception ex){
throw new RuntimeException(ex);
}finally{
try{
if(null!=dataOut) dataOut.close();
}catch(Exception ex){}
try{
if(null!=dataCache) dataCache.close();
}catch(Exception ex){}
}
return len;
}
/**
* 返回crc32校验码
* @return
*/
long crc32(){
CRC32 crc32 = new CRC32();
crc32.update(toBytes());
return crc32.getValue();
}
}
package org.sl.udp.client;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;
import java.util.Random;
import java.util.concurrent.BlockingDeque;
import java.util.concurrent.LinkedBlockingDeque;
/**
* UDP发送请求服务
* @author shanl
*
*/
public class UDPSenderService{
private BlockingDeque<RequestObject> requestPool = null;
private long intervalTime = 200L;
private DatagramSocket udpSender = null;
private boolean shutdown = true;
private Object lockObj = new Object();
public UDPSenderService(){
requestPool = new LinkedBlockingDeque<RequestObject>();
}
/**
*
* @param poolSize 请求缓冲池上限
* @param intervalTime 间隔时间
*/
public UDPSenderService(int poolSize,int intervalTime){
requestPool = new LinkedBlockingDeque<RequestObject>(poolSize);
this.intervalTime = intervalTime;
}
/**
* 过程
*/
void process() {
do{
long now = System.currentTimeMillis();
try{
synchronized(lockObj){
lockObj.wait(intervalTime);
if(!requestPool.isEmpty()){
send(now);
}
}
} catch (Exception e) {
e.printStackTrace();
}
}while(!shutdown);
}
// int c=0;
private void send(long time) throws IOException{
RequestObject ro = null;
try {
for(int i =0; i<10; i++){
ro = requestPool.poll();
// ro = requestPool.take();
// ro = requestPool.pollFirst();
if(null!=ro){
if(ro.getTime() <= time){
// System.out.println(c++);
udpSender.send(ro.getDataPacket());
}else{
requestPool.put(ro);
// requestPool.offerLast(ro);
}
}else{
break;
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 重复多送发送一个udp请求
* @param request 请求包
* @param repeatCount 重复次数
* @param interval 重复发送请求包间隔
*/
public void addRepeatingRequest(DatagramPacket request, int repeatCount, long interval){
addImmediateRequest(request);
// timingRequest(System.currentTimeMillis(), request);
byte[] reqData = new byte[request.getLength()-request.getOffset()];
System.arraycopy(request.getData(), request.getOffset(), reqData, request.getOffset(), request.getLength());
long now = System.currentTimeMillis();
DatagramPacket dpClone = null;
for(long i=0,nextTime=interval; i<repeatCount; i++, nextTime+=interval){
dpClone = new DatagramPacket(reqData, request.getOffset(), request.getLength());
dpClone.setSocketAddress(request.getSocketAddress());
addTimingRequest(now+nextTime, dpClone);
}
}
/**
* 添加一个定时的请求,在一个近似的时间执行发送.<br/>
* 如果这个请求为过期的请求,则会在下一个时间被执行.
* @param time
* @param request
*/
public void addTimingRequest(long time, DatagramPacket request){
try {
// requestPool.offerLast(new RequestObject(time, request), 300, TimeUnit.MILLISECONDS);
requestPool.put(new RequestObject(time, request));
// requestPool.offerLast(new RequestObject(time, request));
} catch (Exception e) {
e.printStackTrace();
}
synchronized(lockObj){
lockObj.notify();
}
// try {
// Thread.sleep(1);
// } catch (InterruptedException e) {
// }
}
/**
* 立即发送一个请求
* @param request
*/
public void addImmediateRequest(DatagramPacket request){
try{
udpSender.send(request);
}catch(Exception ex){
ex.printStackTrace();
}
}
/**
* 启动服务
*/
public void start(){
if(this.shutdown){
try {
if(null==udpSender){
udpSender = new DatagramSocket();
}
} catch (SocketException e1) {
e1.printStackTrace();
return;
}
try {
Thread t = new Thread(new ProcessService(),"UDPSenderService-"+new Random().nextInt(999));
t.start();
} catch (Exception e) {
throw new RuntimeException(e.getMessage());
}
this.shutdown = false;
}
}
/**
* 中止服务
*/
public void shutdown(){
this.shutdown = true;
try{
Thread.sleep(1000*10);
}catch(Exception ex){
}
try{
this.udpSender.disconnect();
}catch(Exception ex){
}
try{
this.udpSender.close();
}catch(Exception ex){
}
}
public void setDatagramSocket(DatagramSocket sender){
this.udpSender = sender;
}
private class ProcessService implements Runnable{
public void run(){
process();
}
}
static class RequestObject {
private Long time;
private DatagramPacket dataPacket = null;
public RequestObject(DatagramPacket dataPacket){
this.time = System.currentTimeMillis();
}
public RequestObject(Long time,DatagramPacket dataPacket){
this.time = time;
this.dataPacket = dataPacket;
}
public Long getTime(){
return time;
}
public DatagramPacket getDataPacket(){
return dataPacket;
}
}
}
package org.sl.udp.server;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetSocketAddress;
import org.sl.udp.beans.IUdpRequestHandler;
/**
* udp接收器
* @author shanl
*
*/
public class UDPReceptor implements Runnable{
private String hostname = "localhost";
private int port = 7777;
private int recePacketSize = 512;
private IUdpRequestHandler requestHandler = null;
/**
* 过程
*/
public void run() {
DatagramSocket udpRece = null;
DatagramPacket dataPack = null;
byte[] buff = null;
try{
udpRece = new DatagramSocket(new InetSocketAddress(this.hostname, this.port));
udpRece.setReceiveBufferSize(this.recePacketSize);
for(;;){
buff = new byte[this.recePacketSize];
dataPack = new DatagramPacket(buff, this.recePacketSize);
udpRece.receive(dataPack);
if(null!=requestHandler) requestHandler.parse(dataPack);
}
}catch(Exception ex){
ex.printStackTrace();
}
}
/**
* 注入请求处理
* @param requestHandler 请求处理
*/
public void setRequestHandler(IUdpRequestHandler requestHandler){
this.requestHandler = requestHandler;
}
/**
* 设置接收包大小
* @param udpPacketSize
*/
public void setRecePacketSize(int udpPacketSize){
this.recePacketSize = udpPacketSize;
}
public void setHostname(String hostname){
this.hostname = hostname;
}
public void setPort(int port){
this.port = port;
}
}
package org.sl.udp.demo;
import java.net.DatagramPacket;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import org.sl.udp.beans.PacketFormat;
import org.sl.udp.beans.IUdpRequestHandler;
public class DemoMultiThreadUDPRequestHandlerImpl implements IUdpRequestHandler{
private ExecutorService threadPool = null;
public void parse(DatagramPacket requestPack) {
threadPool.execute(new HandlerService(requestPack));
}
public void setThreadPool(ExecutorService threadPool){
this.threadPool = threadPool;
}
private static
class HandlerService extends Thread{
private DatagramPacket requestPack = null;
public HandlerService(DatagramPacket requestPack){
this.requestPack = requestPack;
}
public void run(){
try{
PacketFormat reqPack = new PacketFormat();
if(!reqPack.parse(requestPack.getData(),requestPack.getOffset(), requestPack.getLength())){
return;
}
Set<Object> keyset = reqPack.keySet();
System.out.println("value:"+reqPack.getProperty("value"));
System.out.println("====time:"+System.currentTimeMillis());
for(Object key: keyset){
System.out.println(key+":"+reqPack.getProperty((String)key) );
}
}catch(Exception ex){
ex.printStackTrace();
}
}
}
}
package org.sl.udp.demo;
import java.net.DatagramPacket;
import java.util.Set;
import org.sl.udp.beans.PacketFormat;
import org.sl.udp.beans.IUdpRequestHandler;
public class DemoUDPRequestHandlerImpl implements IUdpRequestHandler{
private DatagramPacket requestPack = null;
public void parse(DatagramPacket requestPack) {
this.requestPack = requestPack;
process();
}
private void process() {
try{
PacketFormat reqPack = new PacketFormat();
if(!reqPack.parse(requestPack.getData(),requestPack.getOffset(), requestPack.getLength())){
return;
}
Set<Object> keyset = reqPack.keySet();
synchronized(UDPReceptorDemo.iset){
UDPReceptorDemo.iset.add(Integer.parseInt(reqPack.getProperty("value")));
}
// System.out.println("value:"+reqPack.getProperty("value"));
// System.out.println("====time:"+System.currentTimeMillis());
// for(Object key: keyset){
// System.out.println(key+":"+reqPack.getProperty((String)key) );
// }
}catch(Exception ex){
ex.printStackTrace();
}
}
}
package org.sl.udp.demo;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.Executors;
import org.sl.udp.beans.IUdpRequestHandler;
import org.sl.udp.server.UDPReceptor;
public class UDPReceptorDemo {
public static void main(String[] args){
// t1();
t2();
t3();
}
public static Set<Integer> iset = new HashSet<Integer>(10000);
static void t3(){
for(;;){
System.out.println(iset.size());
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
static void t2(){
System.err.println("启动udp监听,端口7777...");
IUdpRequestHandler handlerImpl = new DemoUDPRequestHandlerImpl();
UDPReceptor rece = new UDPReceptor();
rece.setRequestHandler(handlerImpl);
rece.setHostname("192.168.2.23");
rece.setPort(7777);
Thread t = new Thread(rece,"udpRece_port7777");
t.start();
}
/**
* 已普通实例启动
*/
static void t1(){
System.err.println("启动udp监听,端口7777...");
DemoMultiThreadUDPRequestHandlerImpl handlerImpl = new DemoMultiThreadUDPRequestHandlerImpl();
handlerImpl.setThreadPool(Executors.newFixedThreadPool(5));
UDPReceptor rece = new UDPReceptor();
rece.setRequestHandler(handlerImpl);
rece.setHostname("192.168.2.23");
rece.setPort(7777);
rece.run();
}
}
package org.sl.udp.demo;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetSocketAddress;
import java.net.SocketException;
import java.util.Random;
import org.sl.udp.beans.PacketFormat;
import org.sl.udp.client.UDPSenderService;
public class UDPSenderServiceDemo {
public static void main(String[] args){
// t1();
// t2();
// t3();
t4();
}
static void t4(){
UDPSenderService udpSenderService = new UDPSenderService();
udpSenderService.start();
System.err.println("启动udp发送服务");
Random rand = new Random();
byte[] buff = null;
long time = System.currentTimeMillis();
DatagramPacket req = null;
PacketFormat pf = null;
for(int i=0,endi=300; i<endi; i++){
// time = (rand.nextInt(100)+1)*100L+System.currentTimeMillis();
pf = new PacketFormat();
pf.setProperty("msg", "test");
pf.setProperty("msg", "test");
pf.setProperty("type", "cpu");
pf.setProperty("value", ""+i);
buff = new byte[512];;
int packLen = pf.getBytes(buff, 0);
req = new DatagramPacket(buff,0,packLen);
// System.out.println("value:"+pf.getProperty("value"));
req.setSocketAddress(new InetSocketAddress("192.168.2.23", 7777) );
//有一定几率丢包
// for(int n=0; n<3; n++){
// udpSenderService.addImmediateRequest(req);
// }
//多次重发保证可靠性
// udpSenderService.addRepeatingRequest(req, 2, 1L);
//有一定几率丢包
udpSenderService.addTimingRequest(time, req);
// try {
// if(i%10==0){
// Thread.sleep(3);
// rand.setSeed(time);
// }
// } catch (Exception e) {
// }
}
try{
Thread.sleep(1000*60);
}catch(Exception ex){
}
udpSenderService.shutdown();
System.err.println("udp服务关闭.");
}
static void t3(){
UDPSenderService udpSenderService = new UDPSenderService();
udpSenderService.start();
System.err.println("启动udp发送服务");
Random rand = new Random();
byte[] buff = new byte[512];;
long time = 0;
for(int i=0,endi=3*100*100; i<endi; i++){
PacketFormat pf = new PacketFormat();
pf.setProperty("msg", "test");
pf.setProperty("msg", "test");
pf.setProperty("type", "cpu");
pf.setProperty("value", "33.05");
int packLen = pf.getBytes(buff, 0);
DatagramPacket data = new DatagramPacket(buff,0,packLen);
data.setSocketAddress(new InetSocketAddress("192.168.2.23", 7777) );
time = (rand.nextInt(4)+1)*1000L+System.currentTimeMillis();
// udpSenderService.timingRequest(time, data);
udpSenderService.addTimingRequest(time, data);
try {
if(i%50==0){
Thread.sleep(1);
rand.setSeed(time);
}
} catch (Exception e) {
}
}
try{
Thread.sleep(1000*10);
}catch(Exception ex){
}
udpSenderService.shutdown();
System.err.println("udp服务关闭.");
}
static void t2(){
UDPSenderService udpSenderService = new UDPSenderService();
try {
// InetSocketAddress serviceAddress = new InetSocketAddress("localhost", 7777);
// DatagramSocket udpSocket = new DatagramSocket(serviceAddress);
// udpSenderService.setDatagramSocket(udpSocket);
udpSenderService.start();
System.err.println("启动udp发送服务");
} catch (Exception e) {
e.printStackTrace();
}
Random rand = new Random();
byte[] buff = "test".getBytes();
long time = 0;
for(int i=0,endi=3; i<endi; i++){
DatagramPacket data = new DatagramPacket(buff,0,buff.length);
data.setSocketAddress(new InetSocketAddress("192.168.2.23", 7777) );
// int c = (rand.nextInt(2)+1)*50;
// time = (rand.nextInt(15)+1)*100L+System.currentTimeMillis();
// time = System.currentTimeMillis();
time = (rand.nextInt(4)+1)*1000L+System.currentTimeMillis();
// System.out.println(time);
// for(int j=0,endj=(rand.nextInt(2)+1)*50; j<endj; j++){
// udpSenderService.addRequest(time, data);
// }
// System.out.println("time:"+time);
udpSenderService.addTimingRequest(time, data);
try {
if(i%50==0){
// Thread.sleep(1);
rand.setSeed(time);
}
} catch (Exception e) {
}
}
try{
Thread.sleep(1000*10);
}catch(Exception ex){
}
udpSenderService.shutdown();
System.err.println("udp服务关闭.");
}
static void t1(){
UDPSenderService udpSenderService = new UDPSenderService();
try {
InetSocketAddress serviceAddress = new InetSocketAddress("localhost", 7777);
DatagramSocket udpSocket = new DatagramSocket(serviceAddress);
udpSenderService.setDatagramSocket(udpSocket);
udpSenderService.start();
System.err.println("启动udp监听,服务端口7777...");
} catch (SocketException e) {
e.printStackTrace();
}
Random rand = new Random();
for(int i=0; i<1000; i++){
PacketFormat pack = new PacketFormat();
pack.setProperty("msg", "test");
byte[] buff = new byte[128];
int packLen = pack.getBytes(buff, 0);
DatagramPacket data = new DatagramPacket(buff,0,packLen);
udpSenderService.addTimingRequest(System.currentTimeMillis()+rand.nextInt(300)+1000, data);
try {
Thread.sleep(10);
} catch (InterruptedException e) {
}
}
try{
Thread.sleep(1000*60*2);
udpSenderService.shutdown();
System.err.println("udp服务关闭.");
}catch(Exception ex){
}
}
}