UDP(User Datagram Protocol)即用户数据包协议,是OSI(Open Sysem Interconnection,开放式系统互联)模型的一种无连接的传输层协议,提供面向事务的简单不可靠信息传输服务。
在网络中与TCP协议一样,用于处理数据包,是一种无连接信息,不关系数据包,只关注是否发出数据包,UDP有不提供数据包分组、组装,和不能对数据包进行排序的缺点。也就是说,当报文发送之后,是无法得知是否安全完整到达的。
UDP用来支持那些需要在计算机之间传输数据的网络应用,比如视频会议系统在内的众多C/S模式的网络应用都需要使用UDP协议。
import io.netty.bootstrap.Bootstrap; import io.netty.buffer.Unpooled; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelOption; import io.netty.channel.EventLoopGroup; import io.netty.channel.SimpleChannelInboundHandler; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.DatagramPacket; import io.netty.channel.socket.nio.NioDatagramChannel; import io.netty.util.CharsetUtil;
/** * Netty UDP协议-服务端 */ public class N04UDPServer { public static void main(String[] args) throws Exception { //UDP和Tcp不同,不关心数据包是否达到目的地 EventLoopGroup eventLoopGroup = new NioEventLoopGroup();
//启动类 Bootstrap bootstrap = new Bootstrap(); bootstrap.group(eventLoopGroup) //绑定链接类 .channel(NioDatagramChannel.class) //模式 .option(ChannelOption.SO_BROADCAST, true) //广播 .handler(new UdpServerHandler()); //数据处理类
//绑定端口 ChannelFuture channelFuture = bootstrap.bind(1111).sync(); channelFuture.channel().closeFuture().await();
//关闭 eventLoopGroup.shutdownGracefully(); } } /** * 服务端数据处理 */ class UdpServerHandler extends SimpleChannelInboundHandler
/* * 接收数据的处理 */ @Override protected void messageReceived(ChannelHandlerContext ctx, DatagramPacket packet) throws Exception { String data = packet.content().toString(CharsetUtil.UTF_8); System.out.println("服务端:接收到的数据 - " + data);
//回传消息 ctx.writeAndFlush(new DatagramPacket(Unpooled.copiedBuffer("收到数据。",CharsetUtil.UTF_8),packet.sender())); }
@Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { cause.printStackTrace(); } } |
import java.net.InetSocketAddress;
import io.netty.bootstrap.Bootstrap; import io.netty.buffer.Unpooled; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelOption; import io.netty.channel.EventLoopGroup; import io.netty.channel.SimpleChannelInboundHandler; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.DatagramPacket; import io.netty.channel.socket.nio.NioDatagramChannel; import io.netty.util.CharsetUtil;
/** * Netty UDP协议-客户端 */ public class N04UDPClient { public static void main(String[] args) throws Exception { //发数据 EventLoopGroup eventLoopGroup = new NioEventLoopGroup();
//启动类 Bootstrap bootstrap = new Bootstrap(); bootstrap.group(eventLoopGroup) //绑定 .channel(NioDatagramChannel.class) //模式 .option(ChannelOption.SO_BROADCAST, true) //广播 .handler(new UdpClientHandler()); //处理类
//绑定广播 ChannelFuture channelFuture = bootstrap.bind(0).sync();
//写出端口和广播掩码地址 channelFuture.channel().writeAndFlush(new DatagramPacket(Unpooled.copiedBuffer("你好啊", CharsetUtil.UTF_8), new InetSocketAddress("255.255.255.255",1111))).sync();
//关闭 eventLoopGroup.shutdownGracefully(); } } class UdpClientHandler extends SimpleChannelInboundHandler
@Override protected void messageReceived(ChannelHandlerContext ctx, DatagramPacket msg) throws Exception { String data = msg.content().toString(CharsetUtil.UTF_8); System.out.println(data); }
@Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { cause.printStackTrace(); } } |
WebSocket将网络套接字引入到了客户端和服务端,众所周知,之前实现聊天功能,可能需要古老的Socket技术,或者是古老的DWR框架,反之Ajax技术,在有可能就是Comet服务器推技术,HT的WebSocket可以很轻松的进行聊天功能实现,Netty和H5的WebSocket结合非常的简单,Netty为我们封装了其协议类。
单一的TCP链接,双方即可通信。
对代理、防火墙和路由是透明的。
无头部信息,Cookie和身份验证。
无安全开销。
通过ping/pong帧保持链路激活。
服务器可主动传递消息给客户端,不在需要客户端轮询。
import java.util.Date;
import io.netty.bootstrap.ServerBootstrap; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelFutureListener; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInitializer; import io.netty.channel.EventLoopGroup; import io.netty.channel.SimpleChannelInboundHandler; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.SocketChannel; import io.netty.channel.socket.nio.NioServerSocketChannel; import io.netty.handler.codec.http.DefaultFullHttpResponse; import io.netty.handler.codec.http.FullHttpRequest; import io.netty.handler.codec.http.HttpHeaderUtil; import io.netty.handler.codec.http.HttpObjectAggregator; import io.netty.handler.codec.http.HttpResponseStatus; import io.netty.handler.codec.http.HttpServerCodec; import io.netty.handler.codec.http.HttpVersion; import io.netty.handler.codec.http.websocketx.CloseWebSocketFrame; import io.netty.handler.codec.http.websocketx.PingWebSocketFrame; import io.netty.handler.codec.http.websocketx.PongWebSocketFrame; import io.netty.handler.codec.http.websocketx.TextWebSocketFrame; import io.netty.handler.codec.http.websocketx.WebSocketFrame; import io.netty.handler.codec.http.websocketx.WebSocketServerHandshaker; import io.netty.handler.codec.http.websocketx.WebSocketServerHandshakerFactory; import io.netty.util.CharsetUtil;
/** * WebSocket服务端 */ public class N05WebSocketServer { public static void main(String[] args) throws Exception { //建立连接和传输数据 EventLoopGroup connectEventLoopGroup = new NioEventLoopGroup(); EventLoopGroup dataEventLoopGroup = new NioEventLoopGroup();
//启动类 ServerBootstrap serverBootstrap = new ServerBootstrap(); serverBootstrap.group(connectEventLoopGroup, dataEventLoopGroup) //绑定 .channel(NioServerSocketChannel.class) //模式 .childHandler(new ChannelInitializer @Override protected void initChannel(SocketChannel ch) throws Exception { ch.pipeline().addLast("http-codec",new HttpServerCodec()); ch.pipeline().addLast("aggregator",new HttpObjectAggregator(65535)); ch.pipeline().addLast("handler",new WebSocketHandler()); //处理类 } });
//绑定端口 ChannelFuture channelFuture = serverBootstrap.bind(1111).sync(); channelFuture.channel().closeFuture().sync();
//关闭 connectEventLoopGroup.shutdownGracefully(); dataEventLoopGroup.shutdownGracefully(); } } class WebSocketHandler extends SimpleChannelInboundHandler
private WebSocketServerHandshaker serverHandshaker;
@Override protected void messageReceived(ChannelHandlerContext ctx, Object msg) throws Exception { //================= Http接入 if(msg instanceof FullHttpRequest) { FullHttpRequest httpRequest = (FullHttpRequest) msg; //http-解码失败 if(!httpRequest.decoderResult().isSuccess() || (!"websocket".contentEquals(httpRequest.headers().get("Upgrade")))) { //写出响应码 DefaultFullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.BAD_REQUEST); ByteBuf byteBuf = Unpooled.copiedBuffer(response.status() + "",CharsetUtil.UTF_8); response.content().writeBytes(byteBuf); byteBuf.release(); HttpHeaderUtil.setContentLength(response, response.content().readableBytes());
//如果是非Keep-Alive,关闭连接 ChannelFuture channelFuture = ctx.channel().writeAndFlush(response); if(!HttpHeaderUtil.isKeepAlive(httpRequest) || response.status().code() != 200) { channelFuture.addListener(ChannelFutureListener.CLOSE); } return ; }
//构建握手响应返回 WebSocketServerHandshakerFactory socketServerHandshakerFactory = new WebSocketServerHandshakerFactory("ws://localhost:8080/websocket", null, false); serverHandshaker = socketServerHandshakerFactory.newHandshaker(httpRequest); if(null == serverHandshaker) { WebSocketServerHandshakerFactory.sendUnsupportedVersionResponse(ctx.channel()); }else { serverHandshaker.handshake(ctx.channel(), httpRequest); }
//================= WebSocket接入 }else if(msg instanceof WebSocketFrame) { WebSocketFrame webSocketFrame = (WebSocketFrame) msg;
//判断是否关闭链路的指令 if(webSocketFrame instanceof CloseWebSocketFrame) { serverHandshaker.close(ctx.channel(), (CloseWebSocketFrame) webSocketFrame.retain()); return ; }
//是否是Ping if(webSocketFrame instanceof PingWebSocketFrame) { ctx.channel().write(new PongWebSocketFrame(webSocketFrame.content().retain())); return ; }
//不支持二进制消息 if(!(webSocketFrame instanceof TextWebSocketFrame)) { throw new UnsupportedOperationException("不支持二进制消息"); }
//返回应答 TextWebSocketFrame textWebSocketFrame = (TextWebSocketFrame) webSocketFrame; System.out.println("服务端:" + textWebSocketFrame.text());
//写出 ctx.channel().write(new TextWebSocketFrame(new Date().toString() + " - 欢迎")); } }
@Override public void channelReadComplete(ChannelHandlerContext ctx) throws Exception { ctx.flush(); }
@Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { cause.printStackTrace(); } } |
DOCTYPE html> <html> <head> <meta charset="UTF-8"> Netty WebSocket 时间服务器 head> <br> <body> <br> <script type="text/javascript"> var socket; if (!window.WebSocket) { window.WebSocket = window.MozWebSocket; } if (window.WebSocket) { socket = new WebSocket("ws://localhost:1111/websocket"); socket.onmessage = function(event) { var ta = document.getElementById('responseText'); ta.value=""; ta.value = event.data }; socket.onopen = function(event) { var ta = document.getElementById('responseText'); ta.value = "打开WebSocket服务正常,浏览器支持WebSocket!"; }; socket.onclose = function(event) { var ta = document.getElementById('responseText'); ta.value = ""; ta.value = "WebSocket 关闭!"; }; } else { alert("抱歉,您的浏览器不支持WebSocket协议!"); }
function send(message) { if (!window.WebSocket) { return; } if (socket.readyState == WebSocket.OPEN) { socket.send(message); } else { alert("WebSocket连接没有建立成功!"); } } script> <form onsubmit="return false;"> <input type="text" name="message" value="Netty最佳实践"/> <br><br> <input type="button" value="发送WebSocket请求消息" onclick="send(this.form.message.value)"/> <hr color="blue"/> <h3>服务端返回的应答消息h3> <textarea id="responseText" style="width:500px;height:300px;">textarea> form> body> html> |