Code background
The following functions need to be realized: click the page button in the foreground, and the background will send instructions to the lower computer to control the Internet of things devices.
In fact, this logic can be applied to many scenarios. The process of my solution is as follows:
- The foreground uses OnClick event to bind a button
- Button triggered WebSocket related methods
- Send WebSocket data to the foreground
- Using Netty to design a WebSocketServer to receive the ws data in the background
- Calling Netty Client to send data in the handler processing class of ws data
- Netty Server receives, processes, and returns results
Related code
onclick event of foreground page
<div class="row"> <div class="col-md-2"> <button type="button" class="btn btn-primary" onclick="connectWS(this.value)" value="01"> Open fan</button> </div> <div class="col-md-2"> <button type="button" class="btn btn-primary" onclick="connectWS(this.value)" value="02"> Fan off</button> </div> <div class="col-md-2"> <button type="button" class="btn btn-primary" onclick="connectWS(this.value)" value="03"> Turn on the fan and shake your head</button> </div> </div>
JS at the front desk
var ws; function connectWS(command) { // Current address var path = window.location.pathname; // Current host var hostaddress = window.location.host + path.substring(0,path.substr(1).indexOf('/')+1); // Background wb controller url var target = "/wb/test"; // Replace http protocol with ws if (window.location.protocol == 'http:') { target = 'ws://' + hostaddress + target; } else { target = 'wss://' + hostaddress + target; } console.log('WebSocketServer Address:'+target); //Create a webSocket object for the controller if ('WebSocket' in window) { ws = new WebSocket(target); } else if ('MozWebSocket' in window) { ws = new MozWebSocket(target); } else { $.modal.confirm("Your browser does not support WebSocket!"); return; } // If there is no live broadcast status of ws object, set the corresponding button to 2 if(ws==null){ console.log("WebSocket Create failure...") } // Open WS ws.onopen = function () { //Send instructions to the background startsent(command); console.log('Send control command'); }; // Return information of WS ws.onmessage = function (event) { console.log('WS Information received:' + event.data); }; // WS shut down ws.onclose = function (event) { console.log('WS Closed:' + event.data ); }; } function startsent(command){ if (ws != null) { // Console printing console.log('Start sending Wb instructions'); // Push information ws.send(command); } else { $.modal.confirm("WebSocket Connection establishment failed, please reconnect"); } }
config configuration of webSocketServer in the background
package com.teavamc.transsocket.config; import com.teavamc.transsocket.websocket.WbHandler; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.socket.WebSocketHandler; import org.springframework.web.socket.config.annotation.EnableWebSocket; import org.springframework.web.socket.config.annotation.WebSocketConfigurer; import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry; import org.springframework.web.socket.server.standard.ServerEndpointExporter; import javax.websocket.server.ServerEndpointConfig; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import java.util.logging.StreamHandler; /** * @author Zhang Chao TeamC * @Description:Add WebSocket configuration to support * @ClassName WebSocketConfig * @date 2019/5/4 9:54 **/ @Configuration @EnableWebSocket public class WebSocketConfig implements WebSocketConfigurer { private Logger log = LogManager.getLogger(WebSocketConfig.class); @Override public void registerWebSocketHandlers(WebSocketHandlerRegistry registry){ String mapping = "/wb/test"; registry.addHandler(webSocketHandler(),mapping); log.info("WebSocket Registered. WB Address:" + mapping); } @Bean public WebSocketHandler webSocketHandler(){ return new WbHandler(); } }
ws processing class
package com.teavamc.transsocket.websocket; import com.teavamc.transsocket.client.TcpClient; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.springframework.web.socket.CloseStatus; import org.springframework.web.socket.WebSocketHandler; import org.springframework.web.socket.WebSocketMessage; import org.springframework.web.socket.WebSocketSession; import java.lang.reflect.Array; import java.util.ArrayList; /** * @author Zhang Chao TeamC * @Description:TODO * @ClassName WbHandler * @date 2019/5/4 15:10 **/ public class WbHandler implements WebSocketHandler { private static final Logger log = LogManager.getLogger(WbHandler.class); @Override public void afterConnectionClosed(WebSocketSession session, CloseStatus arg1) throws Exception{ log.info("Normal log:"+session.getRemoteAddress()+"Disconnect!"); } @Override public void afterConnectionEstablished(WebSocketSession session) throws Exception{ log.info("Normal log:"+session.getRemoteAddress()+"Open connection!"); } @Override public void handleMessage(WebSocketSession conn, WebSocketMessage<?> message) throws Exception { log.info("Log information:"+message.getPayload()); sendCMDtoSocket(message.getPayload().toString()); } @Override public void handleTransportError(WebSocketSession session, Throwable arg1) throws Exception { if(session.isOpen()){ session.close(); } log.error( "error log: IP:" +session.getRemoteAddress()+" "+ arg1.getMessage() ); } @Override public boolean supportsPartialMessages() { return false; } private void sendCMDtoSocket(String msg){ try { TcpClient.sendMsg(msg); log.info("TCP Client send message" + msg); } catch (Exception e) { log.info("TCP Client Failed to send message:" + e); } } }
Called tcpcclient client
package com.teavamc.transsocket.client; import io.netty.bootstrap.Bootstrap; import io.netty.channel.*; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.nio.NioSocketChannel; import io.netty.handler.codec.LengthFieldBasedFrameDecoder; import io.netty.handler.codec.LengthFieldPrepender; import io.netty.handler.codec.string.StringDecoder; import io.netty.handler.codec.string.StringEncoder; import io.netty.util.CharsetUtil; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; /** * @author Zhang Chao TeamC * @Description:TODO * @ClassName TcpClient * @date 2019/5/4 17:25 **/ public class TcpClient { private static Logger log = LogManager.getLogger(TcpClient.class); public static String HOST = "127.0.0.1"; public static int PORT = 8888; public static Bootstrap bootstrap = getBootstrap(); public static Channel channel = getChannel(HOST, PORT); /** * Initialize Bootstrap */ public static final Bootstrap getBootstrap() { EventLoopGroup group = new NioEventLoopGroup(); Bootstrap b = new Bootstrap(); b.group(group).channel(NioSocketChannel.class); b.handler(new ChannelInitializer<Channel>() { @Override protected void initChannel(Channel ch) throws Exception { ChannelPipeline pipeline = ch.pipeline(); pipeline.addLast("frameDecoder", new LengthFieldBasedFrameDecoder(Integer.MAX_VALUE, 0, 4, 0, 4)); pipeline.addLast("frameEncoder", new LengthFieldPrepender(4)); pipeline.addLast("decoder", new StringDecoder(CharsetUtil.UTF_8)); pipeline.addLast("encoder", new StringEncoder(CharsetUtil.UTF_8)); pipeline.addLast("handler", new TcpClientHandler()); } }); b.option(ChannelOption.SO_KEEPALIVE, true); return b; } // Connection port public static final Channel getChannel(String host, int port) { Channel channel = null; try { channel = bootstrap.connect(host, port).sync().channel(); log.info("TCP Client Already in" + host + "Of" + port + "Port establishment Channel"); } catch (Exception e) { System.out.println("Connect Server(IP{},PORT{})fail"+"host:"+host+"port:"+port+"e:"+e); return null; } return channel; } /** * Send message * @author Zhang Chao TeamC * @date 2019/5/2 * @return void */ public static void sendMsg(String msg) throws Exception { if (channel != null) { channel.writeAndFlush(msg).sync(); } else { log.info("Message sending failed,Connection not established!"); } } }
Client's processing class
package com.teavamc.transsocket.client; import com.teavamc.transsocket.server.TcpServerHandler; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.SimpleChannelInboundHandler; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; /** * @author Zhang Chao TeamC * @Description:TODO * @ClassName TcpClientHandler * @date 2019/5/4 17:26 **/ public class TcpClientHandler extends SimpleChannelInboundHandler<Object> { private static Logger log = LogManager.getLogger(TcpServerHandler.class); private final String SUCCEED = "1"; private final String FAILED = "0"; private final String REC = "TCP Client Messages received:"; @Override protected void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception { if (SUCCEED.equals(msg)){ log.info(REC + "Success"); }else if(FAILED.equals(msg)){ log.info(REC + "fail"); }else { log.warn(REC + "Server Returned data exception"); } } }
TcpServer server
package com.teavamc.transsocket.server; import io.netty.bootstrap.Bootstrap; import io.netty.bootstrap.ServerBootstrap; import io.netty.channel.Channel; import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelPipeline; import io.netty.channel.EventLoopGroup; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.SocketChannel; import io.netty.channel.socket.nio.NioServerSocketChannel; import io.netty.handler.codec.LengthFieldBasedFrameDecoder; import io.netty.handler.codec.LengthFieldPrepender; import io.netty.handler.codec.string.StringDecoder; import io.netty.handler.codec.string.StringEncoder; import io.netty.util.CharsetUtil; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; /** * @author Zhang Chao TeamC * @Description:TODO * @ClassName TcpServer * @date 2019/5/2 15:04 **/ public class TcpServer { private static Logger log = LogManager.getLogger(TcpServer.class); // Server address port private static final String IP = "127.0.0.1"; private static final int PORT = 8888; //Determine the IP address of the client private final String CLIENT_IP = "127.0.0.1"; private final int CLIENT_PORT = 3000; /** Number of thread groups used to allocate processing business threads */ protected static final int BIZGROUPSIZE = Runtime.getRuntime().availableProcessors() * 2; /** Business thread size */ protected static final int BIZTHREADSIZE = 4; /* * NioEventLoopGroup It's actually a thread pool, * NioEventLoopGroup n nioeventloops are started in the background to handle Channel events, * Each NioEventLoop is responsible for processing m channels, * NioEventLoopGroup Take NioEventLoop from NioEventLoop array one by one to process Channel */ private static final EventLoopGroup bossGroup = new NioEventLoopGroup(BIZGROUPSIZE); private static final EventLoopGroup workerGroup = new NioEventLoopGroup(BIZTHREADSIZE); // Thread content public static void run() throws Exception { ServerBootstrap b = new ServerBootstrap(); b.group(bossGroup, workerGroup); b.channel(NioServerSocketChannel.class); b.childHandler(new ChannelInitializer<SocketChannel>() { @Override public void initChannel(SocketChannel ch) throws Exception { ChannelPipeline pipeline = ch.pipeline(); // Decode is to code the information sent // @param maxFrameLength maximum frame length // @param lengthFieldOffset length field offset address // @Bytes occupied by param lengthFieldLength length field // @param lengthAdjustment modifies the value defined in the frame data length field, // It can be a negative number because sometimes we are used to record the header in the length. If it is a negative number, it indicates how many fields should be pushed back // @How many lengths are skipped when parsing param initialBytesToStrip pipeline.addLast("frameDecoder", new LengthFieldBasedFrameDecoder(Integer.MAX_VALUE, 0, 4, 0, 4)); // Encode is to decode the received information pipeline.addLast("frameEncoder", new LengthFieldPrepender(4)); pipeline.addLast("decoder", new StringDecoder(CharsetUtil.UTF_8)); pipeline.addLast("encoder", new StringEncoder(CharsetUtil.UTF_8)); pipeline.addLast(new TcpServerHandler()); } }); //Asynchronous binding port b.bind(IP, PORT).sync(); log.info("TCP Server Port:" + PORT); } //Close port public static void shutdown() { workerGroup.shutdownGracefully(); bossGroup.shutdownGracefully(); } }
Server processing class
package com.teavamc.transsocket.server; import io.netty.channel.Channel; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.SimpleChannelInboundHandler; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; /** * @author Zhang Chao TeamC * @Description:TODO * @ClassName TcpServerHandler * @date 2019/5/2 15:05 **/ public class TcpServerHandler extends SimpleChannelInboundHandler<Object> { private static Logger log = LogManager.getLogger(TcpServerHandler.class); private final String OPENFAN = "01"; private final String CLOSEFAN = "02"; private final String OPENAOTUFAN = "03"; private final String SUCCEED = "1"; private final String FAILED = "0"; /** * @Description Print the received content and send it back * @author Zhang Chao TeamC * @date 2019/5/4 * @Time 16:25 * @return void */ @Override protected void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception { if (OPENFAN.equals(msg)){ log.info("TCP Server Received command to turn on the fan:" + msg); ctx.channel().writeAndFlush(SUCCEED); }else if(CLOSEFAN.equals(msg)){ log.info("TCP Server Receive the instruction to start watering:" + msg); ctx.channel().writeAndFlush(SUCCEED); }else if(OPENAOTUFAN.equals(msg)){ log.info("TCP Server Receive command to turn on shaking head:" + msg); ctx.channel().writeAndFlush(SUCCEED); }else { log.info("Unknown instruction:" + msg); ctx.channel().writeAndFlush(FAILED); } } /** * @Description * @author Zhang Chao TeamC * @date 2019/5/4 * @Time 16:50 * @return void */ @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { log.info("exceptionCaught!cause:" + cause.toString()); ctx.close(); } }
Realization effect
Click the button to turn on the fan on the test page, and you can see that a data is sent
See the output of console
See the console output of IDEA
The front button sends data to the Server