问题遇到的现象和发生背景
需要跟别的公司进行网络通讯,对方通过C++的Socket发来电文,我们需要解析进行数据处理,还需要针对收到的电文给他们一个应答电文,Netty服务端部署在服务器上,再通过客户端请求就报上图的400,导致对方收不到正确应答电文。但是将Netty服务端部署到本地就没有问题。
问题相关代码
GeneratedNumberUtil 是我们本地工具类,实际就是SimpleDateFormat解析日期
客户端:
/**
* netty 客户端类
*/
@Configuration
public class NettyClient {
// @Value("${netty.remoteAddr}")
private static String host = "127.0.0.1";
// @Value("${netty.remotePort}")
private static Integer port = 9001;
public static void main(String[] args) {
System.out.println("客户端启动!ip:"+host+",post:"+port);
//客户端只需要创建一个线程就足够了
EventLoopGroup group = new NioEventLoopGroup();
try {
//客户端启动类
Bootstrap bootstrap = new Bootstrap();
bootstrap.group(group)//设置线程组
.channel(NioSocketChannel.class)//设置通道类型
.remoteAddress(new InetSocketAddress(host, port))//设置IP和端口
.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
socketChannel.pipeline().addLast("encoder", new StringEncoder());
socketChannel.pipeline().addLast("decoder", new StringDecoder());
socketChannel.pipeline().addLast(new ClientHandler());
}
});
//阻塞通道
ChannelFuture channelFuture = bootstrap.connect().sync();
channelFuture.channel().closeFuture().sync();
} catch (Exception e) {
} finally {
try {
group.shutdownGracefully().sync();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
客户端处理器:
/**
* 客户端逻辑处理类
*/
public class ClientHandler extends SimpleChannelInboundHandler<String> {
private static final Logger logger = LoggerFactory.getLogger(ClientHandler.class);
/**
* 连接成功后,发送给服务端的消息
* @param ctx
* @throws Exception
*/
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
byte[] bs = new byte[30];
// 结束符
byte[] bytes = {0x0a};
StringBuffer sb = new StringBuffer();
// 电文长度 电文号 电文发送日期
sb.append("0030").append("999999").append(GeneratedNumberUtil.sdf.format(new Date())).
// 电文发送时间 发送端描述码 接收端描述码 传送功能码
append(GeneratedNumberUtil.sdfhms.format(new Date())).append("LS").append("CX").append("C");
byte[] bytes1 = sb.toString().getBytes(CharsetUtil.UTF_8);
for (int i = 0; i < bytes1.length; i++) {
bs[i] = bytes1[i];
}
bs[29] = bytes[0];
String string = new String(bs);
logger.info("发送的数据:{}",string);
// ctx.writeAndFlush(string);
ctx.write(string);
ctx.write(bs);
ctx.flush();
}
/**
* 回调方法,接收服务器发送的消息
* @param ctx
* @param msg
* @throws Exception
*/
@Override
protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
// System.out.println("--------消息:"+ msg.toString(CharsetUtil.UTF_8) + "-----------");
logger.info("Client accept : {}" ,msg);
}
/**
* 在处理过程中引发异常时被调用
* @param ctx
* @param cause
* @throws Exception
*/
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
ctx.close();
}
}
服务端代码:
/**
* Netty
* 服务端
*/
@Configuration
public class NettyServer {
private final Integer port = 9001;
//四个处理请求的逻辑类
@Autowired
ServerInboundHandler serverInboundHandler;
@Autowired
ServerInboundGetTimeHandler serverInboundGetTimeHandler;
@Autowired
ServerLastOutboundHandler serverLastOutboundHandler;
@Autowired
ServerOutboundHandler serverOutboundHandler;
public void startServer() {
System.out.println("服务端启动成功");
//创建两个线程组,用于接收客户端的请求任务,创建两个线程组是因为netty采用的是反应器设计模式
//反应器设计模式中bossGroup线程组用于接收
EventLoopGroup bossGroup = new NioEventLoopGroup();
//workerGroup线程组用于处理任务
EventLoopGroup workerGroup = new NioEventLoopGroup();
//创建netty的启动类
ServerBootstrap bootstrap = new ServerBootstrap();
//创建一个通道
ChannelFuture f;
try {
bootstrap.group(bossGroup, workerGroup) //设置线程组
.channel(NioServerSocketChannel.class) //设置通道为非阻塞IO
.option(ChannelOption.SO_BACKLOG, 128) //设置日志
.option(ChannelOption.SO_RCVBUF, 32 * 1024) //接收缓存
.childOption(ChannelOption.SO_KEEPALIVE, true)//是否保持连接
.childHandler(new ChannelInitializer<SocketChannel>() {
//设置处理请求的逻辑处理类
@Override
protected void initChannel(SocketChannel ch) throws Exception {
//ChannelPipeline是handler的任务组,里面有多个handler
ChannelPipeline pipeline = ch.pipeline();
pipeline.addLast("decoder", new StringDecoder());
pipeline.addLast("encoder", new StringEncoder());
//逻辑处理类
// pipeline.addLast(serverLastOutboundHandler);
// pipeline.addLast(serverOutboundHandler);
pipeline.addLast(serverInboundHandler);
// pipeline.addLast(serverInboundGetTimeHandler);
}
});
f = bootstrap.bind(port).sync();//阻塞端口号,以及同步策略
f.channel().closeFuture().sync();//关闭通道
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
//优雅退出
try {
bossGroup.shutdownGracefully().sync();
workerGroup.shutdownGracefully().sync();
}
catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
服务端处理器:
/**
* Inbound处理类,是用来处理客户端发送过来的信息
* Sharable 所有通道都能使用的handler
*/
@Configuration
@ChannelHandler.Sharable
public class ServerInboundHandler extends ChannelInboundHandlerAdapter {
private static final Logger logger = LoggerFactory.getLogger(ServerInboundHandler.class);
/**
* 获取客户端的内容类
* @param ctx
* @param msg
* @throws Exception
*/
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
//将传递过来的内容转换为ByteBuf对象
String s = (String) msg;
logger.info("服务端接收到的信息为: {}", s);
byte[] bs = new byte[110];
byte[] bytes = {0x0a};
// 拼接应答电文
StringBuffer sb = new StringBuffer();
String msgNum = "CXLS01"; // 电文号
if (StringUtils.isNotEmpty(s)){
msgNum = s.substring(4,10);
}
logger.info("接收到应用电文的电文号:{}",msgNum);
String transmissionCode = "A"; // 默认为A 表示ACK
String controlDomain = " ";// 控制域 默认为空格,搭配ACK使用,长度80byte
if (StringUtils.isEmpty(s)){
logger.info("进入NACK...");
transmissionCode = "B"; // 否则为N 表示错误应答 NACK
controlDomain = " ";// 如果出错,控制域使用西文字符指定出错位置,暂不设置
}
// 电文长度 电文号 电文发送日期
sb.append("0110").append(msgNum).append(GeneratedNumberUtil.sdf.format(new Date())).
// 电文发送时间 发送端描述码 接收端描述码 传送功能码
append(GeneratedNumberUtil.sdfhms.format(new Date())).append("LS").append("CX").append(transmissionCode).
// 控制域
append(controlDomain);
byte[] bytes1 = sb.toString().getBytes(CharsetUtil.UTF_8);
for (int i = 0; i < bytes1.length; i++) {
bs[i] = bytes1[i];
}
bs[109] = bytes[0];
String string = new String(bs);
logger.info("服务端反馈的数据:{}",string);
// ctx.writeAndFlush(string);
ctx.write(string);
ctx.write(bs);
ctx.flush();
}
/**
* 刷新后才将数据发出到SocketChannel
* @param ctx
* @throws Exception
*/
@Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
ctx.flush();
}
/**
* 关闭
* @param ctx
* @param cause
* @throws Exception
*/
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause)
throws Exception {
logger.info("报错啦!错误信息:{}",cause.getMessage());
cause.printStackTrace();
ctx.close();
}
}
我的解答思路和尝试过的方法
一开始以为是nginx代理的问题,但这个部署在专门做接口对接的服务,发现并没有涉及nginx。
也试过原生的Socket写服务端,但是一旦部署在服务器上时,本地的客户端请求收不到任何响应,连报错也没有
问过很多人了,也百度很多次了,都没有解决方法,只能网上寻求大佬解决了。网络通讯的通道是建立了,但是由于返回的是报错信息,导致对方无法收到正确格式的电文,所以就没有后续了。
我想要达到的结果
客户端在请求服务端时,需要收到正确的数据。