Getting byte arrays using TCP connections
Asked Answered
G

2

4

I was using UDP to send/receive data but I now want to switch to TCP to avoid packet loss.

I've read several tutorials on TCP and noticed that instead of using DatagramPacket like UDP, TCP uses InputStream/OutputStream.

How do we get the byte[] from DataInputStream, something that's similar to this:

byte[] receiveData = new byte[64000];
DatagramPacket receivePacket = new DatagramPacket(receiveData,receiveData.length); 
receiveData=receivePacket.getData();
Gibbous answered 20/7, 2012 at 23:14 Comment(3)
Are you working in Java?Utter
Yes I am. Forgot to mention it. Editing the post now...Gibbous
are you "streaming" data or sending individual messages?Isochroous
J
7

The answer has 2 parts. Dealing with 2 separate problems your questions is related to.

1. Network facts

TCP is inherently stream based. i.e. Sending byte[1000] first and then byte[1200], is indistinguishable from sending byte[2200] once. What is actually send over the network can very likely be 2 packets, first being a packet with 1400 bytes and the second being 800, or 1401 and 799, and can vary each time. The receiver has no way to know the sender actually sent 1000 bytes first, and then sent 1200 bytes. This is by design in network. Java has nothing to do with this fact. And you can do nothing with it.

2. Java implementation

On the sender side. First, you need OutputStream os = tcpsocket.getOutputStream();. And then, each time, you need os.write(byteArray). On The receiver side, you need InputStream is = tcpsocket.getInputStream();. And then, each time, you need is.read(byteArray). Note that on the receiver side, how much of the byteArray is actually filled will be returned. It may be any number between 1 and the capacity of the byteArray, and is irrelevant to how the sender actually sent it.

To ease the task, you may use DataInputStream is = new DataInputStream(tcpsocket.getInputStream()); at the beginning, and use is.readFully(byteArray) each time you need to read something. This way, it can be guaranteed that byteArray will always be filled.

But you can never know how many bytes you should receive if the actual length is variable unless you add some extra information. For example, send the length first, using 4 bytes. How you will actually do this is usually closely related to your actual use case. And it's up to you

Joinder answered 21/7, 2012 at 0:2 Comment(7)
Thank you for your thorough answer. I really appreciate it! :)Gibbous
@DaoLam I feel glad you like it.Joinder
How do you initialize your byte array since you don't know the size of it yet? I initialized its size as 64000 but that's causing me an arrayindexoutofbounds problem somewhere else in my code.Gibbous
@DaoLam This is true. That's why I wrote the last paragraph. If you can elaborate on what you need to do, I can probably give you a suggestion on how you can make it work.Joinder
Should the number of bytes received ALWAYS be less than or equal to the actual length being sent? I tried doing the following to get the size of the received array: int x = is.read(byteArray); System.out.println(x); but x is actually larger than the size of the byteArray that was sent. Should this be happening or am I doing something wrong?Gibbous
@DaoLam I'm sorry for the late reply. x will not be larger than the actual length of the byte array sent however long the byteArray on the receiver side is. Can you post some code to give us a more complete view of your problem? Maybe posting a new question for your current problem is a faster way to solve your problem. You can add a link to the new question here if you wish.Joinder
Thank you! I actually solved the problem using ObjectOutputStream.Gibbous
I
13

in order to implement a message based protocol over a socket (stream), you basically want to come up with some message format, then read/write that on either end of the connection. a very simple format is to write the length of the message as a 4 byte int, then send the message bytes (then flush the stream). on the receiving end, read a 4 byte int, then read exactly that many following bytes (be careful that you limit your read method call or you may accidentally read part of the next message).

public void writeMessage(DataOutputStream dout, byte[] msg, int msgLen) {
  dout.writeInt(msgLen);
  dout.write(msg, 0, msgLen);
  dout.flush();
}

public byte[] readMessage(DataInputStream din) {
  int msgLen = din.readInt();
  byte[] msg = new byte[msgLen];
  din.readFully(msg);
  return msg;
}
Isochroous answered 21/7, 2012 at 0:2 Comment(0)
J
7

The answer has 2 parts. Dealing with 2 separate problems your questions is related to.

1. Network facts

TCP is inherently stream based. i.e. Sending byte[1000] first and then byte[1200], is indistinguishable from sending byte[2200] once. What is actually send over the network can very likely be 2 packets, first being a packet with 1400 bytes and the second being 800, or 1401 and 799, and can vary each time. The receiver has no way to know the sender actually sent 1000 bytes first, and then sent 1200 bytes. This is by design in network. Java has nothing to do with this fact. And you can do nothing with it.

2. Java implementation

On the sender side. First, you need OutputStream os = tcpsocket.getOutputStream();. And then, each time, you need os.write(byteArray). On The receiver side, you need InputStream is = tcpsocket.getInputStream();. And then, each time, you need is.read(byteArray). Note that on the receiver side, how much of the byteArray is actually filled will be returned. It may be any number between 1 and the capacity of the byteArray, and is irrelevant to how the sender actually sent it.

To ease the task, you may use DataInputStream is = new DataInputStream(tcpsocket.getInputStream()); at the beginning, and use is.readFully(byteArray) each time you need to read something. This way, it can be guaranteed that byteArray will always be filled.

But you can never know how many bytes you should receive if the actual length is variable unless you add some extra information. For example, send the length first, using 4 bytes. How you will actually do this is usually closely related to your actual use case. And it's up to you

Joinder answered 21/7, 2012 at 0:2 Comment(7)
Thank you for your thorough answer. I really appreciate it! :)Gibbous
@DaoLam I feel glad you like it.Joinder
How do you initialize your byte array since you don't know the size of it yet? I initialized its size as 64000 but that's causing me an arrayindexoutofbounds problem somewhere else in my code.Gibbous
@DaoLam This is true. That's why I wrote the last paragraph. If you can elaborate on what you need to do, I can probably give you a suggestion on how you can make it work.Joinder
Should the number of bytes received ALWAYS be less than or equal to the actual length being sent? I tried doing the following to get the size of the received array: int x = is.read(byteArray); System.out.println(x); but x is actually larger than the size of the byteArray that was sent. Should this be happening or am I doing something wrong?Gibbous
@DaoLam I'm sorry for the late reply. x will not be larger than the actual length of the byte array sent however long the byteArray on the receiver side is. Can you post some code to give us a more complete view of your problem? Maybe posting a new question for your current problem is a faster way to solve your problem. You can add a link to the new question here if you wish.Joinder
Thank you! I actually solved the problem using ObjectOutputStream.Gibbous

© 2022 - 2024 — McMap. All rights reserved.