AsynchronousSocketChannel 一次写入/读取所有消息

Posted

技术标签:

【中文标题】AsynchronousSocketChannel 一次写入/读取所有消息【英文标题】:AsynchronousSocketChannel write/read all messages in one 【发布时间】:2014-04-24 13:24:03 【问题描述】:

尊重,

我尝试使用新的Java NIO2在客户端和服务器端创建异步SocketChannel并进行通信,但问题是我发送到服务器上的套接字的所有消息,套接字都作为一条消息读取。这是代码:

我创建用于写入和读取数据的处理程序:

读取处理程序:

public class ReadHandler implements CompletionHandler<Integer, Msg> 


private AsynchronousSocketChannel _socket;
private SocketHandler _socketHandler;

private ByteBuffer _buffer;

public ReadHandler(SocketHandler socketHandler) 

    this._socketHandler = socketHandler;
    _buffer = ByteBuffer.allocate(100);

    this._socket = this._socketHandler.getSocket();
    this._socket.read(_buffer, null, this);


@Override
public void completed(Integer result, Msg attachment) 

    System.out.println("readdddd " + result);

    String message = new String(_buffer.array());
    System.out.println("mess:" + message);



@Override
public void failed(Throwable exc, Msg attachment) 
    System.out.println(exc.getMessage());

ClientWriteHandler

public class ClientWriteHandler implements CompletionHandler<Integer, Msg> 

private AsynchronousSocketChannel _socket;
private ClientSocket _clientHandler;

private ByteBuffer _buffer;

public ClientWriteHandler(ClientSocket clientHandler) 


    this._clientHandler = clientHandler;
    _buffer = ByteBuffer.allocate(2048);
    this._socket = this._clientHandler.getSocket();


@Override
public void completed(Integer result, Msg attachment) 

    System.out.println("write " + result);
    _buffer.clear();


@Override
public void failed(Throwable exc, Msg attachment) 
    System.out.println(exc.getMessage());


public void write(String data) 
    _buffer = ByteBuffer.allocate(2048);
    this._socket.write(_buffer.wrap(data.getBytes()), new Msg(), this);


然后我调用 write 方法 2 次​​p>

socket = AsynchronousSocketChannel.open();
        socket.connect(new InetSocketAddress("localhost", port)).get();
        writeHandler = new ClientWriteHandler(this);
        writeHandler.write("hellooo server :)");
        writeHandler.write("hellooo server again :)");

我尝试在 ByteBuffer 上使用 clear() 函数,但没有效果。有什么建议吗?

【问题讨论】:

【参考方案1】:

WritePendingException 不是因为缓冲区已满而引发的。它被抛出是因为写入未完成但另一个开始写入。

【讨论】:

【参考方案2】:

您在第一个 .write() 调用中发送了一些字节,并在第二次调用 .write() 中发送了更多字节。服务器接收它们。 TCP是面向字节的。如果您想要消息之类的内容,则必须将自己发送的消息分开,例如通过特殊的换行符或 XML 标记。

【讨论】:

好的,但是如果我的 ByteBuffer 已满怎么办,我尝试清除 bytebuffer 但随后我收到错误消息 Exception in thread "main" java.nio.channels.WritePendingException【参考方案3】:

我在这方面工作,我找到了解决方案及其工作,正如我想象的那样。

在 ClientWrite 处理程序内部,我添加了数据列表并在 try..catch 中设置写入方法以检查 write() 是否已完成,如果仍在写入,我将新字符串添加到数据列表中。当 write 方法完成后,我会检查数据列表中是否有新消息并再次写入消息。

public class ClientWriteHandler implements CompletionHandler<Integer, ByteBuffer> 

private AsynchronousSocketChannel _socket;
private ClientSocket _clientHandler;

private ByteBuffer _buffer;
private List<String> datas;

private boolean finished;

public ClientWriteHandler(ClientSocket clientHandler) 


    this._clientHandler = clientHandler;
    _buffer = ByteBuffer.allocate(2048);
    this._socket = this._clientHandler.getSocket();
    finished = true;
    datas = new ArrayList<>();


@Override
public void completed(Integer result, ByteBuffer attachment) 

    System.out.println("client write complete " + result);

    if(datas.size() > 0) 
        this._socket.write(_buffer.wrap(datas.remove(0).getBytes()), _buffer, this);
    
    else 
        /////
    


@Override
public void failed(Throwable exc, ByteBuffer attachment) 
    System.out.println(exc.getMessage());


public void write(String data) 

    try 
        //finished = false;
        this._socket.write(_buffer.wrap(data.getBytes()), _buffer, this);
    catch(WritePendingException ex) 

        datas.add(data);
    

我还在附件中发送缓冲区。 读取完成后在 ReadHandler 内部清除 ByteBuffer 并再次调用 read() 方法,所以下一次读取我得到了新行,现在我不需要设置行分隔符。

public class ReadHandler implements CompletionHandler<Integer, ByteBuffer> 


private AsynchronousSocketChannel _socket;
private SocketHandler _socketHandler;

private ByteBuffer _buffer;

public ReadHandler(SocketHandler socketHandler) 

    this._socketHandler = socketHandler;
    _buffer = ByteBuffer.allocate(2048);

    this._socket = this._socketHandler.getSocket();
    this._socket.read(_buffer, _buffer, this);


@Override
public void completed(Integer result, ByteBuffer attachment) 

    attachment.flip();
    System.out.println("readdddd " + result);

    String message = new String(attachment.array());
    System.out.println("mess:" + message);

    _buffer.clear();
    this._socket.read(_buffer, _buffer, this);



@Override
public void failed(Throwable exc, ByteBuffer attachment) 
    System.out.println(exc.getMessage());

现在这很好用,但是当我继续处理更复杂的东西时,我会检查这段代码的行为。你觉得这个解决方案可以吗?

【讨论】:

以上是关于AsynchronousSocketChannel 一次写入/读取所有消息的主要内容,如果未能解决你的问题,请参考以下文章

Java NIO2 AsynchronousSocketChannel/AsynchronousServerSocketChannel 和 TLS/SSL

AsynchronousSocketChannel 一次写入/读取所有消息

AIO异步非阻塞学习

异步通道和异步运算结果

TCP_AIO_Server_ZC_01

Java - 异步套接字通道无法连接到远程异步服务器套接字通道