使用异步套接字

时间:2016-09-12 18:30:43

标签: java sockets networking tcp packet

因此,我正在构建一个基于AsynchronousSocketChannel类的小型客户端< - >服务器通信应用程序。 我设法使一切正常工作:服务器运行,客户端能够连接,数据包正在发送/接收都很好。 至少在我尝试一个接一个地发送一堆数据包之前(比如3个数据包),起初我遇到了一个WritePendingException,我能够在稍微搜索一下之后修复它,但数据包仍然没有正确发送。 从我的调试看起来似乎服务器尝试发送3个数据包,它成功发送所有3个,但客户端只接收前2个,我似乎无法理解问题出在哪里,并且在控制台上根本没有错误,如果需要进一步调试帮我问一下:

创建数据包时,它们会被添加到_sendQueue中,如下所示:

_sendQueue.add(packet);

_sendQueue是ByteBuffers的ConcurrentLinkedQueue(它是我的数据包)。 然后通过以下方法发送数据包:

public final void executeWriteTask()
{
    if (!_sendQueue.isEmpty())
    {
        _writeLock.lock();
        if (!_pendingWrite)
        {
            _pendingWrite = true;

            ThreadPool.execute(() ->
            {
                final PacketWriter packet = _sendQueue.poll();
                final ByteBuffer duplicate = packet.getBuffer().duplicate();
                System.out.println("Sending packet opcode: " + duplicate.getInt());
                _channel.write(packet.getBuffer(), this, _writeHandler);
            });
        }
        _writeLock.unlock();
    }
}

在写入每个数据包之后,将再次为队列中的下一个数据包调用该方法,因此基本上所有3个数据包都被发送,这就是我的println显示的内容:

Sending packet opcode: 0
Sending packet opcode: 1
Sending packet opcode: 1

哪个好,但在客户端:

Received packet opcode: 0
Received packet opcode: 1

我尝试了很多东西,比如尝试在3秒后再次手动调用读取但是没有待接收的数据包,我也尝试在3秒后从服务器发送第3个数据包并且它有效,但是那个&#39 ;不是我想要的,只是检查问题是否确实来自我试图立即发送3个数据包的事实,我发现它是。 那么第三个数据包去哪了?我想不出更深入地调试它的方法......任何帮助都会受到赞赏。

注意我确实希望自己构建它,所以不要向我推荐任何预先制作的通信代码。

1 个答案:

答案 0 :(得分:0)

嗯,它仍然没有直接回答我的问题,但它似乎解决了这个问题。 我想有一些防止数据包泛滥的机制,所以我决定不再一个接一个地发送数据包,而是将它们分批编写为1个数据包然后作为一个整体发送,如下所示:

public final void executeWriteTask()
{
    if (!_sendQueue.isEmpty())
    {
        _writeLock.lock();
        if (!_pendingWrite)
        {
            _pendingWrite = true;

            ThreadPool.execute(() ->
            {
                final Queue<PacketWriter> copy = new ConcurrentLinkedQueue<>();
                while (!_sendQueue.isEmpty())
                    copy.add(_sendQueue.poll());

                int bytes = 0;
                for (final PacketWriter packet : copy)
                    bytes += packet.getBuffer().limit();
                final ByteBuffer toSend = ByteBuffer.allocateDirect(bytes);
                for (final PacketWriter packet : copy)
                    toSend.put(packet.getBuffer());
                toSend.flip();

                _channel.write(toSend, this, _writeHandler);
            });
        }
        _writeLock.unlock();
    }
}

然后在客户端:

@Override
public void readPacket()
{
    final ByteBuffer buffer = getReader().getBuffer();
    buffer.flip();

    while (buffer.hasRemaining())
    {
        final int opCode = getReader().readInt();
        final PacketInfo inf = PacketInfo.values()[opCode];
        final IIncomingPacket<GameClient> packet = inf.newIncomingPacket();
        packet.read(this, getReader());

        if (inf.isAuthedState() == isAuthed())
            packet.run(this);
    }
    buffer.clear();

    getChannel().read(buffer, this, getReadHandler());
}