各位大虾,本人是初学者,请各位帮帮忙。项目要用到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())
//如果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())
解决方案 »
- java(se) 怎么实现窗体的渐显效果
- MANIFEST.MF中怎样设置程序启动内存大小
- 环境(eclipse ,tomcat5.0)中出现错误:HTTP Status 500,帮助!!
- 如何在控制台下接受输入的时候不回显键盘敲入的字符?
- 巨白痴问题,如何让jtextfield中的内容实时变化
- 问个小问题.
- 关于ResultSetMetaData的问题
- 急急急,求SQL Server 7.0 Jdbc驱动(在线等,解决后结贴)
- 初学者!!求教....
- java学到什么水平,可以去应聘JAVA程序员?希望大家踊跃讨论!!!!!
- java for循环问题
- 关于JAVA SE中克隆clone方法的问题
不太明白你说的连续存放是啥意思? 不同客户端存放的同一块内存的意思?
不同客户端,socket也不同,各自allocate了,就不会“连续存放”
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);这动作好像没必要吧
ByteBuffer只需每个线程分配一个,就可以反复使用,因为它只是作为读写缓冲而不是用户数据存储。没有必要为每个SelectKey分配一个ByteBuffer,如果是读事件在多个线程中处理的话,应该为每个线程分配一个ByteBuffer而不能跨线程共享。但同一线程中处理完第一个sk,只需复位ByteBuffer就可以为下一个sk使用,为什么要花很大我开销为每个sk分配一个ByteBuffer?sk.interestOps(SelectionKey.OP_READ);这动作好像没必要吧
你怎么知道一次Select就能把对方发送的数据读完?如果当好前没有读完难要阻塞在这里等待?
你怎么知道一次Select就能把对方发送的数据读完?如果当好前没有读完难要阻塞在这里等待?
怎么说法呢?
1.我只看了这部分代码没有出现“sk.interestOps( sk.interestOps() & ~SelectionKey.OP_READ)”这样的行为
2.去仔细研究一下selectorKey.interestOps()以及selectorKey.readOps()的机制底层socket没有数据的时候会如何看看基本行为就知道不知道你的所谓阻塞从何说起。interestOps() OP_READ才会出现不断readOps()...最后,讨论问题就讨论问题“都白研究了”这种话先对自己说省得人家说你“先学做人后学技术”
对于同一次交互中比较大的数据,必须使用sk.interestOps(SelectionKey.OP_READ);来多次读取。无论你出于什么原因只要你知道他的作用就不可能说它不必要。就象阻塞方法中我们while((len = in.read(buf)) > 0){....};如果你说while不必要的话,那只能说明你根本不懂IO操作。理论上即使对方发两个字节的数据,一次select也可能只读到第一次字节,除非在第一次select时,已经读到的数据中包含中约定好的结束标记或已经知道的长度。而楼主的代码只是将读到的数据打印出来,既没有判断是否已经读到结束标记,也没判断已经读到的数据长度是否达到多少而不需再读,那么sk.interestOps(SelectionKey.OP_READ);来读下次数据怎么会不必要?只要你懂这段代码的意思就不可能说出这样的话。说出这样的话只能代表你不懂。
你长篇大论那么多无非是在说读取不一定能完全读完底层OS级缓冲中的数据。但是你要知道,不用重复注册就能不断得到readyop(OP_READ)为真这个道理。你不信可以去写个最最简单的例子尝试一下,底层没有数据的时候会怎么样,然后一旦有数据后需不需要重复interestOps()动作你不做测试也没关系看看MINA核心部分代码再说话如果你非要这样攻击别人么,我奉陪你的水平也就这种程度而已,不懂装懂的程度。
当客户端和服务端进行通讯时,因为是非阻塞的,所以在正在进行READ操作时,用户也可能提交WRITE操作。
如果一个KEY同时注册了READ,WRITE操作。那么正在READ时,如果协议栈的写缓冲准备好。要不要触发WRITEABLE?那么当前的写操作会不会被CONTENT SWITCH?而如果我知道我当前的任务是埋头苦读,我就不允许当前KEY触发操作,所以k.interestOps(SelectionKey.OP_READ)把这个KEY的操作完全置为READ,我一门心思读完。
没有这个操作当然可以读到数据,但能说明k.interestOps(SelectionKey.OP_READ)没有必要吗?
因为程序员知道我现在不必要写,所以即使写缓冲状态是WRITEABLE请你也不要通知我,如果没有这个操作,虽然能读到数据,但其中可能多次被注册的其它操作的事件把线程处理抢占过去。假如进行k.interestOps(SelectionKey.OP_READ)用了10ms读完了所有数据,没有这个操作可能要50ms(当然我只是举例而已)。
你自己说这个操作有没有必要?
就是你这样的人才导致现在学Java的人越来越浮躁明明就简单问题的“可与否”却要对口头用于纠缠不清就像人家说对你说“你好”,你回答人家“你怎么知道我好?”一个样子你自己去仔细思考一下为啥所有的用nio做的框架都用“反应堆”模式而且其模式中基本都是单线程完成selector的select()及其后续具体IO只是将IO得到的数据分发到线程池中进行分发处理。。
那是因为本身具体IO就是应该有序的无论你先读或先写。对于上层来说需要也应该有序而对于底层,虽然nio对貌似read行为和write是对应类似的但是事实上注意“事实上”注册OP_READ与OP_WRITE导致的效果是不一样的。
1。注册OP_READ后如果不去显示注销(即interestOps(interestOps() & ~SelectionKey.OP_READ)操作)那么出现效果为:select()会关心可以读的数据(或连接失败情况),只有这种情况会触发释放select()操作。当没有数据的时候根本不会关注。注意:“这就是说只要你需要一直读取数据,就没必要去注销OP_READ操作。”既然没有注销操作,而且该注册一直有效那在重复注册有何意义?不是多余是啥?
2。而注册OP_WRITE后反而会导致不断的导致select()释放,最终导致select无法正常休眠而使CPU占用率达到100%。因为其真正关注的只是interestOps(SelectionKey.OP_WRITE)操作。所以,就算是你所说同时注册OP_READ以及OP_WRITE也应该控制OP_WRITE的注销与注册而不是OP_READ。如果说要整体wirte控制流量也轮不到进行k.interestOps(SelectionKey.OP_READ)而是应该在其他地方用k.interestOps(k.interestOps() & ~SelectionKey.OP_WRITE)来注销写关注;如果只是读就更没有这个必要了。OP_READ这册后没有显示注销那就一直有效,你一次没读完的底层数据可以直接下次select()继续读。之前已经说了很清楚了。。更何况对于上层操作一般都是有序操作操作。对于NIO的读写方式的论述就知道没有做过真正可靠的NIO,只研究研究buffer clear和filp的行为就以为很了不起了么?
连LZ想表达的意义都没有看明白,就在这里随便攻击别人。去研究你的“xyz”编译期常量把。。
楼主的代码中的key你根本不知道它是在同一线程中处理还是在多线程中委派的。正因为如此,在我读的时候我根本不知道其它线程是否会注册WRITE操作来打扰我的READ操作。所以我每读完一次才
sk.interestOps(SelectionKey.OP_READ),即把这个KEY上的所有任何操作都注销让它只关心READ操作。你自己都知道“而注册OP_WRITE后反而会导致不断的导致select()释放,最终导致select无法正常休眠而使CPU占用率达到100%。”那么我怎么知道在我读的时候这个KEY有没有被其它线程注册WRITE操作呢?为了保证我只关心READ操作所以我才用sk.interestOps(SelectionKey.OP_READ)把这个KEY上的所有操作都更新为READ.你自己说的话不正是我这个观点的解释吗?自己煽自己的耳光去吧!
sk.interestOps(SelectionKey.OP_READ),即把这个KEY上的所有任何操作都注销让它只关心READ操作。 我之前也说的很清楚了,sk.interestOps(SelectionKey.OP_READ)这个就事多余的而已在write到相关的地方
k.interestOps(k.interestOps() & ~SelectionKey.OP_WRITE)以及k.interestOps(SelectionKey.OP_WRITE)
来控制write行为而与是否重复注册sk.interestOps(SelectionKey.OP_READ)有关系。。并不是说
sk.interestOps(SelectionKey.OP_READ)因为只注册了sk.interestOps(SelectionKey.OP_READ)导致取消了WRITE就是对的。。因为他的代码中根本没有要说明WRITE相关的操作。所以这句话就是多余的扇耳光的说法就留给你自己吧。。
我个人认同 wfeng007 的观点。sk.interestOps(SelectionKey.OP_READ)这条语句没有这个必要,只要你不是显式的注销 SelectionKey.OP_READ,系统就会一直监测是否有数据可读。 tcp 是全双工的,可以同时接收和发送,不是axman 所说的一定要把通道所有的数据都读完了才能用通道去发送数据。设想如果接收的数据量很大的话那岂不是把数据发送的任务给耽搁了,所以这个是完全错误的。当有数据需要发送的话就去注册 SelectionKey.OP_WRITE 事件,不用管是否有数据要读,把接收和发送缓冲区分别管理好,同一个socketchannel可以在不同的线程里同时读和写操作.只要注意数据发送完了就要注销 SelectionKey.OP_WRITE 事件,以避免 cpu 100%,这个我看两位都应该是明白的。