日期:2014-05-20  浏览次数:20789 次

java nio之SocketChannel read方法的问题
各位大虾,本人是初学者,请各位帮帮忙。项目要用到TCP通讯,多个客户端给服务器端发送数据,利用内部通讯协议,使用nio能保证没有丢包发生吗?还有,我将接收到的数据存放到ByteBuffer中,不同的客户端数据会不会连续存放呢?部分代码如下:
//如果sk对应的通道有数据需要读取
if (sk.isReadable())
{
// 获取该SelectionKey对应的Channel,该Channel中有可读的数据
SocketChannel sc = (SocketChannel) sk.channel();

// 定义准备执行读取数据的ByteBuffer
ByteBuffer buff = ByteBuffer.allocate(1024);
String content = "";
// 开始读取数据
try
{
while (sc.read(buff) > 0)
{
buff.flip();
byte[] b = new byte[buff.limit()];

buff.get(b);
content += UserFunctions.byteArray2HexString(b,b.length);
}
if(!map.containsKey(content.substring(14, 24)))
map.put(content.substring(14, 24),getSocketChannel());

// 打印从该sk对应的Channel里读取到的数据
System.out.println(content);
// 将sk对应的Channel设置成准备下一次读取
sk.interestOps(SelectionKey.OP_READ);
}
// 如果捕捉到该sk对应的Channel出现了异常,即表明该Channel
// 对应的Client出现了问题,所以从Selector中取消sk的注册
catch (IOException ex)
{
// 从Selector中删除指定的SelectionKey
sk.cancel();
if (sk.channel() != null)
{
sk.channel().close();
}
}
}//if(sk.isReadable())

------解决方案--------------------
不会,只能帮顶了。。。。。。。。
------解决方案--------------------
TCP保证不丢包

> 不同的客户端数据会不会连续存放呢
不太明白你说的连续存放是啥意思? 不同客户端存放的同一块内存的意思?
不同客户端,socket也不同,各自allocate了,就不会“连续存放”
------解决方案--------------------
1.tcp本身有一定容错性。但是你自己的协议必须也有必要的容错机制。比如,使用应答机制。
2. ByteBuffer 在使用时你只要作为局部对象使用是不可能出现同步问题的。也就是你说出现不同客户端的数据被写到同一个ByteBuffer对象中。(除非你自己刻意这么做)
3.比较明显的是你的代码在一个seletor.select()的大循环中。。。主要是看整体有没有问题。至少在你的这段代码中整体结构正确的。(每次都ByteBuffer buff = ByteBuffer.allocate(1024); )
4.其实catch (IOException ex)后应该尽量多判断情况,不一定close。还有就是有时底层tcp已经断掉,这时read()动作可能会返回<0但是没有exception(我遇到过 还有write也是)。。。
5.sk.interestOps(SelectionKey.OP_READ);这动作好像没必要吧。。。
------解决方案--------------------
...接上面

我最近也在研究这块内容...可以看看我的blog。研学了MINA的代码所以自己仿照作了一个类似的框架。哈哈。
------解决方案--------------------
从你上在这两个回复看来你是白研究了。
ByteBuffer只需每个线程分配一个,就可以反复使用,因为它只是作为读写缓冲而不是用户数据存储。没有必要为每个SelectKey分配一个ByteBuffer,如果是读事件在多个线程中处理的话,应该为每个线程分配一个ByteBuffer而不能跨线程共享。但同一线程中处理完第一个sk,只需复位ByteBuffer就可以为下一个sk使用,为什么要花很大我开销为每个sk分配一个ByteBuffer?

sk.interestOps(SelectionKey.OP_READ);这动作好像没必要吧。。。
你怎么知道一次Select就能把对方发送的数据读完?如果当好前没有读完难要阻塞在这里等待?
------解决方案--------------------
楼上是存心找吵架的 哈哈。。。作技术的难免这种人。。

我只是针对LZ问题回答,具体如何处理当然要去研究ByteBuffer的内部机制。你非要说LZ是错的那也没办法。。。。。(从性能角度出发每次都分配一个ByteBuffer当然不够优化)。。。但是对于每个线程只用一个buffer的方式我也可以说你“都白研究了”更好的方式是整个体系中只有一个大的bytebuffer封装体,然后每次使用都从这个封装体中去取然后手工释放也就有些人说的“memcache”。


你怎么知道一次Select就能把对方发送的数据读完?如果当好前没有读完难要阻塞在这里等待?
怎么说法呢?
1.我只看了这部分代码没有出现“sk.interestOps( sk.interestOps() & ~SelectionKey.OP_READ)”这样的行为。。。
2.去仔细研究一下selectorKey.interestOps()以及selectorKey.readOps()的机制。。。底层socket没有数据的时候会如何。。。看看基本行为就知道。。。不知道你的所谓阻塞从何说起。interestOps() OP_READ才会出现不断readOps()...

最后,讨论问题就讨论问题。。。“都白研究了”这种话先对自己说。。。省得人家说你“先学做人后学技术”






------解决方案--------------------
反驳你的胡说八道就叫找碴?让你在这里不懂装懂?

费话少说,以技术见高低。你上面的回复更说明你对NIO和ByteBuffer根本不理解(我没说一无所知,因为你只是了解这个名词)。

更好的方式是整个体系中只有一个大的bytebuffer封装体,然后每次使用都从这个封装体中去取然后手工释放也就有些人说的“memcache”。

你说这句话的时候已经说明连入门级还没到。多个线程中不同的KEY读取的数据在同一个bytebuffer中交叉存放,然后如果分配给每个具体的处理程序?“手工释放”这种搞笑的话也说得出来?如何手工?用手去操作内存?除了因为理屈词穷而想出这种混乱思维,你说的有些人在哪里见到这种使用方式?



对sk.interestOps(SelectionKey.OP_READ);的两点你说明什么?说明你去看了底层的操作?底层如何操作了?只说明你根本没有能力看懂sk.interestOps(SelectionKey.OP_READ);的作用。