Netty作为服务器,利用普通的socket进行调用时,服务器能收到客户端的请求信息,但是客户端收不到响应。最终定位到问题是:inputstram的read()方法被阻塞了,所以收不到响应信息。
解决方法:
在利用socket通信时,一般都会在报文内容前面添加10位数字(报文长度),先read()响应报文的前10位长度,然后根据长度去读取。这样就不会一直阻塞了。详细内容看代码:
服务端:
public class SocketServer {
private static final Logger logger = LoggerFactory.getLogger(SocketServer.class);
@Value("${server.port}")
private String port;
@PostConstruct
public void init() {
new Thread(new Runnable() {
@Override
public void run() {
bind();
}
}).start();
}
private void bind() {
try {
EventLoopGroup bossGroup = new NioEventLoopGroup(2); //bossGroup就是parentGroup,是负责处理TCP/IP连接的
EventLoopGroup workerGroup = new NioEventLoopGroup(100); //workerGroup就是childGroup,是负责处理Channel(通道)的I/O事件
ServerBootstrap sb = new ServerBootstrap();
sb.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.option(ChannelOption.SO_BACKLOG, 128) //初始化服务端可连接队列,指定了队列的大小128
.childOption(ChannelOption.SO_KEEPALIVE, false) //短连接
.childHandler(new ChannelInitializer<SocketChannel>() { // 绑定客户端连接时候触发操作
@Override
protected void initChannel(SocketChannel sh) throws Exception {
sh.pipeline()
.addLast(new MyDecoder(Charset.forName("UTF8"))) //解码request
.addLast(new MyEncoder(Charset.forName("UTF8")))
.addLast(new MyServerHandler()); //使用ServerHandler类来处理接收到的消息
}
});
//绑定监听端口,调用sync同步阻塞方法等待绑定操作完
ChannelFuture future = sb.bind(Integer.parseInt(port)).sync();
future.channel().closeFuture().sync();
}catch (Exception e){
logger.error(e.getMessage(),e);
}
}
}
class MyDecoder extends StringDecoder {
private static final Logger logger = LoggerFactory.getLogger(MyDecoder.class);
private Charset charset;
public MyDecoder(Charset charset) {
this.charset = charset;
}
@Override
protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) {
private StringBuffer sb = new StringBuffer();
String data = in.toString(charset);
try {
if (data != null && data.length() >= 10) {
data = data.substring(10);//去掉10位长度
}
sb.append(data);
} catch (NumberFormatException e) {
e.printStackTrace();
}
out.add(sb.toString());
}
}
class MyEncoder extends MessageToByteEncoder {
private Charset charset;
public MyEncoder(Charset charset) {
this.charset = charset;
}
@Override
protected void encode(ChannelHandlerContext ctx, Object msg, ByteBuf out) {
out.writeBytes(setLength((String) msg).getBytes(charset));
}
private String setLength(String str) {
// 完整报文:10位长度 + 报文内容
return String.format("%010d", str.getBytes(charset).length) + str;
}
}
class MyServerHandler extends SimpleChannelInboundHandler<String> {
private static final Logger logger = LoggerFactory.getLogger(AcceptMsgEntryBySocket.class);
@Override
protected void channelRead0(ChannelHandlerContext channelHandlerContext, String requestContent) throws Exception {
String response = "hello,world!";
System.out.println("请求数据:"+ requestContent);
channelHandlerContext.writeAndFlush(response);
System.out.println("响应数据:"+ response);
}
@Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
ctx.flush();
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
ctx.writeAndFlush(cause);
ctx.close();
}
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
}
}
客户端:
import org.apache.commons.io.FileUtils;
import java.io.*;
import java.net.InetSocketAddress;
import java.net.Socket;
public class SocketClient {
public static void main(String[] args) throws IOException {
String ip = "127.0.0.1";
int port = 8088;
String charSetType = "UTF-8";
String reqData = FileUtils.readFileToString(new File("src/test/java/socket请求报文.json"), charSetType);
OutputStream out = null; // 写
InputStream in = null; // 读
String respData = ""; // 响应报文
Socket socket = new Socket(); // 客户机
ByteArrayOutputStream bytesOut = null;
try {
socket.setTcpNoDelay(true);//关闭socket的缓冲,将数据立即发送出去
socket.setReuseAddress(true);
socket.setSoLinger(true, 0);
socket.setSendBufferSize(32*1024);//发送缓存区
socket.setReceiveBufferSize(32*1024);//接收缓存区
socket.setKeepAlive(false);//短连接
socket.connect(new InetSocketAddress(ip, port), 5000);
/**
* 发送TCP请求
*/
out = socket.getOutputStream();
String requestData = setLength(reqData, charSetType);
System.out.println("请求数据:" + requestData);
out.write(requestData.getBytes(charSetType));
out.flush();
/**
* 接收TCP响应
*/
socket.setSoTimeout(30000);//socket调用InputStream读数据的超时时间,以毫秒为单位
in = socket.getInputStream();
//1、读取数据长度
byte[] lenBytes = new byte[10];
in.read(lenBytes);
Integer length = Integer.valueOf(new String(lenBytes));
System.out.println("返回数据长度:" + length);
//2、读取报文数据
byte[] data = new byte[1024 * 4];
int len = 0;
bytesOut = new ByteArrayOutputStream();
Integer dataLen = 0;
while ((len = in.read(data)) != -1) {
bytesOut.write(data, 0, len);
dataLen = bytesOut.toByteArray().length;
if(length.equals(dataLen)){
break;
}
}
System.out.println("返回数据:" + bytesOut.toString(charSetType));
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (out != null) {
out.close();
}
if (socket != null) {
socket.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
private static String setLength(String str, String charSetType) throws UnsupportedEncodingException {
// 完整报文:10位长度 + 报文内容
return String.format("%010d", str.getBytes(charSetType).length) + str;
}
}