0

How to receive the data from server example "cutting" in parcels of 1024 bytes, cause when the answer comes from server in packets like 2 parts I don't know how solve that.

Ex. When the first packet arrives the size informed by the server is 1988 and received is 1444, that it's ok, but when the second packet arrives the size informed is something like 808333347 and received 540, the sum of 1444 + 540 = 1984 that is right. I don't know where this number 808333347 is coming from.

I'm googling for this solution and all then teachs using udp and I need this for a tcp/ip connection.

The class:

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;
}

I changed the function like this:

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);
        }
    }
}

But it's very ugly now.

Elikill58
  • 4,050
  • 24
  • 23
  • 45
Luciano Coelho
  • 89
  • 1
  • 11

2 Answers2

0

You can't control how data arrives on a TCP connection, from either end. It can arrive a byte at a time or in any other quanta up to the size of the data. You have to loop at the receiving end until you have everything you need. You need to use the same read buffer for the size of the socket so you can accumulate data for this loop, and to preserve any data that might belong to a subsequent message.

The 'size informed' cannot be as huge as you state. That only results from a bug. Probably you are getting out of sync.

user207421
  • 305,947
  • 44
  • 307
  • 483
  • I understood and solve the problem accumulating the data, now it's ok, but the issue about the size, the first size that arrives from server is right, how can I sync that? – Luciano Coelho Oct 06 '13 at 20:52
  • Make sure you read exactly the number of message bytes indicated by the size word. Otherwise you will misread the next service/size words. – user207421 Oct 06 '13 at 20:59
-1

The problem is that with TCP you have no control how the data is sent (fragmented). You do not know how much data is read with channel.read(...).

You call receiveMessage() in a loop, there you fill read data into a buffer.

You can not be sure that

int size    =  buffer.getInt();

is the size of the message you receive(only in the first call, if you receive at least 4 bytes.). You have to remember the first 4 received bytes, the size, (since you use getInt()), then you have to channel.read(...) until you have received size bytes, after that -> process the next message.

Also re-use your buffer. Since you use NIO (and non-blocking) i also suggest you to use select(), instead of busy reading.

Ortwin Angermeier
  • 5,957
  • 2
  • 34
  • 34
  • I put the receiveMessage on the run in a loop cause if I don't do that I can't receive all the message but I retired the do/while from the function and it's still workin good. The issue now is to ignore the second size that I receive from the server. – Luciano Coelho Oct 06 '13 at 22:03
  • You *do* know how much data is read with `channel.read(...)`. It's in the return value. – user207421 May 12 '23 at 00:32