2011-11-07 96 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

相關問題