How to read bytes on receiver in socket stream (java) - java

I have set up a socket connection between a server and i client. Right now i'm trying to send data from my client to the server. Actually the data is a byte array which contain numbers in the index 14 to 27. An example of the array is here:
{27, 14, 16, 18, 20, 22, 14, 17, 15, 17} and so on.
Have made it as an byte array because the data have to be in bytes.
The difficulty is that when i sent a line from the array to the server, i don't know how to read it other than a string. And if it is a string, it return some weird numbers like the one you see in the picture.
Some code how i do it:
Sender
for (int i = 0; i < data.length; i++) {
writer.write(data[i]);
}
writer.flush();
writer.close();
Receiver
public void readResponse(Socket client) throws IOException{
String userInput;
BufferedReader stdIn = new BufferedReader(new InputStreamReader(client.getInputStream()));
System.out.println("Response from client:");
while ((userInput = stdIn.readLine()) != null) {
System.out.println(userInput);
}
}
My byte array is made like this:
private byte data[] = new byte[12];
if i change it to Byte with uppercase, i can read it with my code, but i'm not sure if it in bytes then? Have to use some math to calculate an average.
private Byte data[] = new Byte[12];
So, how do i read it?
Update:
So i understand that i'm going to use a different input/output stream. Right now i have changed it too a Datainput and output stream.
Code looks like this:
Server
public void readResponse(Socket client) throws IOException{
DataInputStream input = null;
byte data;
try {
input = new DataInputStream(client.getInputStream());
}
catch (IOException e) {
System.out.println(e);
}
System.out.println("Response from client:");
while ((data = input.readByte()) != -1) {
System.out.println(data);
}
}
Client
public void sentData(Socket client) throws IOException{
DataOutputStream output = null;
try {
output = new DataOutputStream(client.getOutputStream());
}
catch (IOException e) {
System.out.println(e);
}
for (int i = 0; i < data.length; i++) {
output.write(data[i]);
}
output.flush();
output.close();
}
As you can see in my client, i want to sent a byte at a time to the server, but it still shows weird numbers like [?][?][?].

All the *Reader classes are meant for text data only. When working with binary data, just use the streams directly. In your case, just use BufferedInputStream instead of BufferedInputStreamReader.

