I know that there is a good variant to use Scanner object when you need to get data from server during connetion. But I have question about the following code snippet:
public void sendMessage(String message) {
try {
OutputStream os = socket.getOutputStream();
try {
byte[] buffer;
buffer = message.getBytes();
os.write(buffer);
} finally {
os.close();
}
InputStream is = socket.getInputStream();
try {
StringBuffer data = new StringBuffer();
Scanner in = new Scanner(is);
while (in.hasNext()) {
data.append(in.next());
}
System.out.println(data.toString());
} finally {
is.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
I'm confused by the snippet where Scanner gets data from InputStream, because it starts just after I send a message to the Server. Is it fair to suppose that data from the Server won't be in InputStream immediatelly after sending message to it?
Please, give me an advice, what is the best way to make reading data from InputStream in such case and what I should to take into consideration?
The InputStream.read() method called by Scanner blocks until there is some data available. So you don't have to worry about the response time of the server.
See: http://download.oracle.com/javase/6/docs/api/java/net/Socket.html#getInputStream()
The code is invalid. All it does is read as much input as can be read without blocking. There is no implication that what has been read is a complete message, or corresponds to a single write() invocation at the sender, etc. If you want messages in TCP/IP you must implement them yourself, with a length word prefix, a self-describing protocol such as Object Serialization or XML, etc. etc.
Related
I am trying to create an application where my client program reads the message from echo server. I'm trying to use Future to read the message from the server that will have a larger size than my allocated bytebuffer. My thought is to read into a outputstream until end-of-stream. However I think the code will stuck at readBytes = socket.read(buffer).get() at the last try becuase there will be nothing left to read from the socketchannel and Future will be blocked here.
Please let me know how to fix this or another way around.
public String receiveMessage(){
String message = "";
if (socket.isOpen()) {
try {
ByteBuffer buffer = ByteBuffer.allocate(2);
Future<Integer> readResult = socket.read(buffer);
int readBytes = readResult.get();
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
while (readBytes != -1) {
outputStream.write(buffer.array());
buffer.clear();
readBytes = socket.read(buffer).get();//stuck at here
}
byte result[] = outputStream.toByteArray();
System.out.println(result);
message = new String(result, Charset.defaultCharset()).trim();
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
return message;
}
'''
As this is an assignment, I believe I am not supposed to provide a completely functional answer, but here are some hints to guide you:
Oracle has many great Java tutorials including the one on sockets.
For asynchronous execution, I recommend creating a new java.lang.Thread object. Threads and Concurrency (unsurprisingly) also has a tutorial by Oracle. You may have something like the following, which I found useful when experimenting with Java sockets.
// write to server
Socket socket = //...
String message = //...
try (PrintWriter writer = new PrintWriter(socket.getOutputStream(), false)) {
writer.println(message);
// will auto-flush on '\n' (newline character) if 'false' in constructor is changed to
// true or omitted (look at PrintWriter documentation)
writer.flush();
} catch (IOException ioe) {
ioe.printStackTrace();
}
// read from server
Socket socket = //...
try (BufferedReader reader = new BufferedReader(new InputReader(socket.getInputStream()))) {
// TODO
} catch (IOException ioe) {
ioe.printStackTrace();
}
// pipe input stream to output stream
// Perhaps you want what comes from the server to go directly into stdout
Socket socket = //...
new Thread() {
#Override
public void run() {
try {
socket.getInputStream().transferTo(System.out);
// socket input stream is at end of stream, but not necessarily closed
} catch (IOException e) {
e.printStackTrace();
}
}
}.start();
Note that using InputStream#transferTo(OutputStream) will not terminate until the InputStream is closed, which is why you might want to execute it in its own thread.
Also, be careful about the above code segment: if you send a message through a socket using a PrintWriter then immediately close the PrintWriter, the PrintWriter will try to close the underlying OutputStream. Once that closes, it will generally try to close the Socket (whose OutputStream was being written to by the PrintWriter), and no more communication can be done through that socket (which will lead to a BrokenPipeException on attempted further use). So, perhaps try to send a message using newline characters as delimiters or something similar, which would be convenient for using a BufferedReader.
My problem is when it tries to read the object the second time, it throws the exception:
java.io.StreamCorruptedException: invalid type code: AC
at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1356)
at java.io.ObjectInputStream.readObject(ObjectInputStream.java:351)
at Client.run(BaseStaInstance.java:313)
java.io.StreamCorruptedException: invalid type code: AC
at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1356)
at java.io.ObjectInputStream.readObject(ObjectInputStream.java:351)
at Client.run(BaseStaInstance.java:313)
The first time I send the exact same object message; however, when I try doing the same thing the second time, it throws the error above. Do I need to re-intialize the readObject() method? I even printed out the message object that is being received by the line below and its exact the same as the first instance where it works ok.
Object buf = myInput.readObject();
I'm assuming there's some problem with appending, but I really have no use for appending. I just want to read a fresh line everytime.
I'd really appreciate some help in fixing this bug. Thank you.
==================================
Before that one line, I'm just creating the input and output objects for the socket in the run() method. The object declaration is outside the run() method in the class:-
#Override
public void run() {
try {
sleep((int) 1 * 8000);
} catch (Exception e) {
e.printStackTrace();
}
try {
//Creating input and output streams to transfer messages to the server
myOutput = new ObjectOutputStream(skt.getOutputStream());
myInput = new ObjectInputStream(skt.getInputStream());
while (true) {
buf = myInput.readObject();
}
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
} catch (Exception e) {
e.printStackTrace();
}
}
}
You're right; I don't close the object. I'm not sure how to do that.
The underlying problem is that you are using a new ObjectOutputStream to write to a stream that you have already used a prior ObjectOutputStream to write to. These streams have headers which are written and read by the respective constructors, so if you create another ObjectOutputStream you will write a new header, which starts with - guess what? - 0xAC, and the existing ObjectInputStream isn't expecting another header at this point so it barfs.
In the Java Forums thread cited by #trashgod, I should have left out the part about 'anew for each object at both ends': that's just wasteful. Use a single OOS and OIS for the life of the socket, and don't use any other streams on the socket.
If you want to forget what you've written, use ObjectOutputStream.reset().
And don't use any other streams or Readers or Writers on the same socket. The object stream APIs can handle all Java primitive datatypes and all Serializable classes.
I am developing a tool to get client information, send to a server, and receive the information again (a proxy). I'm also trying to dump the data being received from the server. I can read the Integer representation of the inputStream, but I am not able to read the String format. I've tried the below example, but it hangs and never connects to the server. Also, System.out.println(inputStream.nextLine()) displays only one line and hangs.
public void run() {
try {
int i;
while ((i = inputStream.read()) != -1){
System.out.println(IOUtils.toString(inputStream));
outputStream.write(i);
}
} catch (IOException e) {
System.out.println("Lost connection to the client.");
}
}
My guess at this is that you're reading from the input stream, and then using the IOUtils library to read from the stream too. My suspicion is that your application is reading the first byte from the input stream, then reading the remainder of the inputstream with the IOUtils library, and then printing out the initial byte that was read.
It doesn't make any sense to call IOUtils.toString(inputstream) from within a loop. That method call will put all the data from the inputstream into a string. Why have the loop at all in this case?
You might want to try not using the IOUtils library for this. Just read a byte of data, push it into a StringBuilder, and then print that byte. In this approach, the loop would be necessary, and you'll probably get what you're looking for.
Try something like this, but modify it as necessary to print the data at the same time to your output stream:
public static String inputStreamToString(final InputStream is, final int bufferSize)
{
final char[] buffer = new char[bufferSize];
final StringBuilder out = new StringBuilder();
try {
final Reader in = new InputStreamReader(is, "UTF-8");
try {
for (;;) {
int rsz = in.read(buffer, 0, buffer.length);
if (rsz < 0)
break;
out.append(buffer, 0, rsz);
}
}
finally {
in.close();
}
}
catch (UnsupportedEncodingException ex) {
/* ... */
}
catch (IOException ex) {
/* ... */
}
return out.toString();
}
The code you posted doesn't attempt to connect to the server, but if any of it executes you must already have connected.
If your program is hanging in this code, either the server hasn't sent any data yet, or the IOUtils.toString() method probably tries to read to EOS, so if the peer doesn't close the connection you will block here forever.
If your program hangs at a readLine() call it means the peer hasn't sent a line to read.
I am sending byte array to the server and server should receive the data I sent. But the server taking very long time to receive the data. Server is waiting in Inputstream.
If I send String data by converting to bytes then the server will receive. I dont know what is happening. Please help me.
Client:
void Send(byte []arr)
{
//Other code
String s=new String(arr);
byte []msgByte=s.getBytes();
try
{
outStream.write(msgByte);
}
catch(Exception e)
{}
//Other Code
}
Server:
InputStream inStream1=connection.openInputStream();
BufferedReader bReader1=new BufferedReader(new InputStreamReader(inStream1));
String lineRead=bReader1.readLine();
System.out.println(lineRead);
inStream1.close();
You need to add a '\n' (new line character) at the end of the message because you expect to read a line on the server and also you need to flush the stream after writing the message, because, by default system is not automatically flushing it and also flushing depends on the type of the OutputStream used.
void Send(byte[] arr) {
// Other code
String s = new String(arr) + "\n"; // appending '\n'
byte[] msgByte = s.getBytes();
try {
outStream.write(msgByte);
} catch (Exception e) {
}
outStream.flush(); //flushing the stream
// Other Code
}
Try closing the outStream using outStream.close()
The Bluetooth chat example for Android is very useful to learn how to pass strings between phones - is it possible to use the same code to pass objects between phones? I have the same classes defined in both phones, I just want to pass the instance of one class from one phone to another. Is there any sample code available? I tried using serialization and replacing outputstream and inputstream in the chat example with objectoutputstream and objectinputstream but it didn't seem to work
The best way I found to handle this was the following:
I set up my objects as implementing Serializable that I wanted to send.
I set up the following code to manage the messages:
public byte[] serialize() throws IOException {
ByteArrayOutputStream b = new ByteArrayOutputStream();
ObjectOutputStream o = new ObjectOutputStream(b);
o.writeObject(this);
return b.toByteArray();
}
//AbstractMessage was actually the message type I used, but feel free to choose your own type
public static AbstractMessage deserialize(byte[] bytes) throws IOException, ClassNotFoundException {
ByteArrayInputStream b = new ByteArrayInputStream(bytes);
ObjectInputStream o = new ObjectInputStream(b);
return (AbstractMessage) o.readObject();
I changed the write statements to accept a Serializable, and then make the final write:
/**
* Write to the connected OutStream.
* #param buffer The bytes to write
*/
public void write(AbstractMessage buffer) {
try {
Log.v(TAG,"Writing \""+(buffer.serialize())+"\"");
mmOutStream.write(buffer.serialize());
// Share the sent message back to the UI Activity
mHandler.obtainMessage(AbstractMessageManager.MESSAGE_WRITE, -1, -1, buffer)
.sendToTarget();
} catch (IOException e) {
Log.e(TAG, "Exception during write", e);
}
}
The Bluetooth Chat example is a demonstration of using the Serial Port Profile (SPP) which is based upon RFCOMM. You can serially send across any data you like once the connection is established; you simply need to be able to represent your objects into a serial stream of bytes, i.e. serialize them.
Therefore the use of serialization would certainly be a way of getting your objects sent over the link. The Bluetooth API's send and receive functions deal with arrays of bytes, but you could easily adapt the Bluetooth Chat example to use streams, e.g. the send function would read bytes out of a stream and put them into an array buffer, then you send that buffer, etc. Then the application code would simply talk via input and output stream pipes - that's one way I've done it in the past.
So there's nothing wrong with your actual idea. The bigger problem is that the way you've implemented it is not right, and more problematic still is that the way you've asked your question is quite poor, too. You need to be more descriptive about exactly what didn't work, explain what debugging you've already tried, and post code samples and Logcat outputs so we can help you properly.
Finally, I did find what I think is a bug in the Bluetooth Chat code example: The data receive function passes a reference of the receive byte array to the ArrayList that's used to show each line of text received. This is alright when small amounts of slow text are being transmitted across, but when you try to send large amounts of data, you start to see the data being corrupted, presumably because the ArrayList adapter is still reading bytes out of that same array when the array is being filled with even newer data.
The answer is yes. A String is an Object. Remember? But how exactly to do it, I am still searching for a solution and that's what brought me here...
Trev16v,
First of all, thanks for your initial feedback.
In order to serialise my object, I used the classes serializeObject and deserializeObject from
http://www.jondev.net/articles/Android_Serialization_Example_(Java)
They seem to work well: if I serialise an object (created out of a class that implements Serializable) from a phone/activity and deserialize it from the same phone i manage to get an object out of the generated byte[].
I then tried to use the same code in the class BluetoothChatServices in the bluetooth chat example in oder to send the serialised object to the other phone (in that example there is
public ConnectedThread(BluetoothSocket socket) {
Log.d(TAG, "create ConnectedThread");
mmSocket = socket;
InputStream tmpIn = null;
OutputStream tmpOut = null;
// Get the BluetoothSocket input and output streams
try {
tmpIn = socket.getInputStream();
tmpOut = socket.getOutputStream();
} catch (IOException e) {
Log.e(TAG, "temp sockets not created", e);
}
mmInStream = tmpIn;
mmOutStream = tmpOut;
}
and the bytes are passed using
public void write(byte[] buffer) {
try {
mmOutStream.write(buffer);
// Share the sent message back to the UI Activity
mHandler.obtainMessage(BluetoothChat.MESSAGE_WRITE, -1, -1, buffer)
.sendToTarget();
} catch (IOException e) {
Log.e(TAG, "Exception during write", e);
}
}
and read using
public void run() {
Log.i(TAG, "BEGIN mConnectedThread");
byte[] buffer = new byte[10240];
int bytes;
// Keep listening to the InputStream while connected
while (true) {
try {
// Read from the InputStream
bytes = mmInStream.read(buffer);
// Send the obtained bytes to the UI Activity
mHandler.obtainMessage(BluetoothManageActivity.MESSAGE_READ, bytes, -1, buffer)
.sendToTarget();
} catch (IOException e) {
Log.e(TAG, "disconnected", e);
connectionLost();
break;
}
}
}
The problem with using BluetoothChatServices as it is is that the array of bytes received on the other phone is different from the one sent when it comes to serialised objects. For example, to give an idea element [0] of the seriealized object is =-84 when i send it, the one i receive from the other phone has element [0] to [4] =0, then [5]=4 and all the other elements are also not aligned. I tried in the methods write and run above to change Inputstream and Outputstream with ObjectInputStream and ObjectOutputstream but without success (if this was supposed to be the way to implement it, I can post the code I tried to use)
Again, thanks a lot for your help, I am new to all these concepts so if I am talking nonsense I will be also happy to be addressed to a tutorial
thanks
Facing same problem ... When i am sending a series of objects from one Android device, data sends properly ... But in receiving end all objects does not construct from received byte[].
Error occurs randomly for any received object but the same code works properly in Java ... I think the some bytes misses when transferring data from one device to another ...
Serializable object to byte[] and byte[] to object conversion can be done with the following code
public static byte[] toByteArray(Object obj)
{
byte[] bytes = null;
ObjectOutputStream oos = null;
try
{
oos = new ObjectOutputStream(new ByteArrayOutputStream());
oos.writeObject(obj);
oos.flush();
return bos.toByteArray();
}
catch(Exception e)
{
Log.e("Bluetooth", "Cast exception at sending end ...");
}
return bytes;
}
public static Object toObject(byte[] bytes)
{
Object obj = null;
ObjectInputStream ois = null;
try
{
ois = new ObjectInputStream(new ByteArrayInputStream(bytes));
return ois.readObject();
}
catch(Exception ex)
{
Log.e("Bluetooth", "Cast exception at receiving end ...");
}
return obj;
}
I actually found the problem - when the bytes are loaded using
try {
// Read from the InputStream
bytes = mmInStream.read(buffer);
they are actually loaded in 2 steps.. While debugging and stepping into the code I found that if first loads 990 bytes and then the remaining bytes.. so when i am back to the UI handler i see only the bytes loaded in the second step..
i wonder if there is a way to force to load all bytes at once