**使用java編碼的springboot項目在調(diào)用C語言等其他語言編寫的Tcp接口時,使用netty框架可以實現(xiàn)數(shù)據(jù)雙向持續(xù)交互處理。
注:在交互過程中,c語言生成的二進制字節(jié)碼轉(zhuǎn)java字符串時往往出現(xiàn)亂碼,請看后面處理方式(netty處理類中的代碼)。**
一、引入netty的jar包
io.netty
netty-all
二、使用netty框架
1、創(chuàng)建客戶端
package com.iflytek.digtal.netty;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
/**
-
netty客戶端
*/
public class NettyClient {/**
-
連接tcp服務(wù)端初始化
-
@throws InterruptedException
*/
public static void init() throws InterruptedException {
//客戶端需要一個事件循環(huán)組
NioEventLoopGroup group = new NioEventLoopGroup();try {
//創(chuàng)建客戶端啟動對象
//注意客戶端使用的不是SocketBootstrap而是Bootstrap
Bootstrap bootstrap = new Bootstrap();// 設(shè)置相關(guān)參數(shù) bootstrap.group(group) //設(shè)置線程組 .channel(NioSocketChannel.class)// 使用NioSocketChannel作為客戶端的通道實現(xiàn) .handler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel ch) throws Exception { ch.pipeline().addLast(new NettyClientHandler()); } }); System.out.println("netty client start.."); ChannelFuture cf = bootstrap.connect("127.0.0.1", 6000).sync(); cf.channel().closeFuture().sync();
}finally {
group.shutdownGracefully();
}
}
public static void main(String[] args) throws InterruptedException {
//客戶端需要一個事件循環(huán)組
NioEventLoopGroup group = new NioEventLoopGroup();try { //創(chuàng)建客戶端啟動對象 //注意客戶端使用的不是SocketBootstrap而是Bootstrap Bootstrap bootstrap = new Bootstrap(); // 設(shè)置相關(guān)參數(shù) bootstrap.group(group) //設(shè)置線程組 .channel(NioSocketChannel.class)// 使用NioSocketChannel作為客戶端的通道實現(xiàn) .handler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel ch) throws Exception { ch.pipeline().addLast(new NettyClientHandler()); } }); System.out.println("netty client start.."); ChannelFuture cf = bootstrap.connect("127.0.0.1", 6000).sync(); cf.channel().closeFuture().sync(); }finally { group.shutdownGracefully(); }
}
-
}
2、創(chuàng)建服務(wù)端
package com.iflytek.digtal.netty;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
public class NettyServer {
public static void main(String[] args) throws InterruptedException {
//創(chuàng)建兩個線程組bossGroup和workerGroup,含有的子線程NioEventLoop的個數(shù)默認(rèn)是CPU的兩倍
//bossGroup只是處理連接請求,真正的和客戶端業(yè)務(wù)處理,會交給workerGroup完成
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
EventLoopGroup workerGroup = new NioEventLoopGroup(1);
try {
//創(chuàng)建服務(wù)器端的啟動對象
ServerBootstrap bootstrap = new ServerBootstrap();
//使用鏈?zhǔn)骄幊虂砼渲脜?shù)
bootstrap.group(bossGroup, workerGroup)//設(shè)置兩個線程組
.channel(NioServerSocketChannel.class)//使用NioServerSocketChannel作為服務(wù)器的通道實現(xiàn)
//初始化服務(wù)器連接隊列大小,服務(wù)端處理客戶端連接請求是順序處理的,所以同一時間只能處理一個客戶端連接
//多個客戶端同時來的時候,服務(wù)端將不能處理的客戶端連接請求放在隊列中等待處理
.option(ChannelOption.SO_BACKLOG, 1024)
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel channel) throws Exception {
//對workerGroup的SocketChannel設(shè)置處理器
channel.pipeline().addLast(new NettyServerHandler());
}
});
System.out.println("netty server start..");
//綁定一個端口并且同步生成一個ChannelFuture異步對象,通過isDone()等方法可以判斷異步事件的執(zhí)行情況
//啟動服務(wù)器(并綁定的端口),bind是異步操作,sync方法是等待異步操作執(zhí)行完畢
ChannelFuture cf = bootstrap.bind(9000).sync();
//給cf注冊監(jiān)聽器,監(jiān)聽我們關(guān)心的事件
cf.addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture channelFuture) throws Exception {
if (cf.isSuccess()) {
System.out.println("監(jiān)聽端口9000成功");
} else {
System.out.println("監(jiān)聽端口9000失敗");
}
}
});
//等待服務(wù)端監(jiān)聽端口關(guān)閉,closeFuture是異步操作
//通過sync方法同步等待通道關(guān)閉處理完畢,這里會阻塞等待通道關(guān)閉完成,內(nèi)部調(diào)用的是Object的wait()方法
cf.channel().closeFuture().sync();
} finally {
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
}
3、編寫處理類
package com.iflytek.digtal.netty2;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import lombok.extern.slf4j.Slf4j;
import java.io.*;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.Charset;
import java.util.Arrays;
/**
-
處理類
*/
@Slf4j
public class NettyClientHandler2 extends ChannelInboundHandlerAdapter {/**
- 客戶端連接標(biāo)識
- @param ctx
- @throws Exception
*/
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
ByteBuf buf = Unpooled.copiedBuffer(“HelloServer”.getBytes(Charset.forName(“GBK”)));
ctx.writeAndFlush(buf);
}
//當(dāng)通道建立后有事件時會觸發(fā),即服務(wù)端發(fā)送數(shù)據(jù)給客戶端
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {ByteBuf buf = (ByteBuf) msg; log.info("哈哈哈:{}",msg); // 復(fù)制內(nèi)容到字節(jié)數(shù)組bytes byte[] bytes = new byte[buf.readableBytes()]; buf.readBytes(bytes); log.info("收到的數(shù)據(jù)為:{}", bytes); int[] lengthArr = {4,4,4,4,4,4,56,24,56,24,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,56,32,8}; String[] strArr = {"int","int","int","int","int","int","char","char","char","char", "float","float","int","int","float","float","float","float","int","int","int","int", "int","int","int","int","char","char","systime"}; String[] strs = new String[29]; int offset = 0; for(int i =0;i<29;i++){ byte[] bytes1 = new byte[lengthArr[i]]; System.arraycopy(bytes, offset, bytes1, 0, lengthArr[i]); offset = offset + lengthArr[i]; if("int".equals(strArr[i])){ strs[i] = String.valueOf(bytesToInt(bytes1,0)); }else if("char".equals(strArr[i])){ strs[i] = bytesToChar(bytes1); }else if("float".equals(strArr[i])){ strs[i] = String.valueOf(byteToFloat(bytes1,0)); }else if("systime".equals(strArr[i])){ strs[i] = bytesToChar(bytes1); } } System.out.println("二進制數(shù)據(jù)轉(zhuǎn)化后的結(jié)果:"+ Arrays.toString(strs)); // 前六個int數(shù)值 String[] str = new String[6]; for(int i =0,j=0;j<24;i++){ str[i] = String.valueOf(bytesToInt(bytes,j)); j = j+4; } System.out.println("str:"+ Arrays.toString(str)); //第七個試驗名稱 byte[] bytes1 = new byte[56]; System.arraycopy(bytes, 24, bytes1, 0, 56);
// for(int i=0;i<56;i++){
// bytes1[i] = bytes[20+i];
// }
log.info(“試驗名稱字節(jié)數(shù)組為:{}”, bytes1);
String str1 = bytesToChar(bytes1);
System.out.println(“試驗名稱str1:”+str1);
//第八個通道名稱
byte[] bytes2 = new byte[24];
System.arraycopy(bytes, 80, bytes2, 0, 24);
// for(int i=0;i<56;i++){
// bytes1[i] = bytes[20+i];
// }
log.info(“通道名稱字節(jié)數(shù)組為:{}”, bytes2);
String str2 = bytesToChar(bytes2);
System.out.println(“通道名稱str2:”+str2);
//第九個采集名稱
byte[] bytes3 = new byte[56];
System.arraycopy(bytes, 104, bytes3, 0, 56);
// for(int i=0;i<56;i++){
// bytes1[i] = bytes[20+i];
// }
log.info(“采集名稱字節(jié)數(shù)組為:{}”, bytes3);
String str3 = bytesToChar(bytes3);
System.out.println(“采集名稱str3:”+str3);
System.out.println("服務(wù)端地址是:" + ctx.channel().remoteAddress());
}
public int combine(byte b[])
{
int t1=(b[3]&0xff)<<24;
int t2=(b[2]&0xff)<<16;
int t3=(b[1]&0xff)<<8;
int t4=b[0]&0xff;
//System.out.println(b[1]&0xff);//輸出的是一個整形數(shù)據(jù)
//在java中,設(shè)計int和比int位數(shù)來的小的類型b,如byte,char等,都是先把小類型擴展成int再來運算,
//return( t1<<24)+(t2<<16)+(t3<<8)+t4;//必須加括號
return t1+t2+t3+t4;
}
private char[] getChars (byte[] bytes) {
Charset cs = Charset.forName ("GBK");
ByteBuffer bb = ByteBuffer.allocate (bytes.length);
bb.put (bytes);
bb.flip ();
CharBuffer cb = cs.decode (bb);
return cb.array();
}
/**
* byte轉(zhuǎn)int
* @Title: bytesToInt
*
* @param: @param src 源數(shù)組
* @param: @param offset 起始偏移量
* @return: int
*/
public int bytesToInt(byte[] src, int offset) {
int value;
value = src[offset] & 0xFF;
value |= ((long) (src[offset + 1] & 0xFF) << 8);
value |= ((long) (src[offset + 2] & 0xFF) << 16);
value |= ((long) (src[offset + 3] & 0xFF) << 24);
return value;
}
public String bytesToChar(byte[] bytes){
char[] chars = new char[bytes.length];
for (int i = 0; i < bytes.length; i++) {
chars[i] = (char) (bytes[i] & 0xFF); // 使用位運算將字節(jié)轉(zhuǎn)換為字符
}
// for(int i =0;i<chars.length;i++){
// System.out.println(“char數(shù)組:”+chars[i]);
// }
String str = new String(chars);
// System.out.println(“轉(zhuǎn)換后的字符數(shù)組:” + str);
return str;
}文章來源:http://www.zghlxwxcb.cn/news/detail-831142.html
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
log.info("關(guān)閉通道");
cause.printStackTrace();
ctx.close();
}
/**
* byte轉(zhuǎn)long
* @Title: bytesToLong
*
* @param: @param src 源數(shù)組
* @param: @param offset 起始偏移量
* @return: long
*/
public static long bytesToLong(byte[] src, int offset) {
long value = 0;
for(int i = 0; i < 8; i++) {
value |= ((long) (src[offset + i] & 0xFF) << (8 * i));
}
return value;
}
/**
* byte轉(zhuǎn)float
* @Title: byteToFloat
*
* @param: @param src 源數(shù)組
* @param: @param offset 起始偏移量
* @return: float
*/
public static float byteToFloat(byte[] src, int offset) {
int value;
value = src[offset + 0] & 0xFF;
value |= ((long) (src[offset + 1] & 0xFF) << 8);
value |= ((long) (src[offset + 2] & 0xFF) << 16);
value |= ((long) (src[offset + 3] & 0xFF) << 24);
return Float.intBitsToFloat(value);
}
/**
* byte轉(zhuǎn)double
* @Title: byteToDouble
*
* @param: @param src 源數(shù)組
* @param: @param offset 起始偏移量
* @return: double
*/
public static double byteToDouble(byte[] src, int offset) {
long value = 0;
for (int i = 0; i < 8; i++) {
value |= ((long) (src[offset + i] & 0xff)) << (8 * i);
}
return Double.longBitsToDouble(value);
}
public static void main(String[] args){
File file = new File("C:\\Users\\Administrator\\Desktop\\test.txt"); // 要讀取的文件名
try (BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(file), "GBK"))) {
String line;
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
} catch (IOException e) {
e.printStackTrace();
}
try{
String str = "hello";
byte[] bytes = str.getBytes("GBK"); // 將字符串轉(zhuǎn)換為字節(jié)數(shù)組
String newStr = new String(bytes, "GBK"); // 將字節(jié)數(shù)組轉(zhuǎn)換為字符串
System.out.println("源字符串:"+str);
}catch (Exception e){
e.printStackTrace();
}
}
}
注:其中c語言二進制字節(jié)碼int、double、float型通過位運算皆可;
而string類型轉(zhuǎn)換的時候存在亂碼,此時我是通過通配符替換掉亂碼的,就是除了中文、數(shù)字、特殊字符一律替換為空;
另外還有一個問題,就是netty的channel循環(huán)接收時,上一輪的字節(jié)數(shù)的最后不是字符的最后一位,這樣下一輪的開頭其實是一個字符剩下的部分,這時拼接轉(zhuǎn)寫也會出現(xiàn)為空的情況,這時java小端字節(jié)碼編碼方式造成的,只要規(guī)避即可。文章來源地址http://www.zghlxwxcb.cn/news/detail-831142.html
到了這里,關(guān)于Springboot使用Netty連接Tcp接口(c語言二進制字節(jié)碼轉(zhuǎn)java字符串)的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!