UDP协议的介绍:
我就不多说了,参考 百度百科
跟我个人理解,需要注意的是:
1、UDP的有效端口范围在0-65535之间,大约49151的端口都代表动态端口
2、UDP是一种无连接的,不可靠的传输层协议
接下来就是Netty实现UDP,
使用Netty的好处?
他简化网络应用的编程过程开发,例如UDP和TCP的socket服务开发,他功能比较强大,并且提供了编码好解码的能力,并且很多著名的框架都是底层采用Netty,比如Dubbo,服务提供者和消费者之间的通信。淘宝的消息中间件 RocketMQ 的消息生产者和消息消费者之间
- 易用性好
- 性能好
- 健壮性好
- 安全性好
大致结构:
1.所需的netty的jar
<!--netty-all -->
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>4.1.49.Final</version>
</dependency>
2.ThreeUdpServer
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioDatagramChannel;
/**
* <B>说 明<B/>:UdpServer
*/
public class ThreeUdpServer {
private final Bootstrap bootstrap;
private final NioEventLoopGroup acceptGroup;
private Channel channel;
public void start(String host,int port) throws Exception{
try {
channel = bootstrap.bind(host, port).sync().channel();
System.out.println("ThreeUdpServerHandler start success"+port);
channel.closeFuture().await();
} finally {
acceptGroup.shutdownGracefully();
}
}
public Channel getChannel(){
return channel;
}
public static ThreeUdpServer getInstance(){
return UdpServerHolder.INSTANCE;
}
private static final class UdpServerHolder{
static final ThreeUdpServer INSTANCE = new ThreeUdpServer();
}
private ThreeUdpServer(){
bootstrap = new Bootstrap();
acceptGroup = new NioEventLoopGroup();
bootstrap.group(acceptGroup)
.channel(NioDatagramChannel.class)
.option(ChannelOption.SO_BROADCAST, true)
.handler(new ChannelInitializer<NioDatagramChannel>() {
@Override
protected void initChannel(NioDatagramChannel ch)
throws Exception {
ChannelPipeline pipeline = ch.pipeline();
pipeline.addLast(new ThreeUdpServerHandler());
}
});
}
}
3.ThreeUdpServerHandler
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.channel.socket.DatagramPacket;
import lombok.extern.slf4j.Slf4j;
/**
* <B>说 明<B/>:
*
*/
@Slf4j
public class ThreeUdpServerHandler extends SimpleChannelInboundHandler<DatagramPacket>{
// @Override
// public void channelActive(ChannelHandlerContext ctx) throws Exception {
// //当channel就绪后。
// Channel incoming = ctx.channel();
// System.out.println("UDP-Client:" + incoming.remoteAddress() + "上线");
// }
@Override
protected void channelRead0(ChannelHandlerContext ctx, DatagramPacket msg)
throws Exception {
// 接受client的消息
log.info("开始接收来自client的数据");
final ByteBuf buf = msg.content();
int readableBytes = buf.readableBytes();
byte[] content = new byte[readableBytes];
buf.readBytes(content);
String clientMessage = new String(content,"UTF-8");
log.info("Read clientMessage is: "+clientMessage);
if(clientMessage.contains("UdpServer")){
ctx.writeAndFlush(new DatagramPacket(Unpooled.wrappedBuffer("helloClient".getBytes()),msg.sender())).sync();
}
}
//捕获异常
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
log.error("UdpServerHandler exceptionCaught" + cause.getMessage());
cause.printStackTrace();
ctx.close();
}
}
4.ThreeUdpServerTest
public class ThreeUdpServerTest {
private static final String host = "192.168.1.184";
private static final int port = 60000;
public static void main(String[] args) {
try {
ThreeUdpServer.getInstance().start(host, port);
} catch (Exception e) {
e.printStackTrace();
}
}
}
5.ThreeUdpClient
import com.zxk.netty.udp.three.decode.ThreeUdpClientDecoder;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioDatagramChannel;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
import org.apache.tomcat.jni.Local;
import java.nio.charset.Charset;
/**
* <B>说 明<B/>:LogPush UDP client
*/
public class ThreeUdpClient {
private final Bootstrap bootstrap;
public final NioEventLoopGroup workerGroup;
public static Channel channel;
private static final Charset ASCII = Charset.forName("ASCII");
public void start(Integer localPort) throws Exception{
try {
channel = bootstrap.bind(localPort).sync().channel();
channel.closeFuture().await(1000);
} finally {
// workerGroup.shutdownGracefully();
}
}
public Channel getChannel(){
return channel;
}
public static ThreeUdpClient getInstance(){
return logPushUdpClient.INSTANCE;
}
private static final class logPushUdpClient{
static final ThreeUdpClient INSTANCE = new ThreeUdpClient();
}
private ThreeUdpClient(){
bootstrap = new Bootstrap();
workerGroup = new NioEventLoopGroup();
bootstrap.group(workerGroup)
.channel(NioDatagramChannel.class)
.option(ChannelOption.SO_BROADCAST, true)
.handler(new ChannelInitializer<NioDatagramChannel>() {
@Override
protected void initChannel(NioDatagramChannel ch)throws Exception {
ChannelPipeline pipeline = ch.pipeline();
pipeline.addLast(new ThreeUdpClientDecoder(ASCII));
pipeline.addLast(new StringEncoder(ASCII));
pipeline.addLast(new ThreeUdpClientHandler());
}
});
}
}
6.ThreeUdpClientHandler
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.channel.socket.DatagramPacket;
import io.netty.util.concurrent.GenericFutureListener;
import lombok.extern.slf4j.Slf4j;
import java.net.InetSocketAddress;
import java.nio.charset.Charset;
import java.util.List;
/**
* <B>说 明<B/>:
*/
@Slf4j
public class ThreeUdpClientHandler extends SimpleChannelInboundHandler<Object>{
private static Channel channel = ThreeUdpClient.getInstance().getChannel();
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
//当channel就绪后。
log.info("UDP client channel is ready!");
// ctx.writeAndFlush("started");//阻塞直到发送完毕 这一块可以去掉的
// NettyUdpClientHandler.sendMessage("你好UdpServer", new InetSocketAddress("127.0.0.1",8888));
// sendMessageWithInetAddressList(message);
// log.info("client send message is: 你好UdpServer");
}
@Override
protected void channelRead0(ChannelHandlerContext ctx, Object msg) {
//log.info("msg length "+msg.toString().length());
log.info("来自服务端的消息msg "+msg);
}
public String bytesToHexString(byte[] bArray) {
StringBuffer sb = new StringBuffer(bArray.length);
String sTemp;
for (int i = 0; i < bArray.length; i++) {
sTemp = Integer.toHexString(0xFF & bArray[i]);
if (sTemp.length() < 2){
sb.append(0);
}
sb.append(sTemp.toUpperCase());
}
return sb.toString();
}
/**
* 向服务器发送消息
* @param msg 按规则拼接的消息串
* @param inetSocketAddress 目标服务器地址
*/
public static void sendMessage(final String msg,final InetSocketAddress inetSocketAddress){
if(msg == null){
throw new NullPointerException("msg is null");
}
// TODO 这一块的msg需要做处理 字符集转换和Bytebuf缓冲区
senderInternal(datagramPacket(msg, inetSocketAddress));
}
/**
* 发送数据包并监听结果
* @param datagramPacket
*/
public static void senderInternal(final DatagramPacket datagramPacket,List<Channel> channelList) {
for (Channel channel : channelList) {
if(channel != null){
channel.writeAndFlush(datagramPacket).addListener(new GenericFutureListener<ChannelFuture>() {
@Override
public void operationComplete(ChannelFuture future)
throws Exception {
boolean success = future.isSuccess();
if(log.isInfoEnabled()){
log.info("Sender datagramPacket result : "+success);
}
}
});
}
}
}
/**
* 组装数据包
* @param msg 消息串
* @param inetSocketAddress 服务器地址
* @return DatagramPacket
*/
private static DatagramPacket datagramPacket(String msg, InetSocketAddress inetSocketAddress){
ByteBuf dataBuf = Unpooled.copiedBuffer(msg,Charset.forName("UTF-8"));
DatagramPacket datagramPacket = new DatagramPacket(dataBuf, inetSocketAddress);
return datagramPacket;
}
/**
* 发送数据包服务器无返回结果
* @param datagramPacket
*/
private static void senderInternal(final DatagramPacket datagramPacket) {
log.info("ThreeUdpClient.channel"+ThreeUdpClient.channel);
if(ThreeUdpClient.channel != null){
ThreeUdpClient.channel.writeAndFlush(datagramPacket).addListener(new GenericFutureListener<ChannelFuture>() {
@Override
public void operationComplete(ChannelFuture future)
throws Exception {
boolean success = future.isSuccess();
if(log.isInfoEnabled()){
log.info("Sender datagramPacket result : "+success);
}
}
});
}else{
throw new NullPointerException("channel is null");
}
}
}
7.自定义解码器ThreeUdpClientDecoder
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.socket.DatagramPacket;
import io.netty.handler.codec.MessageToMessageDecoder;
import io.netty.util.internal.ObjectUtil;
import lombok.extern.slf4j.Slf4j;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;
@Slf4j
public class ThreeUdpClientDecoder extends MessageToMessageDecoder<DatagramPacket> {
private final Charset charset;
public ThreeUdpClientDecoder() {
this(Charset.defaultCharset());
}
public ThreeUdpClientDecoder(Charset charset) {
this.charset = (Charset) ObjectUtil.checkNotNull(charset, "charset");
}
@Override
protected void decode(ChannelHandlerContext channelHandlerContext, DatagramPacket packet, List<Object> list) throws Exception {
final ByteBuf buf = packet.content();
int readableBytes = buf.readableBytes();
byte[] content = new byte[readableBytes];
buf.readBytes(content);
// 解决返回16(HEX)进制问题, 将16进制转换成16进制字符串样式
// StringBuffer sb = new StringBuffer(content.length);
// String sTemp;
// for (int i = 0; i < content.length; i++) {
// sTemp = Integer.toHexString(0xFF & content[i]);
// if (sTemp.length() < 2){
// sb.append(0);
// }
// sb.append(sTemp.toUpperCase());
// }
list.add(new String(content));
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause)
throws Exception {
log.error("--------数据读异常----------: ");
cause.printStackTrace();
ctx.close();
}
}
8.ThreeUdpClientTest
@Slf4j
public class ThreeUdpClientTest {
//服务端IP
private static final String server_host = "192.168.1.184";
//服务端端口 (UDP有效端口范围在 0-65535之间)
private static final int server_port = 60000;
public static void main(String[] args) {
try {
//启动ud客户端并定点端口
ThreeUdpClient.getInstance().start(1000);
int i = 0;
while( i < 10){
ThreeUdpClientHandler.sendMessage(new String("Hello UdpServer!!!"), new InetSocketAddress(server_host,server_port));
log.info(i+" client send message is: Hello UdpServer");
i++;
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
结果;依次运行ThreeUdpServerTest 和 ThreeUdpClientTest,效果如下:
服务daunt收到客户端的数据:
客户端向服务端发送消息,并得到回应
至此,简单的UDP实现到此结束, 至于更深奥的,博主任在努力学习中......