There is also TCP protocol your program has to conform to, with it own buffering and flushing mechanism. This existence of this layer is non-obvious when you first code your Java program with raw byte stream.
I would suggest you either construct a deterministic protocol, e.g. using a marker byte like "000" to indicate the start of your transmission, and encoded payload that excludes the "000", and finally, "000" to terminate your transmission. (This still doesn't deal with loss in transmission well).
Alternatively, Google's Protocol Buffer, or Msgpack to help along with some of the intermediary process.

Related

Accessing byte array values from socket inputStream

I am attempting to retrieve the byte values from an InputStream which is being sent to the socket. I have used many ways but it always prints me the address of the byte array instead of its contents.
Below is my code for Client and Server. When a packet is sent from the client to the server, the server instantiates a new Thread to handle the connection. So slaveSocket is the socket I want to use for this.
public class TCPClient {
public static void main(String[] args) throws IOException{
Socket socket;
String address;
int port;
String userInput;
String serverResponse;
PrintWriter out;
BufferedReader in;
//read characters from user
BufferedReader stdIn;
if (args.length != 2) {
System.err.println("Usage: java EchoClient <address> <port>");
System.exit(1);
}
byte[] mode = "octet".getBytes(Charset.forName("UTF-8"));
address = args[0];
port = Integer.parseInt(args[1]);
try{
//connect socket to server
socket = new Socket(address, port);
//Construct PrintWriter to write objects to the socket
out = new PrintWriter(socket.getOutputStream(), true);
//Construct BufferedReader to read input from the socket
in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
//Another reader to read characters typed by the user
stdIn = new BufferedReader(new InputStreamReader(System.in));
//scanner for menu option
Scanner scanner = new Scanner(System.in);
int menuOption;
System.out.println("Press 1 to read from file or 2 to write to file");
menuOption = scanner.nextInt();
if (menuOption == 1){
String filename = "";
String text = "";
System.out.println("Enter file name");
filename = scanner.next();
byte[] packet = new byte[512];
//Constructing the RRQ Packet
//Ading the OPCODE
packet[0] = 1;
//adding the filename
filename.getBytes(Charset.forName("UTF-8"));
byte[] filenameB = filename.getBytes(Charset.forName("UTF-8"));
System.arraycopy(filenameB,0,packet,1, filenameB.length);
//adding a 0
packet[filenameB.length +1] = 0;
//adding the mode
System.arraycopy(mode,0,packet,1+filenameB.length+1,mode.length);
//adding the last 0
packet[1+filenameB.length+1+mode.length+1] = 0;
out.println(packet);
}else if(menuOption == 2){
}
socket.close();
}catch(UnknownHostException e){
System.err.println("Dont know about host" + address);
System.exit(1);
}catch(IOException e){
System.err.println("Couldnt get I/O for the connection to " + address);
System.exit(1);
}
}
}
public class TCPServer {
public static void main(String[] args) throws IOException{
//port of the server
int port = 10000;
//Socket objects
ServerSocket masterSocket;
Socket slaveSocket;
//instantiate the server socket
masterSocket = new ServerSocket(port);
System.out.println("Server Started");
boolean flag1 = true;
while(true){
slaveSocket = masterSocket.accept();
System.out.println("Accepted TCP connection from: " +
slaveSocket.getInetAddress() + ", " + slaveSocket.getPort() + "...");
System.out.println("Initialising new Thread...");
new TCPServerThread(slaveSocket).start();
}
}
}
public class TCPServerThread extends Thread{
private Socket slaveSocket = null;
public TCPServerThread(Socket socket){
super("TCPServerThread");
this.slaveSocket = socket;
}
public void run(){
byte[] ClientPacket = new byte[512];
PrintWriter socketOutput;
InputStream socketInput;
try{
//send packet to client
socketOutput = new PrintWriter((slaveSocket.getOutputStream()), true);
//read packet from client
socketInput = new DataInputStream(slaveSocket.getInputStream());
ClientPacket = socketInput.readAllBytes();
System.out.println(new String(ClientPacket, StandardCharsets.UTF_8));
}catch (IOException e){
System.err.println(e);
}
}
}
You've hopelessly overengineered this.
Writer and Reader do character input and output. InputStream and OutputStream do byte input and output.
You turn byte-based stuff (and in the end, network ports are byte based, not character based) into character based stuff in dangerous ways and then are attempting to read and write bytes into and out of the char-based things.
The solution is simple. Just stop doing that. You have byte-based stuff, there is absolutely no need to involve Reader and Writer.
A bunch of lines that cause problems:
out.println(packet);
PrintStreams are debug aids. You can't use them for any of this. For example, this line will print newlines (definitely not something you'd want in a byte based stream system!), and will print 'objects' - it does that by invoking the .toString() method, and the toString method of arrays are mostly useless. That explains why you see what you see. This is not how you send bytes. You cannot send bytes to a PrintStream (which is a confused mess, as it tries to let you send characters to a byte based system. As I said, you use it for debugging and nothing else. You should not be using it here at all).
new InputStreamReader(socket.getInputStream())
This is dangerous. You're turning a byte based system (InputStream) into a char-based one (Reader) and this always means somebody is making an explicit, 'out of band' (not based on the data in that stream) decision about charset encoding. In this case, as per the docs of InputStreamReader, you get the 'platform default'. Starting with JDK18, it's guaranteed to be UTF-8 fortunately, but before that, who knows what it is. You never want to call this constructor to avoid the confusion. new InputStreamReader(socket.getInputStream, StandardCharsets.UTF_8).
Mostly, though, don't make a reader in the first place. You have no interest whatsoever in reading streams of characters, you just want bytes.
If you have smallish strings and the information about where they 'end' is done 'out of band' (example: The size in bytes (not characters) is sent first, then X bytes that are the string, UTF_8 encoded), you can just read that in as bytes, and then make a string off of that, bypassing any need for Readers and Writers. Reader and Writer is useful only if the entire stream is all character based, or if you have huge strings (hundreds of megabytes) where their end can only be surmised by interpreting the data as characters first. (Mostly, those are horrible protocols that shouldn't be used).
//Construct PrintWriter to write objects to the socket
No, you can't write objects to sockets. Objects aren't bytes. You can write bytes to a socket; some objects will let themselves be turned into bytestreams but this is decidedly not a trivial job, and PrintWriter can't do it at all.
catch (IOException e) { System.err.println(e);
Most code has no reasonable route to 'deal' with them, but the solution to that is to throw them onwards. Not to catch the exception, print a note of despair, and just keep going on like nothing happened. Doing it right is also less code, so, win-win.
stdIn = new BufferedReader(new InputStreamReader(System.in));
//scanner for menu option
Scanner scanner = new Scanner(System.in);
You're making 2 different ways to read standard input. That makes no sense. Pick one.
I tried to fix it for you:
public class TCPClient {
public static void main(String[] args) throws Exception { // always throw Exception from `main`.
if (args.length != 2) {
System.err.println("Usage: java EchoClient <address> <port>");
System.exit(1);
return; // Always return after System.exit.
}
byte[] mode = "octet".getBytes(Charset.forName("UTF-8"));
String address = args[0];
int port = Integer.parseInt(args[1]);
Scanner scanner = new Scanner(System.in);
scanner.useDelimiter("\\R"); // split on newlines, not spaces. So much more logical.
// resources need to be safe-closed - use try-with!
try (var socket = new Socket(address, port);
var out = new BufferedOutputStream(socket.getOutputStream());
var in = socket.getInputStream()) {
System.out.println("Press 1 to read from file or 2 to write to file");
int menuOption = scanner.nextInt();
if (menuOption == 1) {
System.out.println("Enter file name");
String filename = scanner.next();
//Constructing the RRQ Packet
//Adding the OPCODE
out.write(1);
out.write(filename.getBytes(StandardCharsets.UTF_8));
out.write(0);
// The above is dangerous; NUL (0) is actually a valid char.
// A proper way to send strings is to send length in bytes
// first. I'll leave it to you to fix your protocol.
// If it can't be fixed, scan for `\0` chars and get rid of em.
//adding the mode
out.write(mode);
out.write(0);
}else if (menuOption == 2) {
}
}
}
Sending bytes one at a time can be slow (as it ends up sending an entire packet) but can also be useful - the data is just sent, instead of waiting perhaps for a long time for more data. In your case, you send it all in one go, so sending it all off very quickly is not a good idea. Hence, why the outputstream is wrapped in a BufferedOutputStream, which fixes that. You can always use flush() to force sending now, in case you want to keep the connection open (close(), naturally, also flushes).
It's fine if you want to use a byte[] packet instead, but it seems convoluted and unneccessary here. out.write(someByteArray), where out is an OutputStream of some sort, works fine. out.println(byteArray), where out is a Writer of some sort, or a PrintStream - doesn't work at all. (It would take the array, call toString() on it which isn't useful, then convert those bytes using some unknown charset and send that, and none of that is what you want).
You'll need to similarly eliminate PrintStream and the like from your server code.

Getting Garbage value while reading from serial port in java

I am reading data from serialport using jSerialComm package. I am getting following data
A total of 22 bytes are being received and I am getting these three garbage bytes too. The readable data is correct but what these garbage characters are happening?
Following is my code.
public static void main(String[] args) {
SerialPort serialPort = SerialPort.getCommPort("/dev/ttyUSB0");
if(serialPort.openPort())
{
System.out.println("Port Opened Successfully...");
}
else
{
System.out.println("Unable to open port....");
return;
}
serialPort.setComPortParameters(1200, 8, 1, 0);
try
{
while(true)
{
while(serialPort.bytesAvailable() != 0)
{
Thread.sleep(1000);
byte[] readBuffer = new byte[serialPort.bytesAvailable()];
int numRead = serialPort.readBytes(readBuffer, readBuffer.length);
String data = new String(readBuffer);
System.out.println("Read "+numRead+ " bytes." + readBuffer);
System.out.println(data);
}
}
}
catch(Exception e)
{
e.printStackTrace();
}
serialPort.closePort();
System.out.println("done...");
}
SerialPort.readBytes seems to be not thread safe, thus yielding "garbage" when called during character reception.
I suggest to use the snippet from the author's example which worked fine for me:
// Get a new instance of SerialPort by opening a port.
SerialPort port = SerialPort.open("COM2");
// Configure the connection
port.setTimeout(100);
port.setConfig(BaudRate.B115200, Parity.NONE, StopBits.ONE, DataBits.B8);
// You have the choice, you can either use the Java NIO channels
// or classic Input/Ouput streams to read and write data.
//DEL SerialChannel channel = port.getChannel();
InputStream istream = port.getInputStream();
// Read some data using a stream
byte[] byteBuffer = new byte[4096];
// Will timeout after 100ms, returning 0 if no bytes were available.
int n = istream.read(byteBuffer);
// *** Use n bytes of byteBuffer ***
//DEL ...
port.close();
//DEL : removed from the original code for clarity
You did not explain the protocol but I suggest to look it up. Presumably these are control characters or like a comment suggests binary data. You create a String from a byte buffer without an encoding so this also depends on your environment/ JVM's default encoding.
Try treating the first and the last two bytes as specified in the protocol in use for your project. It might also be related to jSerialComm not removing serial signalling, e.g. handshake, EOT and such.
If you're reverse-engineering the protocol instead maybe also try another library like RxTx to see if the bytes stay the same.
To inspect the bytes safely use for example BigInteger to print out a Hex-String instead:
BigInteger bigInteger = new BigInteger(1, bytes);
System.out.printf("%0" + (bytes.length << 1) + "x", bigInteger);
Use this code block and it will run perfectly;
serialPort.addDataListener(new SerialPortDataListener() {
#Override
public int getListeningEvents() {
return SerialPort.LISTENING_EVENT_DATA_RECEIVED;
}
#Override
public void serialEvent(SerialPortEvent serialPortEvent) {
if (serialPortEvent.getEventType() != SerialPort.LISTENING_EVENT_DATA_RECEIVED) {
return;
}
byte[] newData = serialPortEvent.getReceivedData();
String data = new String(newData);
System.out.println(data);
}
});

Java: receive different type of file from InputStream in socket programming

In my project client send some text, some images to the server. Server need to handle all these things i.e if text is there then it need to display on TestArea which is on frame and if image file is there then server need to store that image file on the computer.
I created one application which handle either string(text) or image file but I don't know how to store all these things when these are send by the client at the same time.
I know when I add this code:
InputStream in = socket.getInputStream();
then all the data send by the client is in this InputStream
So how I identify up to this much range data is image file so need to store in Byte Array, up to this much is test or string so need to display in TextAreea.
If client send two or more images at a time to the server then how server understand up to this much data this is first image, up to this much data this is second image file.
I tried this code to send Images:
Client Code:
public void sendPhotoToServer(String str){ // str is image location
try {
InputStream input = new FileInputStream(str);
byte[] buffer=new byte[1024];
int readData;
while((readData=input.read(buffer))!=-1){
dos.write(buffer,0,readData); // dos is DataOutputStream
}
} catch (FileNotFoundException e) {
} catch (IOException e) {
}
}
and this method is in the loop so client send all the images in his folder.
Now Server Code, this is in thread and while loop is there which listen client data every time:
public void run() {
while (true) {
try {
byte[] buffer = new byte[8192];
fis = new FileOutputStream("C:\\"+(s1++)+".jpg"); // fis is FileOutputStream
while ((count = in.read(buffer)) > 0){ //count is a integer and 'in' is InputStream
fis.write(buffer, 0, count);
fis.flush();
}
} catch (Exception e) {}
}
}
but by this code first image is received by the server after that it not show other images.
and here how server understand if simple string is send by the client.
In server side application it open one port to listen all the data send by the client by
FileOutputStream fis = socket.getInputStream();
now how it distinguish all these files and simple string.
Why don't you just use ObjectInputStream and ObjectOutputStream, if you know the datatypes in advance.
Here's sending:
import java.io.*;
public class Client {
public static void main(String[] args) throws IOException {
// write string
ObjectOutputStream out = new ObjectOutputStream(
new BufferedOutputStream(
new FileOutputStream("test-string.data")));
try {
out.writeObject("Hello");
} finally {
out.close();
}
// write byte arrays
out = new ObjectOutputStream(
new BufferedOutputStream(
new FileOutputStream("test-byteArrays.data")));
try {
out.writeObject(new byte[] { 'H', 'e', 'l', 'l', 'o' });
out.writeObject(new byte[] { 'W', 'o', 'r', 'l', 'd' });
} finally {
out.close();
}
}
}
Here's receiving:
import java.io.*;
import java.util.*;
public class Server {
public static void main(String[] args) throws IOException, ClassNotFoundException {
// write string
ObjectInputStream in = new ObjectInputStream(
new BufferedInputStream(
new FileInputStream("test-string.data")));
try {
Object o = null;
while ((o = in.readObject()) != null) {
System.out.printf("Class: %s, toString: %s\n", o.getClass(), o.toString());
}
} catch (EOFException e) {
// finished
} finally {
in.close();
}
// write byte arrays
in = new ObjectInputStream(
new BufferedInputStream(
new FileInputStream("test-byteArrays.data")));
try {
Object o = null;
while ((o = in.readObject()) != null) {
System.out.printf("Class: %s, toString: %s\n", o.getClass(), o.toString());
}
} catch (EOFException e) {
// finished
} finally {
in.close();
}
}
}
There's no automatic handling of data boundaries. The server code has no way of knowing where one file/message ends and the other begins. You'll have to come up with your own protocol.
What you're doing is sending raw data over a socket. That's all you have: a stream of bytes. In order to make sure the receiving end can understand what part belongs with what file, and the type of this file, you'll need to come up with a way to encode this into the stream of bytes in such a way that the actual data isn't disturbed and both sender/receiver "understand" this structure. This is the sort of thing known as a protocol.
Coming up with your own protocol can be useful in some situations, but know that it can be very complex based on what you want to achieve. Most likely a protocol already exists that is well suited to your needs. You could use HTTP with the MIME type indicated in the header fields. This would let you figure out the file type and, in the case of text, the encoding. FTP is suitable for file transfers, and the extension of the file name could be used to detect its type. SOAP allows you to send complex data structures in a way that is independent on the client's or server's implementation languages.
TL;DR version: don't reinvent the wheel, search around for what exists out there to suit your needs. Most likely, the suitable protocol has support in Java out-of-the-box or via a third-party library. Apache's Commons Net could help you here.

Java -- How to read an unknown number of bytes from an inputStream (socket/socketServer)?

Looking to read in some bytes over a socket using an inputStream. The bytes sent by the server may be of variable quantity, and the client doesn't know in advance the length of the byte array. How may this be accomplished?
byte b[];
sock.getInputStream().read(b);
This causes a 'might not be initialized error' from the Net BzEAnSZ. Help.
You need to expand the buffer as needed, by reading in chunks of bytes, 1024 at a time as in this example code I wrote some time ago
byte[] resultBuff = new byte[0];
byte[] buff = new byte[1024];
int k = -1;
while((k = sock.getInputStream().read(buff, 0, buff.length)) > -1) {
byte[] tbuff = new byte[resultBuff.length + k]; // temp buffer size = bytes already read + bytes last read
System.arraycopy(resultBuff, 0, tbuff, 0, resultBuff.length); // copy previous bytes
System.arraycopy(buff, 0, tbuff, resultBuff.length, k); // copy current lot
resultBuff = tbuff; // call the temp buffer as your result buff
}
System.out.println(resultBuff.length + " bytes read.");
return resultBuff;
Assuming the sender closes the stream at the end of the data:
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] buf = new byte[4096];
while(true) {
int n = is.read(buf);
if( n < 0 ) break;
baos.write(buf,0,n);
}
byte data[] = baos.toByteArray();
Read an int, which is the size of the next segment of data being received. Create a buffer with that size, or use a roomy pre-existing buffer. Read into the buffer, making sure it is limited to the aforeread size. Rinse and repeat :)
If you really don't know the size in advance as you said, read into an expanding ByteArrayOutputStream as the other answers have mentioned. However, the size method really is the most reliable.
Without re-inventing the wheel, using Apache Commons:
IOUtils.toByteArray(inputStream);
For example, complete code with error handling:
public static byte[] readInputStreamToByteArray(InputStream inputStream) {
if (inputStream == null) {
// normally, the caller should check for null after getting the InputStream object from a resource
throw new FileProcessingException("Cannot read from InputStream that is NULL. The resource requested by the caller may not exist or was not looked up correctly.");
}
try {
return IOUtils.toByteArray(inputStream);
} catch (IOException e) {
throw new FileProcessingException("Error reading input stream.", e);
} finally {
closeStream(inputStream);
}
}
private static void closeStream(Closeable closeable) {
try {
if (closeable != null) {
closeable.close();
}
} catch (Exception e) {
throw new FileProcessingException("IO Error closing a stream.", e);
}
}
Where FileProcessingException is your app-specific meaningful RT exception that will travel uninterrupted to your proper handler w/o polluting the code in between.
The simple answer is:
byte b[] = new byte[BIG_ENOUGH];
int nosRead = sock.getInputStream().read(b);
where BIG_ENOUGH is big enough.
But in general there is a big problem with this. A single read call is not guaranteed to return all that the other end has written.
If the nosRead value is BIG_ENOUGH, your application has no way of knowing for sure if there are more bytes to come; the other end may have sent exactly BIG_ENOUGH bytes ... or more than BIG_ENOUGH bytes. In the former case, you application will block (for ever) if you try to read. In the latter case, your application has to do (at least) another read to get the rest of the data.
If the nosRead value is less than BIG_ENOUGH, your application still doesn't know. It might have received everything there is, part of the data may have been delayed (due to network packet fragmentation, network packet loss, network partition, etc), or the other end might have blocked or crashed part way through sending the data.
The best answer is that EITHER your application needs to know beforehand how many bytes to expect, OR the application protocol needs to somehow tell the application how many bytes to expect or when all bytes have been sent.
Possible approaches are:
the application protocol uses fixed message sizes (not applicable to your example)
the application protocol message sizes are specified in message headers
the application protocol uses end-of-message markers
the application protocol is not message based, and the other end closes the connection to say that is the end.
Without one of these strategies, your application is left to guess, and is liable to get it wrong occasionally.
Then you use multiple read calls and (maybe) multiple buffers.
Stream all Input data into Output stream. Here is working example:
InputStream inputStream = null;
byte[] tempStorage = new byte[1024];//try to read 1Kb at time
int bLength;
try{
ByteArrayOutputStream outputByteArrayStream = new ByteArrayOutputStream();
if (fileName.startsWith("http"))
inputStream = new URL(fileName).openStream();
else
inputStream = new FileInputStream(fileName);
while ((bLength = inputStream.read(tempStorage)) != -1) {
outputByteArrayStream.write(tempStorage, 0, bLength);
}
outputByteArrayStream.flush();
//Here is the byte array at the end
byte[] finalByteArray = outputByteArrayStream.toByteArray();
outputByteArrayStream.close();
inputStream.close();
}catch(Exception e){
e.printStackTrace();
if (inputStream != null) inputStream.close();
}
Either:
Have the sender close the socket after transferring the bytes. Then at the receiver just keep reading until EOS.
Have the sender prefix a length word as per Chris's suggestion, then read that many bytes.
Use a self-describing protocol such as XML, Serialization, ...
Use BufferedInputStream, and use the available() method which returns the size of bytes available for reading, and then construct a byte[] with that size. Problem solved. :)
BufferedInputStream buf = new BufferedInputStream(is);
int size = buf.available();
Here is a simpler example using ByteArrayOutputStream...
socketInputStream = socket.getInputStream();
int expectedDataLength = 128; //todo - set accordingly/experiment. Does not have to be precise value.
ByteArrayOutputStream baos = new ByteArrayOutputStream(expectedDataLength);
byte[] chunk = new byte[expectedDataLength];
int numBytesJustRead;
while((numBytesJustRead = socketInputStream.read(chunk)) != -1) {
baos.write(chunk, 0, numBytesJustRead);
}
return baos.toString("UTF-8");
However, if the server does not return a -1, you will need to detect the end of the data some other way - e.g., maybe the returned content always ends with a certain marker (e.g., ""), or you could possibly solve using socket.setSoTimeout(). (Mentioning this as it is seems to be a common problem.)
This is both a late answer and self-advertising, but anyone checking out this question may want to take a look here:
https://github.com/GregoryConrad/SmartSocket
This question is 7 years old, but i had a similiar problem, while making a NIO and OIO compatible system (Client and Server might be whatever they want, OIO or NIO).
This was quit the challenge, because of the blocking InputStreams.
I found a way, which makes it possible and i want to post it, to help people with similiar problems.
Reading a byte array of dynamic sice is done here with the DataInputStream, which kann be simply wrapped around the socketInputStream. Also, i do not want to introduce a specific communication protocoll (like first sending the size of bytes, that will be send), because i want to make this as vanilla as possible. First of, i have a simple utility Buffer class, which looks like this:
import java.util.ArrayList;
import java.util.List;
public class Buffer {
private byte[] core;
private int capacity;
public Buffer(int size){
this.capacity = size;
clear();
}
public List<Byte> list() {
final List<Byte> result = new ArrayList<>();
for(byte b : core) {
result.add(b);
}
return result;
}
public void reallocate(int capacity) {
this.capacity = capacity;
}
public void teardown() {
this.core = null;
}
public void clear() {
core = new byte[capacity];
}
public byte[] array() {
return core;
}
}
This class only exists, because of the dumb way, byte <=> Byte autoboxing in Java works with this List. This is not realy needed at all in this example, but i did not want to leave something out of this explanation.
Next up, the 2 simple, core methods. In those, a StringBuilder is used as a "callback". It will be filled with the result which has been read and the amount of bytes read will be returned. This might be done different of course.
private int readNext(StringBuilder stringBuilder, Buffer buffer) throws IOException {
// Attempt to read up to the buffers size
int read = in.read(buffer.array());
// If EOF is reached (-1 read)
// we disconnect, because the
// other end disconnected.
if(read == -1) {
disconnect();
return -1;
}
// Add the read byte[] as
// a String to the stringBuilder.
stringBuilder.append(new String(buffer.array()).trim());
buffer.clear();
return read;
}
private Optional<String> readBlocking() throws IOException {
final Buffer buffer = new Buffer(256);
final StringBuilder stringBuilder = new StringBuilder();
// This call blocks. Therefor
// if we continue past this point
// we WILL have some sort of
// result. This might be -1, which
// means, EOF (disconnect.)
if(readNext(stringBuilder, buffer) == -1) {
return Optional.empty();
}
while(in.available() > 0) {
buffer.reallocate(in.available());
if(readNext(stringBuilder, buffer) == -1) {
return Optional.empty();
}
}
buffer.teardown();
return Optional.of(stringBuilder.toString());
}
The first method readNext will fill the buffer, with byte[] from the DataInputStream and return the amount bytes read this way.
In the secon method, readBlocking, i utilized the blocking nature, not to worry about consumer-producer-problems. Simply readBlocking will block, untill a new byte-array is received. Before we call this blocking method, we allocate a Buffer-size. Note, i called reallocate after the first read (inside the while loop). This is not needed. You can safely delete this line and the code will still work. I did it, because of the uniqueness of my problem.
The 2 things, i did not explain in more detail are:
1. in (the DataInputStream and the only short varaible here, sorry for that)
2. disconnect (your disconnect routine)
All in all, you can now use it, this way:
// The in has to be an attribute, or an parameter to the readBlocking method
DataInputStream in = new DataInputStream(socket.getInputStream());
final Optional<String> rawDataOptional = readBlocking();
rawDataOptional.ifPresent(string -> threadPool.execute(() -> handle(string)));
This will provide you with a way of reading byte arrays of any shape or form over a socket (or any InputStream realy). Hope this helps!

how to send an array of bytes over a TCP connection (java programming)

Can somebody demonstrate how to send an array of bytes over a TCP connection from a sender program to a receiver program in Java.
byte[] myByteArray
(I'm new to Java programming, and can't seem to find an example of how to do this that shows both ends of the connection (sender and receiver.) If you know of an existing example, maybe you could post the link. (No need to reinvent the wheel.) P.S. This is NOT homework! :-)
The InputStream and OutputStream classes in Java natively handle byte arrays. The one thing you may want to add is the length at the beginning of the message so that the receiver knows how many bytes to expect. I typically like to offer a method that allows controlling which bytes in the byte array to send, much like the standard API.
Something like this:
private Socket socket;
public void sendBytes(byte[] myByteArray) throws IOException {
sendBytes(myByteArray, 0, myByteArray.length);
}
public void sendBytes(byte[] myByteArray, int start, int len) throws IOException {
if (len < 0)
throw new IllegalArgumentException("Negative length not allowed");
if (start < 0 || start >= myByteArray.length)
throw new IndexOutOfBoundsException("Out of bounds: " + start);
// Other checks if needed.
// May be better to save the streams in the support class;
// just like the socket variable.
OutputStream out = socket.getOutputStream();
DataOutputStream dos = new DataOutputStream(out);
dos.writeInt(len);
if (len > 0) {
dos.write(myByteArray, start, len);
}
}
EDIT: To add the receiving side:
public byte[] readBytes() throws IOException {
// Again, probably better to store these objects references in the support class
InputStream in = socket.getInputStream();
DataInputStream dis = new DataInputStream(in);
int len = dis.readInt();
byte[] data = new byte[len];
if (len > 0) {
dis.readFully(data);
}
return data;
}
Just start with this example from the Really Big Index. Notice though, that it's designed to transmit and receive characters, not bytes. This isn't a big deal, though - you can just deal with the raw InputStream and OutputStream objects that the Socket class provides. See the API for more info about the different types of readers, writers and streams. Methods you'll be interested in are OutputStream.write(byte[]) and InputStream.read(byte[]).
The Oracle Socket Communications Tutorial would seem to be the appropriate launch point.
Note that it's going to extra trouble to turn characters into bytes. If you want to work at the byte level, just peel that off.
This Sun Sockets tutorial should give you a good starting point
What you need to use is the write method of an java.io.OutputStream, and the read method of an java.io.InputStream, both of which you can retrieve from the Socket you open.
I would ask you to use ObjectOutputStream and ObjectInputStream. These send everything as an object and receive as the same.
ObjectOutputStream os = new ObjectOutputStream(socket.getOutputStream());
os.flush();
ObjectInputStream is = new ObjectInputStream(socket.getInputStream());
os.writeObject(byte_array_that_you_want_to_send);
byte[] temp = (byte[]) is.readObject();
Also remember first create the output stream, flush it and then go ahead with the input stream because if something left out in the stream the input stream wont be created.
I'm guessing that the question is worded incorrectly. I found this when searching for an answer to why my use of InputStream and OutputStream seemed to be setting the entire array to 0 upon encountering a byte of value 0. Do these assume that the bytes contain valid ASCII and not binary. Since the question doesn't come right out and ask this, and nobody else seems to have caught it as a possibility, I guess I'll have to satisfy my quest elsewhere.
What I was trying to do was write a TransparentSocket class that can instantiate either a TCP (Socket/ServerSocket) or a UDP (DatagramSocket) to use the DatagramPacket transparently. It works for UDP, but not (yet) for TCP.
Follow-up: I seem to have verified that these streams are themselves useless for binary transfers, but that they can be passed to a more programmer-friendly instantiation, e.g.,
new DataOutputStream(socket.getOutputStream()).writeInt(5);
^ So much for that idea. It writes data in a "portable" way, i.e., probably ASCII, which is no help at all, especially when emulating software over which I have no control!
import java.io.*;
import java.net.*;
public class ByteSocketClient
{
public static void main(String[] args) throws UnknownHostException, IOException
{
Socket s=new Socket("",6000);
DataOutputStream dout=new DataOutputStream(new BufferedOutputStream(s.getOutputStream()));
byte[] a = {(byte)0xC0,(byte)0xA8,(byte)0x01,(byte)0x02,(byte)0x53,(byte)0x4D,(byte)0x41,(byte)0x52,(byte)0x54};
dout.write(a);
dout.close();
s.close();
}
}
Here is an example that streams 100 byte wav file frames at a time.
private Socket socket;
public void streamWav(byte[] myByteArray, int start, int len) throws IOException {
Path path = Paths.get("path/to/file.wav");
byte[] data = Files.readAllBytes(path);
OutputStream out = socket.getOutputStream();
DataOutputStream os = new DataOutputStream(out);
os.writeInt(len);
if (len > 0) {
os.write(data, start, len);
}
}
public void readWav() throws IOException {
InputStream in = socket.getInputStream();
int frameLength = 100; // use useful number of bytes
int input;
boolean active = true;
while(active) {
byte[] frame = new byte[frameLength];
for(int i=0; i<frameLength; i++) {
input = in.read();
if(input < 0) {
active = false;
break;
} else frame[i] = (byte) input;
}
// playWavPiece(frame);
// streamed frame byte array is full
// use it here ;-)
}
}

Categories

Resources