简体   繁体   中英

Android(Java) Socket read hung when reading from a Netty Nio server

I've implement a Netty server with a pipeline like this:

 public void initChannel(SocketChannel ch) throws Exception {
     ChannelPipeline p = ch.pipeline();
     p.addLast(new ByteArrayDecoder(), new Byte2MsgHandler()
      , new ByteArrayEncoder(), new Msg2ByteHandler(), new MsgDispatcher());
 }

The business logic is in MsgDispatcher(). See below:

public void channelRead(ChannelHandlerContext ctx, Object msg)
        throws Exception {
    super.channelRead(ctx, msg);
    Msg message = (Msg) msg;
    switch (message.messageType) {
        case MType.SIGN_UP://sends response to the client
            if (userValidator.validate(message.user)) {
                userReg.signUp(message.user);
            } else {
                Msg error = new Msg.Builder().messageType(MType.ERROR)
                        .errorCode(Constants.USERREG_NULL).build();
                ctx.write(error.toByteArray());// write the response back
                ctx.flush(); //flush the stream
            }
            break;
    }
}

I can succesfully send data to the server with the following code from android:

public void writeAndFlush(Msg msg) throws IOException {
    try {
        bos.write(msg.toByteArray());
        bos.flush();
    } catch (IOException e) {
        throw new IOException("Exception writing and flushing Msg to network.", e);
    }
}

bos is a bufferedOutputstream from the socket.

When I close the serversocket after writing I can sucessfully read the data.

Msg error = new Msg.Builder().messageType(MType.ERROR)
                        .errorCode(Constants.USERREG_NULL).build();
                ctx.write(error.toByteArray());// write the response back
                ctx.flush(); //flush the stream
                ctx.close(); //this result in sucessfull read on android socket.

But (without "ctx.close() in the above server code"), it hungs in the while loop(code below):

public Msg read() throws IOException {
    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    byte[] buffer = new byte[1024];
    int read = 0;
    try {
        while ((read = bis.read(buffer, 0, buffer.length)) != -1) {
            Log.e("com.traderreg", "" + read);
            baos.write(buffer, 0, read);
        }
        baos.flush();
    } catch (IOException e) {
        throw new IOException("error reading bytes from socket", e);
    }
    try {
        return wire.parseFrom(baos.toByteArray(), Msg.class);
    }catch (IOException e){
        throw new IOException("error parsing Msg from socket", e);
    }
}

What is the problem?

bis.read() will block until there are bytes available, or the stream is closed. This is why closing the socket causes read() to return. If you want to read in a non-blocking manner then you need to take a look at the classes in the java.nio.channels package. However, you don't really want to be continuously polling for data. The classes in java.nio.channels are really designed for handling multiple sockets within a single thread rather than encouraging polling of the socket (although I'm aware that games sometimes poll a socket as part of their game loop).

Regardless of whether you're working with blocking or non-blocking I/O you need some method of delimiting messages. The suggestion to prepend each message with a length field is a simple, but effective solution. Then your thread loops continuously reading messages and dispatching them to code that can process them.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM