我正在编写在TCP上运行的自定义协议搜索服务。使用EmbeddedChannel进行测试时,一切都很完美。为了进一步测试,我编写了一个服务器并添加了处理程序。通过普通java Socket客户端的请求,服务器接收数据,处理并发回响应。但是,响应未到达客户端套接字。我想可能是我搞乱了大型的channelPipeline。所以我只将实现减少到一个入站处理程序。仍然无法正常工作。有人可以帮忙吗?
服务器:
public void start() throws Exception{
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
final KaiExceptionHandler kaiExceptionHandler = new KaiExceptionHandler();
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
ChannelPipeline pipeline = socketChannel.pipeline();
pipeline.addLast(new SimpleHandler());
}
});
ChannelFuture future = b.bind(new InetSocketAddress("localhost", 9400)).sync();
future.addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture channelFuture) throws Exception {
if(channelFuture.isSuccess()) {
LOGGER.info("Kai Server is bounded to '{}'", "localhost:9400");
}else {
LOGGER.error("Failed to bound Kai to 'localhost:9400'", channelFuture.cause());
}
}
});
future.channel().closeFuture().sync();
}finally {
workerGroup.shutdownGracefully();
bossGroup.shutdownGracefully();
}
简单处理程序:
public class SimpleHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
Charset charset = Charset.defaultCharset();
ctx.write(Unpooled.copiedBuffer("Client is not seeing this", charset));
ctx.flush();
}
@Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
ctx.flush();
} }
测试客户端。一个不整洁的实现。但是,仅供测试。
public class TestClient {
public static void main(String[] args) throws Exception {
Socket socket = new Socket("localhost", 9400);
InputStream is = socket.getInputStream();
StringBuilder sb = new StringBuilder();
byte[] buffer = new byte[64];
int r = 0;
socket.setSoTimeout(10000);
System.out.println("Reading...");
while ((r = is.read(buffer)) != -1) {
sb.append(new String(buffer).trim());
}
System.out.println("String: " + sb.toString());
}
}
答案 0 :(得分:1)
您的测试程序假设在写入数据后连接已关闭,并且假设字符串在发送多字节字符的情况下不会被分段。
如果故意等待套接字关闭,则需要将write
语句更改为以下内容:
ctx.write(Unpooled.copiedBuffer("Client is not seeing this", charset))
.addListener(ChannelFutureListener.CLOSE);
然而,这种通信方式效率低下,因为每次想要向服务器说些什么时都需要重新打开另一个套接字连接。最好的方法是使协议基于行或由长度字段分隔,然后使用BufferedReader
在客户端中逐行读取响应,并且在服务器端,您应该在末尾添加换行符每条消息。
如果服务器需要接收消息,您应该在管道的开头添加new LineBasedFrameDecoder()
,然后添加一个可选的new StringDecoder()
,让netty自动将ByteBuf
转换为字符串,你不必再这样做了。 Netty也可以使用StringEncoder
反向执行此操作,因此您只需编写一个字符串对象,而不是每次都在ByteBuf
中包装它。
答案 1 :(得分:0)
看到最后一个问题。整个问题出在测试客户端上。
第一个问题:
第一个由Ferrybig指出,其中read(buffer) != -1
期望插座关闭(感谢那个人)。
第二个问题:
第二个是,ChannelInboundHandlerAdapter#channelRead(ChannelHandlerContext ctx, Object msg)
永远不会被调用,因为套接字不发送任何内容,因此没有任何内容可读。编辑我的客户端以发送内容使其正常工作。
新客户:
public class TestClient {
public static void main(String[] args) throws Exception {
Socket socket = new Socket("localhost", 9400);
socket.setSoTimeout(10000);
OutputStream raw = socket.getOutputStream();
// Will be the request bytes
raw.write(1);
raw.flush();
InputStream is = socket.getInputStream();
StringBuilder response = new StringBuilder();
// actual expected size will be here
final int expectedResponse = 128;
byte[] buffer = new byte[expectedResponse];
int bytesRead = is.read(buffer);
if (bytesRead != -1) {
response.append(new String(buffer).trim()).append("\n");
}
System.out.println("String: " + response.toString());
}
}