2011-11-07 64 views
1

在我的使用java nio的程序中,当它试图连续写入10 KB消息时,socketchannel.write()变得非常慢。测量写完整10 KB消息的时间在160 ms和200 ms之间。但编写完整的5 KB消息的时间仅需要0.8 ms。当消息大小很大时,socketchannel.write()变得非常慢

在选择器中,我只有Selection.OP_READ并且不处理Selection.OP_WRITE。当收到一个大的完整消息时,它将被写入另一个接收器4次。

有人遇到同样的问题吗?有一个关于socketchannel.write()缓慢的文章。我的问题是如何替换OP_READ和OP_WRITE之间的变化?

如果我添加一个inerval,例如150毫秒,响应时间会缩短。有什么办法可以找到缓冲区已满的时间,这样我可以让程序等待。我的操作系统是Windows XP。

谢谢。

我按照EPJ建议通过检查写入的字节数。但是响应时间依然很高。我在这里发布我的部分代码,并希望检查我的代码是否有问题。

//这是使用NIO的写数据()部分:

 while (buffer.hasRemaining()) { 
     try {   
       buffer.flip();     
       n = socket.write(buffer);   
       if(n == 0) {     
        key.interestOps(SelectionKey.OP_WRITE); 
        key.attach(buffer);    
        break; 
       }        
     } catch (IOException e) {    
      e.printStackTrace(); 
     } finally { 
      buffer.compact(); 
     } 
    } 

    if(buffer.position()==0) {     
     key.interestOps(SelectionKey.OP_READ); 
    } 
+0

我想它正试图永久发送10KB的一个单一的TCP数据包,它失败了很多。 –

+0

消息连续发布。拓扑结构是一系列节点。消息间隔为100毫秒 – susan

+1

@MartijnCourteaux号TCP分组发生在较低级别。它不会尝试发送IP包>路径MTU。 – EJP

回答

1

如果写入时间超过20微秒,我建议你有一个缓冲区满的问题。我假设你正在使用阻塞NIO。当发送缓冲区未满时,通常需要5-20微秒。在过去,我已经配置了我的服务器来杀死需要2毫秒写入的缓慢消费者。 (可能有点侵略性;)

您可以尝试增加发送缓冲区的大小(Socket.setSendBufferSize(int),它也可用于SocketChannels),但它会显示您正尝试发送更多数据你的带宽允许。

10 KB不是一个很大的消息,典型的发送缓冲区大小是64 KB,因此为了使它满,你需要有6-7条消息未发送。这可能解释5KB的速度相对较快。

+0

谢谢。但是我正在使用Java非阻塞套接字。当服务器收到消息时,它将其转发到下一个中​​继站4次。我不会将选择键更改为OP_WRITE。当我写选择键是OP_READ。我想我的问题是tcp缓冲区已满。如果我的猜测是正确的,如何解决这个问题?关闭socket通道会有帮助吗? – susan

+0

确定为什么你没有足够的带宽或缓慢的消费者并修复它是有帮助的。如果你无法控制,关闭套接字是最后的手段。顺便说一句:如果你正在使用非阻塞,它不应该在写入时阻塞,它应该只是写不出任何数据! –

3

我建议您的读取过程很慢,并且这会导致其接收缓冲区备份,这会导致您的发送缓冲区备份,从而导致发送失速。

否则你还没有正确写入非阻塞模式的代码。如果您从write()方法得到零结果,您必须(a)将interestOps更改为OP_WRITE并(b)返回到您的选择循环。当你得到OP_WRITE时,你必须重复写入;如果您编写了所有数据,请将interestOps更改回OP_READ,否则保持原样并等待下一个OP_WRITE。如果您尝试在非阻塞模式下进行循环,即使存在零长度写入,您也会旋转,浪费CPU周期和时间。

模错误:

while (buffer.position() > 0) 
{ 
    try 
    { 
    buffer.flip(); 
    int count = ch.write(buffer); 
    if (count == 0) 
    { 
     key.interestOps(SelectionKey.OP_WRITE); 
     break; 
    } 
    } 
    finally 
    { 
    buffer.compact(); 
    } 
} 
if (buffer.position() == 0) 
{ 
    key.interestOps(SelectionKey.OP_READ); 
} 
+0

@susan你正在做一个全新的注册。我说要将interestOps更改为OP_WRITE,并且我还表示在成功写入时将它们更改回OP_READ。你没有这样做。另一个问题是,无论写入状态如何,您都应该在flip()和write()之后始终* compact(),否则缓冲区将处于无效状态。另一个是write()永远不会返回<0:请参阅Javadoc。 – EJP

+0

如果有一些示例代码,它将非常有帮助。改变windows系统的tcp缓冲区有帮助吗? – susan

+0

@susan请参阅上文。 Windows套接字的发送和接收缓冲区在8k时是非常小的,并且将它们碰到32k或48k总是有帮助的。不过,我相信你有一个编码问题,它不会解决。 – EJP

相关问题