社区微信群开通啦,扫一扫抢先加入社区官方微信群
社区微信群
最近工作中负责银行监管系统的对接。在申请银行测试环境的时候,流程复杂繁琐,耗费时间长。 我负责开发和测试银行的接口,但是开发阶段必须连接银行环境,但是银行环境又不能直接到本地。如果指定了本地端口,等测试验收时环境又不一样。银行回复给我们消息时服务地址又要改变,申请银行更改端口流程又负责。所以想在开发阶段和测试验收阶段使用同一个服务端,我采用了代理的方式。
当在本地local 端想通过中间proxy 来发送和接收请求到银行remote端,就可以使用代理。 就像我们平时使用的vpn一样,连接到vpn服务器上,然后通vpn服务器来转发和接收你的请求。
在netty的example中就有简单的例子。我的例子就是从里面改变而来。
ProxyConfig.properties 配置端口:
localIp=221.228.241.106
localPort=8443
ProxyIp =192.168.2.13
ProxyPort =54951
remoteIp=202.108.57.118
remotePort=35053
HexDumpProxy.java 代理服务器端:
public final class HexDumpProxy
{
private static Logger logger = LoggerFactory.getLogger(HexDumpProxy.class);
//中间IP和端口
static final String PROXY_IP =ProxyConfig.getInstance().getProxyIp();
static final int PROXY_PORT = Integer.parseInt(ProxyConfig.getInstance().getProxyPort());
public static void main(String[] args) throws Exception
{
logger.info("**********************启动代理 ********************** Ip:{} port:{}",PROXY_IP,PROXY_PORT);
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
EventLoopGroup workerGroup = new NioEventLoopGroup();
try
{ //这里启动代理服务器端,用来接收local端和remote端的消息
//把从来自local端的消息转发到remote端
//把从来自remote端的消息转发到local端
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class)
.handler(new LoggingHandler(LogLevel.INFO))
.childHandler(new HexDumpProxyInitializer())
.childOption(ChannelOption.AUTO_READ, false)
.bind(new InetSocketAddress(PROXY_IP, PROXY_PORT)) //代理端口
.sync().channel().closeFuture().sync();
}
finally
{
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
}
HexDumpProxyInitializer.java 文件 主要处理proxy服务器的handler
public class HexDumpProxyInitializer extends ChannelInitializer<SocketChannel>
{
public HexDumpProxyInitializer()
{
}
@Override
public void initChannel(SocketChannel ch)
{
ch.pipeline().addLast(new LoggingHandler(LogLevel.INFO),new HexDumpProxyFrontendHandler());
}
}
在HexDumpProxy 中主要关注HexDumpProxyFrontendHandler该类主要处理连接事件和数据的读取写入事件。
public class HexDumpProxyFrontendHandler extends ChannelInboundHandlerAdapter {
private static Logger logger =LoggerFactory.getLogger(HexDumpProxyFrontendHandler.class);
//写入银行的通道
private volatile Channel outbound2BankChannel;
public HexDumpProxyFrontendHandler() {
}
//当local或者remote与proxy连接时,proxy会发起到local或者remote端的连接
@Override
public void channelActive(ChannelHandlerContext ctx) {
final Channel inboundChannel = ctx.channel();
InetSocketAddress address = (InetSocketAddress)inboundChannel.remoteAddress();
logger.info("============连接代理成功==================");
logger.info("channelActive IP:{} port:{}",address.getHostString(),address.getPort());
// Start the connection attempt.
Bootstrap b = new Bootstrap();
b.group(inboundChannel.eventLoop())
.channel(ctx.channel().getClass())
.option(ChannelOption.AUTO_READ, true)
.handler(new ChannelInitializer<SocketChannel>() {
@Override
public void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline p = ch.pipeline();
//p.addLast(new LoggingHandler(LogLevel.INFO));
p.addLast(new HexDumpProxyBackendHandler(inboundChannel),new LoggingHandler(LogLevel.INFO));
}
});
ChannelFuture f = b.connect(ProxyConfig.getInstance().getRemoteIp(), Integer.parseInt(ProxyConfig.getInstance().getRemotePort()));
outbound2BankChannel = f.channel();
f.addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture future) {
if (future.isSuccess()) {
// connection complete start to read first data
inboundChannel.read();
} else {
// Close the connection if the connection attempt has failed.
inboundChannel.close();
}
}
});
}
//当从local或者remote写入到proxy时,proxy把读取到的数据直接写入到local或者remote端
@Override
public void channelRead(final ChannelHandlerContext ctx, Object msg) {
logger.debug("==============向目标服务器写入数据========================");
InetSocketAddress fromAddress = (InetSocketAddress)ctx.channel().remoteAddress();
logger.debug("数据来自:{}",fromAddress.getHostName());
if (outbound2BankChannel.isActive()) {
outbound2BankChannel.writeAndFlush(msg).addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture future) {
if (future.isSuccess()) {
InetSocketAddress toAddress = (InetSocketAddress)outbound2BankChannel.remoteAddress();
logger.debug("数据发往:{}",toAddress.getHostName());
// was able to flush out data, start to read the next chunk
ctx.channel().read();
} else {
future.channel().close();
}
}
});
}
}
@Override
public void channelInactive(ChannelHandlerContext ctx) {
Channel ch = ctx.channel();
InetSocketAddress address = (InetSocketAddress)ch.remoteAddress();
logger.info("=============与代理服务器端口断开连接==================");
logger.info("channelInactive IP:{} port:{}",address.getHostString(),address.getPort());
if (outbound2BankChannel != null) {
closeOnFlush(outbound2BankChannel);
}
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
Channel ch = ctx.channel();
InetSocketAddress address = (InetSocketAddress)ch.remoteAddress();
logger.info("=============与代理服务器端口端口连接==================");
logger.info("exceptionCaught IP:{} port:{}",address.getHostString(),address.getPort());
cause.printStackTrace();
closeOnFlush(ctx.channel());
}
/**
* Closes the specified channel after all queued write requests are flushed.
*/
static void closeOnFlush(Channel ch) {
if (ch.isActive()) {
ch.flush();
}
}
}
public class HexDumpProxyBackendHandler extends ChannelInboundHandlerAdapter {
private static Logger logger =LoggerFactory.getLogger(HexDumpProxyBackendHandler.class);
//写入本地的通道
private volatile Channel outbound2LocalChannel;
public HexDumpProxyBackendHandler(Channel outbound2LocalChannel)
{
this.outbound2LocalChannel =outbound2LocalChannel;
}
//当proxy与local或者remote连接时,开始从proxy中读取数据
@Override
public void channelActive(ChannelHandlerContext ctx) {
final Channel inboundChannel = ctx.channel();
InetSocketAddress address = (InetSocketAddress)inboundChannel.remoteAddress();
logger.info("##################代理连接目标端口成功#######################");
logger.info("连接目标端口成功。 ip:{} port:{}",address.getHostName(),address.getPort());
ctx.read();
}
//把proxy中的数据读取,同时把数据写入local或者remote端
@Override
public void channelRead(final ChannelHandlerContext ctx, Object msg) {
logger.info("##################目标服务器向代理写入数据#######################");
InetSocketAddress fromAddress = (InetSocketAddress)ctx.channel().remoteAddress();
logger.debug("数据来自:{}",fromAddress.getHostName());
outbound2LocalChannel.writeAndFlush(msg).addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture future) {
if (future.isSuccess()) {
InetSocketAddress toAddress = (InetSocketAddress)outbound2LocalChannel.remoteAddress();
logger.debug("数据发往:{}",toAddress.getHostName());
ctx.channel().read();
} else {
future.channel().close();
}
}
});
}
@Override
public void channelInactive(ChannelHandlerContext ctx) {
logger.info("############代理和目标地址端口断开连接##############");
HexDumpProxyFrontendHandler.closeOnFlush(ctx.channel());
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
logger.info("############代理和目标地址端口断开连接##############");
logger.debug("exceptionCaught:{}",cause.getMessage());
cause.printStackTrace();
HexDumpProxyFrontendHandler.closeOnFlush(ctx.channel());
}
}
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!