NettyClient에 대해 포스팅을 진행하고 있는데, 첫번째 포스팅 글에서는 10개의 보낼 메시지가 있으면 한번 연결된 상태에서 10개의 메시지를 발신할때까지 핸들러에서 제어하면서 처리를 하였습니다.
다른 진행 방법으로 요청사항으로 메시지별 발송 -> 수신 -> 종료 형태로 반복하여 처리해달라고 하게 되면, 어떤식으로 처리를 해야 할지 알아보겠습니다.
NettyClient
1. NettyClientAction.java
package com.psw.socket.nettyPrj.netty.client;
public interface NettyClientAction {
public void close(NettyClientHandler handler);
public void receive(NettyClientHandler handler);
}
콜백 메소드 처리를 위해 interface 메소드 선언
2. NettyClient.java
package com.psw.socket.nettyPrj.netty.client;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import org.apache.log4j.Logger;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
/**
* NettyClient
* @author psw
*/
public class NettyClient {
private static final Logger logger = Logger.getLogger(NettyClient.class);
private Bootstrap bs = new Bootstrap();
private SocketAddress addr_;
private Channel channel_;
private String[] msgArr;
private int idx;
private NioEventLoopGroup group;
public NettyClient(SocketAddress addr, String[] msgArr) {
this.addr_ = addr;
this.msgArr = msgArr;
}
public NettyClient(String host, int port, String[] msgArr) {
this(new InetSocketAddress(host, port), msgArr);
}
//실제로 동작시킬 메소드 Bootstrap 연결 옵션 설정 및 연결 처리
public void run() {
if(this.addr_ == null) {
logger.error("주소 정보가 없습니다.");
}else if(this.msgArr == null || this.msgArr.length == 0) {
logger.error("보낼 메시지가 없습니다.");
}
group = new NioEventLoopGroup(3);
bs.group(group)
.channel(NioSocketChannel.class)
.option(ChannelOption.SO_KEEPALIVE, true);
doConnect();
}
private void doConnect() {
handlerSet();
ChannelFuture f = bs.connect(addr_);
channel_ = f.channel();
logger.info(addr_ + " connect()");
}
private void handlerSet() {
if(bs != null) {
bs.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
NettyClientHandler handler = new NettyClientHandler(msgArr[idx]);
handler.setCallBackClientHandler(new NettyClientAction() {
public void close(NettyClientHandler handler) {
//종료 처리 후 더 보낼게 존재한다면 기존 옵션으로 재 연결처리를 하는 콜백 메소드
closeAndContinue();
}
public void receive(NettyClientHandler handler) {
//응답 받은 메시지 콜백 메소드
String receiveMsg = handler.getReceiveMsg();
logger.info("callBack receive : "+ receiveMsg);
closeAndContinue();
}
});
ch.pipeline().addLast("clientHandler", handler);
}
});
}
}
private void closeAndContinue() {
try {
channel_.close().sync(); //현재의 채널을 일단 닫는다.
if(msgArr.length > ++idx) { //보낼 메시지가 남았으면 재연결 처리
doConnect();
}else { //보낼 메시지가 없다면 종료
bs.group().shutdownGracefully(); //eventLoop에 등록된 Thread를 종료 처리한다.
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
- 생성자를 통해 연결지의 IP, PORT정보를 얻고 보낼 메시지 배열을 전달받습니다.
- run() 메소드를 통해 전송을 위한 옵션 설정을 진행합니다. - Bootstrap 옵션 처리 (옵션처리는 최초에만 한번 설정)
- 옵션 설정이 끝나면 doConnect메소드를 통해 연결처리를 진행합니다.
- 연결 전 handler부분에서 메소드가 동작 후 콜백 받을 메소드를 정의합니다.
- 연결
- 동작들이 일어나고 receive 콜백 메소드가 동작하면 closeAndContinue() 메소드에서 더 보낼지 멈출지 판단합니다.
-> 더 이상 보낼 메시지가 없다면 설정한 그룹들의 EventGroup을 모두 종료하기 위해 shutdown처리를 합니다.
3. NettyClientHandler.java
package com.psw.socket.nettyPrj.netty.client;
import org.apache.log4j.Logger;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
public class NettyClientHandler extends ChannelInboundHandlerAdapter{
private static final Logger logger = Logger.getLogger(NettyClientHandler.class);
private String sendMsg;
public NettyClientHandler(String msg) {
this.sendMsg = msg;
}
private NettyClientAction action_;
public void setCallBackClientHandler(NettyClientAction action) {
this.action_ = action;
}
private String receiveMsg;
public String getReceiveMsg() {
return this.receiveMsg;
}
@Override
public void channelRegistered(ChannelHandlerContext ctx) throws Exception {
logger.info("채널 등록");
}
@Override
public void channelUnregistered(ChannelHandlerContext ctx) throws Exception {
System.out.println("채널 연결이 종료됨.");
}
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
logger.info("채널이 메시지 발송할 준비가 됨.");
ByteBuf messageBuffer = Unpooled.buffer();
messageBuffer.writeBytes(sendMsg.getBytes());
ctx.writeAndFlush( messageBuffer ); //메시지를 발송하고 flush처리
logger.info("발송 메시지 >" + sendMsg);
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
logger.info("메시지를 받는 메소드.");
ByteBuf buf = (ByteBuf)msg;
int n = buf.readableBytes();
if( n > 0 ) {
byte[] b = new byte[n];
buf.readBytes(b);
//수신메시지 출력
this.receiveMsg = new String( b );
logger.info("handler 수신된 메시지 >" + this.receiveMsg);
if(this.action_ != null) {
action_.receive(NettyClientHandler.this);
}
}
}
@Override
public void channelReadComplete(ChannelHandlerContext ctx) {
logger.info("메시지를 받는 동작이 끝나면 동작하는 메소드.");
}
}
- 채널이 발신 준비가되면 active메소드가 동작하고 발송할 메시지를 전달합니다.
- 응답이 오면 메시지를 읽어들이고 지역변수 receiveMsg에 담아주고 콜백 메소드 receive를 호출합니다.
- receive가 동작되면서 getReceiveMsg 메소드를 통해 전달받은 값을 받고, 현재의 채널을 종료처리 합니다.
- 보낼 메시지가 없을때까지 반복합니다.
실행결과
메시지 첫번째의 응답이 오면 채널이 종료되고 재연결되면서 두번째 메시지를 보낸다.
'JAVA' 카테고리의 다른 글
Netty Client 튜토리얼 - 03 (서버에 연결이 안 될 경우 재시도하기) (0) | 2020.12.04 |
---|---|
JAVA - Jsch를 활용한 sftp 전송 처리하기 (0) | 2020.12.02 |
JAVA - JConsole 활용기 (자바 Thread 동작, 종료 체크하기) (0) | 2020.11.30 |
Netty - Netty Client 튜토리얼 - 01 (0) | 2020.11.27 |
JAVA - 문자열에 숫자만 존재하는지 확인하기 (2) | 2020.11.19 |