/*
* Copyright 2012 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*///The MIT License////Copyright (c) 2009 Carl Bystršm////Permission is hereby granted, free of charge, to any person obtaining a copy//of this software and associated documentation files (the "Software"), to deal//in the Software without restriction, including without limitation the rights//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell//copies of the Software, and to permit persons to whom the Software is//furnished to do so, subject to the following conditions:////The above copyright notice and this permission notice shall be included in//all copies or substantial portions of the Software.////THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN//THE SOFTWARE.packagecom.example.springbootnetty.hanlder;importio.netty.channel.*;importio.netty.handler.codec.http.FullHttpResponse;importio.netty.handler.codec.http.websocketx.*;importio.netty.util.CharsetUtil;importlombok.extern.slf4j.Slf4j;@Slf4jpublicclassWebSocketClientHandlerextendsSimpleChannelInboundHandler<Object>{privateWebSocketClientHandshaker handshaker;privateChannelPromise handshakeFuture;publicWebSocketClientHandler(WebSocketClientHandshaker handshaker){this.handshaker = handshaker;}publicChannelFuturehandshakeFuture(){return handshakeFuture;}@OverridepublicvoidhandlerAdded(ChannelHandlerContext ctx){
handshakeFuture = ctx.newPromise();}@OverridepublicvoidchannelActive(ChannelHandlerContext ctx){
handshaker.handshake(ctx.channel());}@OverridepublicvoidchannelInactive(ChannelHandlerContext ctx){
log.info("WebSocket Client disconnected!");}@OverridepublicvoidchannelRead0(ChannelHandlerContext ctx,Object msg)throwsException{Channel ch = ctx.channel();if(!handshaker.isHandshakeComplete()){try{
handshaker.finishHandshake(ch,(FullHttpResponse) msg);
log.info("websocket client 连接成功");
handshakeFuture.setSuccess();}catch(WebSocketHandshakeException e){
log.info("websocket client 连接失败");
handshakeFuture.setFailure(e);}return;}if(msg instanceofFullHttpResponse){FullHttpResponse response =(FullHttpResponse) msg;thrownewIllegalStateException("Unexpected FullHttpResponse (getStatus="+ response.status()+", content="+ response.content().toString(CharsetUtil.UTF_8)+')');}WebSocketFrame frame =(WebSocketFrame) msg;if(frame instanceofTextWebSocketFrame){TextWebSocketFrame textFrame =(TextWebSocketFrame) frame;
log.info("websocket client 接收到的消息:{}",textFrame.text());}elseif(frame instanceofPongWebSocketFrame){
log.info("WebSocket Client received pong");}elseif(frame instanceofCloseWebSocketFrame){
log.info("websocket client关闭");
ch.close();// log.info("websocket client 5秒后开始重连");// Thread.sleep(5000);}}@OverridepublicvoidexceptionCaught(ChannelHandlerContext ctx,Throwable cause){
cause.printStackTrace();if(!handshakeFuture.isDone()){
handshakeFuture.setFailure(cause);}
ctx.close();
log.error("业务处理错误,websocket client关闭");}}
2.WebsocketClient.class
/*
* Copyright 2014 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/packagecom.example.springbootnetty.client;importcom.example.springbootnetty.hanlder.WebSocketClientHandler;importio.netty.bootstrap.Bootstrap;importio.netty.channel.Channel;importio.netty.channel.ChannelInitializer;importio.netty.channel.ChannelPipeline;importio.netty.channel.EventLoopGroup;importio.netty.channel.nio.NioEventLoopGroup;importio.netty.channel.socket.SocketChannel;importio.netty.channel.socket.nio.NioSocketChannel;importio.netty.handler.codec.http.DefaultHttpHeaders;importio.netty.handler.codec.http.HttpClientCodec;importio.netty.handler.codec.http.HttpObjectAggregator;importio.netty.handler.codec.http.websocketx.TextWebSocketFrame;importio.netty.handler.codec.http.websocketx.WebSocketClientHandshakerFactory;importio.netty.handler.codec.http.websocketx.WebSocketVersion;importio.netty.handler.codec.http.websocketx.extensions.compression.WebSocketClientCompressionHandler;importio.netty.handler.ssl.SslContext;importio.netty.handler.ssl.SslContextBuilder;importio.netty.handler.ssl.util.InsecureTrustManagerFactory;importlombok.extern.slf4j.Slf4j;importorg.springframework.stereotype.Service;importjava.net.URI;importjava.util.concurrent.TimeUnit;/**
* This is an example of a WebSocket client.
* <p>
* In order to run this example you need a compatible WebSocket server.
* Therefore you can either start the WebSocket server from the examples
* by running { io.netty.example.http.websocketx.server.WebSocketServer}
* or connect to an existing WebSocket server such as
* <a href="https://www.websocket.org/echo.html">ws://echo.websocket.org</a>.
* <p>
* The client will attempt to connect to the URI passed to it as the first argument.
* You don't have to specify any arguments if you want to connect to the example WebSocket server,
* as this is the default.
*/@Slf4j@ServicepublicclassWebsocketClient{privateString url ="wss:/localhost/webSocketServer";privateChannel channel;privatefinalstaticWebsocketClient WEBSOCKET_CLIENT =newWebsocketClient();privateWebsocketClient(){}publicstaticWebsocketClientgetInstance(){return WEBSOCKET_CLIENT;}privateString URL =System.getProperty("url", url);publicvoidstartClient()throwsException{
log.info("websocket client 连接中....");URI uri =newURI(URL);String scheme = uri.getScheme()==null?"ws": uri.getScheme();finalString host = uri.getHost()==null?"127.0.0.1": uri.getHost();finalint port;if(uri.getPort()==-1){if("ws".equalsIgnoreCase(scheme)){
port =80;}elseif("wss".equalsIgnoreCase(scheme)){
port =443;}else{
port =-1;}}else{
port = uri.getPort();}if(!"ws".equalsIgnoreCase(scheme)&&!"wss".equalsIgnoreCase(scheme)){System.err.println("Only WS(S) is supported.");return;}finalboolean ssl ="wss".equalsIgnoreCase(scheme);finalSslContext sslCtx;if(ssl){
sslCtx =SslContextBuilder.forClient().trustManager(InsecureTrustManagerFactory.INSTANCE).build();}else{
sslCtx =null;}EventLoopGroup group =newNioEventLoopGroup();try{// Connect with V13 (RFC 6455 aka HyBi-17). You can change it to V08 or V00.// If you change it to V00, ping is not supported and remember to change// HttpResponseDecoder to WebSocketHttpResponseDecoder in the pipeline.finalWebSocketClientHandler handler =newWebSocketClientHandler(WebSocketClientHandshakerFactory.newHandshaker(uri,WebSocketVersion.V13,null,true,newDefaultHttpHeaders()));Bootstrap b =newBootstrap();
b.group(group).channel(NioSocketChannel.class).handler(newChannelInitializer<SocketChannel>(){@OverrideprotectedvoidinitChannel(SocketChannel ch){ChannelPipeline p = ch.pipeline();if(sslCtx !=null){
p.addLast(sslCtx.newHandler(ch.alloc(), host, port));}
p.addLast(newHttpClientCodec(),newHttpObjectAggregator(8192),WebSocketClientCompressionHandler.INSTANCE,
handler);}});
channel = b.connect(uri.getHost(), port).sync().channel();
handler.handshakeFuture().sync();// 进行注册
channel.writeAndFlush("发送的第一条消息");// 在这里可以添加一个默认心跳 15s发一次
channel.eventLoop().scheduleAtFixedRate(()->{
channel.writeAndFlush(newTextWebSocketFrame("pong"));},0,15,TimeUnit.SECONDS);//如果下面代码,则main方法所在的线程,即主线程会在执行完bind().sync()方法后,会进入finally 代码块,之前的启动的nettyserver也会随之关闭掉,整个程序都结束了
channel.closeFuture().sync();}catch(Exception e){
channel.close();
log.error("websocket client 启动失败,失败原因:{}", e.getMessage());}finally{
group.shutdownGracefully();}}}