2013-10-06 75 views
0

我的問題是如何從服務器接收1024字節的包裹中的服務器示例「切割」的數據,導致答案來自服務器的數據包如2部分我不知道如何解決這個問題。例如,當第一個數據包到達時,服務器通知的大小爲1988並且收到的數量爲1444,但沒有問題,但是當第二個數據包到達時,通知的大小類似於808333347並且收到540,則1444 + 540 = 1984的總和就是正確的。我不知道這個號碼808333347來自哪裏。 我爲此解決方案使用googling,然後教導使用udp,我需要這個tcp/ip連接。 請幫我解決一下。Java nio從通道012讀取字節緩衝區

類:

public class Connection implements Runnable { 
    private static Connection  instance; 
    private   SocketChannel channel  = null; 
    private   int   port   = 0; 
    private   int   service  = 0; 
    private final int   SOCKET_TIMEOUT = 15 * 1000; 
    private final int   SOCKET_BYTES = 16 * 1024; 
    private final Charset  CHARSET  = Charset.forName("ISO-8859-1"); 
    private   String   host   = null; 
    private   String   message  = ""; 

public Connection(String host, String port){ 
    this.host  = host; 
    this.port  = Integer.parseInt(port); 
} 
public static Connection createConnection(String host, String port) { 
    if(instance == null){ 
     instance = new Conexao(host, port); 
    } 
    return instance; 
} 
public void connect(){ 
    try{ 
     instance.channel = SocketChannel.open(); 
     instance.channel.socket().setSoTimeout(SOCKET_TIMEOUT); 
     instance.channel.socket().setTcpNoDelay(false); 
     instance.channel.socket().setKeepAlive(true); 
     instance.channel.connect(new InetSocketAddress(host, port)); 
     instance.channel.configureBlocking(false); 
    } catch (IOException ioe){ 
     Log.d(TAG, ioe.getMessage() + " " + ioe.toString()); 
    } 
} 
@Override public void run() { 
    if(null != instance.channel){ 
     if(instance.channel.isConnected()){ 
      Log.d(TAG, "CHANNEL CONNECTED = TRUE"); 
     } else { 
      Log.d(TAG, "CHANNEL CONNECTED = FALSE"); 
     } 
    } else { 
     instance.connect(); 
     Log.d(TAG, "CHANNEL CONNECTED"); 
    } 
    sendMessage(); 
    while(true){ 
     receiveMessage(); 
    } 
} 
public void sendMessage() { 
    int   size = message.length(); 
    ByteBuffer buffer = ByteBuffer.allocate(4 + 4 + size); 
    buffer.putInt(service).putInt(size).put(message.getBytes()); 
    buffer.flip(); 
    for(int i = 0; i < size; i++){ 
     try { 
      instance.channel.write(buffer); 
     } catch (IOException ioe) { 
      Log.d(TAG, ioe.getMessage() + " " + ioe.toString()); 
     } 
    } 
} 
public void receiveMessage(){ 
    ByteBuffer buffer  = ByteBuffer.allocateDirect(SOCKET_BYTES); 
    int   bytesReaded = 0; 
    String  received = ""; 
    buffer.clear(); 
    try { 
     do { 
      bytesReaded = instance.channel.read(buffer); 
     } while (bytesReaded == 0); 
    } catch (IOException ioe) { 
     Log.d(TAG, ioe.getMessage() + " " + ioe.toString()); 
    } 
    buffer.flip(); 
    int size = buffer.getInt(); 
    received += CHARSET.decode(buffer); 
    Log.d(TAG,"SERVIÇE: " + size + "/" + received.length() + " MSG: " + received); 
} 
public int getService() { 
    return service; 
} 
public void setService(int service) { 
    this.service = service; 
} 
public String getMessage() { 
    return message; 
} 
public void setMessage(String message) { 
    this.message = message; 
} 

我改變函數是這樣的:

public void receiveMessage(){ 
    ByteBuffer buffer  = ByteBuffer.allocateDirect(SOCKET_BYTES); 
    int   bytesReaded = 0; 
    String  received = ""; 
    buffer.clear(); 
    try { 
     bytesReaded = instance.channel.read(buffer); 
    } catch (IOException ioe) { 
     Log.d(TAG, ioe.getMessage() + " " + ioe.toString()); 
    } 
    buffer.flip(); 
    if(bytesReaded >= 4){ 
     if(size == 0 && size < 5000) size = buffer.getInt(); 
     received += CHARSET.decode(buffer); 
     answer  += received; 
     if(size == answer.length()){ 
      Log.d(TAG,"SERVICE: " + size + "/" + answer.length() + " " + answer); 
     } 
    } 
} 

但現在是非常難看。

回答

0

從任何一端您都無法控制數據如何到達TCP連接。它可以一次到達一個字節,也可以按照數據的大小達到任何其他數量。你必須在接收端循環,直到你有你需要的一切。您需要爲套接字的大小使用相同的讀緩衝區,以便您可以累積此循環的數據,並保留可能屬於後續消息的任何數據。

「大小通知」不能像你說的那麼大。這只是一個bug的結果。可能你會失去同步。

+0

我理解並解決了積累數據的問題,現在沒關係,但是關於大小,從服務器到達的第一個大小的問題是正確的,我該如何同步呢? –

+0

確保您完全讀取大小字所指示的消息字節數。否則,你會誤讀下一個服務/字號。 – EJP

0

問題在於,使用TCP無法控制數據如何發送(分段)。您不知道使用channel.read(...)讀取了多少數據。

您在循環中調用receiveMessage(),在那裏將讀取的數據填充到緩衝區中。

你不能確保

int size = buffer.getInt(); 

是收到郵件的大小(僅在第一次調用,如果收到至少4個字節)。您必須記住前4個接收字節的大小(因爲您使用的是getInt()),那麼您必須登錄channel.read(...),直到您收到大小的字節,之後 - >處理下一個消息。

也重新使用您的緩衝區。既然你使用NIO(和非阻塞),我還建議你使用select(),而不是忙碌的閱讀。

+0

我把receiveMessage放在循環中運行,如果我不這樣做,我不能收到所有的消息,但是我從函數中退出了do/while,它仍然工作正常。現在的問題是忽略從服務器收到的第二個大小。 –