- android - RelativeLayout 背景可绘制重叠内容
- android - 如何链接 cpufeatures lib 以获取 native android 库?
- java - OnItemClickListener 不起作用,但 OnLongItemClickListener 在自定义 ListView 中起作用
- java - Android 文件转字符串
哦,我多么希望 TCP 像 UDP 一样基于数据包! [查看评论] 但是,唉,事实并非如此,所以我正在尝试实现我自己的数据包层。这是到目前为止的事件链(忽略写入数据包)
哦,我的数据包结构非常简单:两个无符号字节作为长度,然后是字节 [长度] 数据。 (我无法想象它们是否更复杂,我会在 if
语句中听到我的耳朵!)
Server
处于无限循环中,接受连接并将它们添加到 Connection
列表中。PacketGatherer
(另一个线程)使用 Selector
来确定哪些 Connection.SocketChannel
已准备好读取。Connection
到 read()
。Connection
都有一个部分 IncomingPacket
和一个 Packet
列表,它们已被完全读取并正在等待处理。<read()
上:
IncomingPacket
读取更多数据。 (下面的IncomingPacket.readData
)IncomingPacket.complete()
),从中创建一个Packet
并将Packet
粘贴到列表中等待进行处理,然后将其替换为新的 IncomingPacket
。这有几个问题。首先,一次只读取一个数据包。如果 IncomingPacket
只需要多一个字节,那么这次只读取一个字节。这当然可以通过循环来解决,但它开始变得有点复杂,我想知道是否有更好的整体方法。
其次,IncomingPacket
中的逻辑有点疯狂,能够读取长度的两个字节,然后读取实际数据。这是代码,为了快速阅读而简化:
int readBytes; // number of total bytes read so far
byte length1, length2; // each byte in an unsigned short int (see getLength())
public int getLength() { // will be inaccurate if readBytes < 2
return (int)(length1 << 8 | length2);
}
public void readData(SocketChannel c) {
if (readBytes < 2) { // we don't yet know the length of the actual data
ByteBuffer lengthBuffer = ByteBuffer.allocate(2 - readBytes);
numBytesRead = c.read(lengthBuffer);
if(readBytes == 0) {
if(numBytesRead >= 1)
length1 = lengthBuffer.get();
if(numBytesRead == 2)
length2 = lengthBuffer.get();
} else if(readBytes == 1) {
if(numBytesRead == 1)
length2 = lengthBuffer.get();
}
readBytes += numBytesRead;
}
if(readBytes >= 2) { // then we know we have the entire length variable
// lazily-instantiate data buffers based on getLength()
// read into data buffers, increment readBytes
// (does not read more than the amount of this packet, so it does not
// need to handle overflow into the next packet's data)
}
}
public boolean complete() {
return (readBytes > 2 && readBytes == getLength()+2);
}
基本上,我需要有关我的代码和整个过程的反馈。请提出任何改进建议。如果您对如何更好地实现整个系统有建议,即使对我的整个系统进行大修也没关系。也欢迎推荐书籍;我喜欢书。我只是觉得有些地方不太对劲。
这是我根据 Juliano 的回答得出的一般解决方案:(如果您有任何问题,请随时发表评论)
public void fillWriteBuffer() {
while(!writePackets.isEmpty() && writeBuf.remaining() >= writePackets.peek().size()) {
Packet p = writePackets.poll();
assert p != null;
p.writeTo(writeBuf);
}
}
public void fillReadPackets() {
do {
if(readBuf.position() < 1+2) {
// haven't yet received the length
break;
}
short packetLength = readBuf.getShort(1);
if(readBuf.limit() >= 1+2 + packetLength) {
// we have a complete packet!
readBuf.flip();
byte packetType = readBuf.get();
packetLength = readBuf.getShort();
byte[] packetData = new byte[packetLength];
readBuf.get(packetData);
Packet p = new Packet(packetType, packetData);
readPackets.add(p);
readBuf.compact();
} else {
// not a complete packet
break;
}
} while(true);
}
最佳答案
可能这不是您正在寻找的答案,但有人会说:您可能对一个非常简单的问题的解决方案进行了过度设计。
在数据包完全到达之前,您没有数据包,甚至没有 IncomingPacket
。你只有一个没有定义意义的字节流。通常,simple解决方案是将传入数据保存在缓冲区中(它可以是一个简单的 byte[] 数组,但如果性能有问题,建议使用适当的弹性和循环缓冲区)。每次读取后,您检查缓冲区的内容,看看是否可以从那里提取整个数据包。如果可以,您构造您的Packet
,丢弃缓冲区开头的正确字节数并重复。如果或当您无法提取整个数据包时,您会将这些传入字节保留在那里,直到您下次从套接字中成功读取某些内容为止。
当你这样做时,如果你正在通过流 channel 进行基于数据报的通信,我建议你在每个“数据包”的开头包含一个魔数(Magic Number),以便你可以测试连接的两端仍然同步。如果由于某种原因(一个错误)其中一个从流中读取或写入错误数量的字节,它们可能会不同步。
关于java - 尝试用非阻塞 IO 打包 TCP 很难!难道我做错了什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2619082/
我是一名优秀的程序员,十分优秀!