slena17
slena17

Reputation: 33

android java socket write and receive byte[] data

I need to sent a packet to a server using socket from my android application. I only know a packet layout:

Packet ID 4 bytes | Packet length 4 bytes(minus len + ID) | payload (protobuf message)

The whole stuff about TLSv1.2 connection and self-signed certificate works well. For example, I need to send authentication packet - LoginRequest and server will response with LoginResponse if the packet was sent successfully. What I am trying do to is connect to a server inside AsyncTask class, write data and receive response, but obviously I am doing it wrong because I got no response. The code for writing and reading a message:

LoginRequest protobuf message:

Protos.LoginRequest loginRequest = Protos.LoginRequest.newBuilder()
                    .setUsername(mailAddress)
                    .setPassword(pass).build();

And the code(inside doInBackground() method):

//TLSSocketFactory is custom SSLSocketFactory class for forcing TLSv1.2 on devices > 16 & < 20
socket = tlsSocketFactory.createSocket("airwave1.exurion.com", 2559);

byte[] payload = loginRequest.toByteArray();

DataOutputStream out = new DataOutputStream(socket.getOutputStream());
InputStream inStream = socket.getInputStream();

out.writeInt(10); //ID of the packet
out.writeInt(payload.length);
out.write(payload);

out.flush();

byte[] data = new byte[100];
int count = inStream.read(data);

out.close();
inStream.close();
socket.close();

As I said I got no response, Sometimes I also get an SSLException while reading the message:

javax.net.ssl.SSLException: Read error: ssl=0xb3a28580: I/O error during system call, Connection timed out

Has anyone an idea how to solve this?

//UPDATED I figured out that the byte order needs to be in LITTLE_ENDIAN, so I tried to use ByteBuffer:

//based on previous packet layout (4 bytes for ID, 4 bytes for payload length, and payload) - is it ByteBuffer.allocate() fine?
    ByteBuffer buffer = ByteBuffer.allocate(8 + payload.length);
    buffer.order(ByteOrder.LITTLE_ENDIAN);

    buffer.putInt(LoginPacketType.LOGIN_REQUEST.getId());
    buffer.putInt(payload.length);
    buffer.put(payload);

    buffer.rewind();
    byte[] result = new byte[buffer.capacity()]; // Could also use result = buffer.array();
    buffer.get(result);

    out.write(result);

But now I am getting OOM exception:

Failed to allocate a 184549388 byte allocation with 16777216 free bytes and 155MB until OOM

Details about that: After writing to an DataOutputStream, I make:

buffer.clear()
out.flush();

//code for reading from InputStream

And now, in my log appears this message several times: Starting a blocking GC Alloc

and than throws OOM exception.

Upvotes: 0

Views: 2314

Answers (2)

slena17
slena17

Reputation: 33

The problem was with LITTLE_ENDIAN and BIG_ENDIAN order. Servers sends response in LITTLE_ENDIAN order so I rewrite your answer a bit:

int type = inStream.readInt();
type = Integer.reverseBytes(type);
int length = inStream.readInt();
length = Integer.reverseBytes(length);

if (length > 0) {
    byte[] data = new byte[length];
    inStream.readFully(data);
    Protos.LoginResponse response = Protos.LoginResponse.parseFrom(data);
}

Thanks for the hint.

Upvotes: 1

user207421
user207421

Reputation: 311039

You're writing a packet type and length and payload, but you're only reading a payload. You're also assuming that read() fills the buffer.

int type = din.readInt();
int length = din.readInt();
byte[] data = new byte[length];
din.readyFully(data);

Upvotes: 0

Related Questions