I am sending files between 2 devices, so I established a socket communication. Right now, I am just trying to send one file, but in the future I want to send multiple files (selected by the user from a gridview).
The problem is that when I send one file, on the server side (that receives the file) the socket.getInputStream().read(buffer) does not detect the end of the file. It just waits for "more" data to be sent.
After searching a bit on this issue, I reached some topics that kind of gave me some options, but I am still not satisfied with it because I dont know if those options would be efficient to send multiple files. This is an example : How to identify end of InputStream in java
I could close the socket or the stream objects after sending a file, but if I want to send a lot of files, it wouldn't be efficient to be always closing and opening the sockets.
Code on the receiver :
File apkReceived = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS) + "/testeReceiveServerComm.apk");
byte[] buffer = new byte [8192];
FileOutputStream fos=new FileOutputStream(apkReceived);
int count=0;
int total=0;//so apra ir vendo quanto recebi.
while((count = in.read(buffer)) != -1){
fos.write(buffer,0,count);
total+=count;
System.out.println("Server Comm receive thread - already received this ammount : "+total);
}
Code on the client (sender) :
File apkToSend=new File(filePath);
byte[] buffer = new byte [8192];
BufferedInputStream bis=new BufferedInputStream(new FileInputStream(apkToSend));
int count;
int total=0;
while((count=bis.read(buffer))!=-1){
out.write(buffer,0,count);
total+=count;
out.reset();
System.out.println("send thread - already sent this ammount : "+total);
}
out.flush();
bis.close();
Related
Update: The problem must be on the Android side, not Qt.
The problem is simply I can't send more than 1000 bytes (correctly) from Windows (via Qt) to Android. Here I post full information:
Code in Qt Creator (Windows side):
QFile inputFile(fileInfo->absoluteFilePath());
QByteArray read;
inputFile.open(QIODevice::ReadOnly);
int size = 0;
while(1){
read.clear();
read = inputFile.read(1000);
qDebug()<< "Read : " <<read.size();
size += read.size();
if(read.size() == 0){
break;
}
QByteArray toWrite(read);
newSocket->write(toWrite);
newSocket->flush();
newSocket->waitForBytesWritten();
this->sleep(1);
}
inputFile.close();
qDebug()<<"Transfer Done! " << size << " bytes";}
Java code (Android side):
DataOutputStream dos;
DataInputStream dis;
Socket s;
s = new Socket("192.168.137.1",8080);
dos = new DataOutputStream(s.getOutputStream());
dis = new DataInputStream(s.getInputStream());
while(true){
if(dis.available() > 0 ) {
int chunkSize = 1000;
byte[] b = new byte[chunkSize];
dis.read(b);
writeToExternalStoragePublic("test.png",b);}
The code works pretty good. It even works when I set the chunks size on both sides to 1,000,000, and the data is written to Android, but a lot of the bytes are empty.
Check these photos from hex Workshop data visualizer. Using 1000 as chunkSize in the left and 2000 in right.
Here are the files:
1000ChunkSize
2000ChunkSize
The problem is 1000 is too slow, and it takes a lot of time even for small files though it works. What do you suggest?
A possible problem was the timing on the server side even though it has:
waitForBytesWritten();
I put
this->sleep(1);
But it didn't help.
Update: The problem must be on the Android side, not Qt.
In my app, i'm sending a file from a client, using sockets. On the other side, another client receive the file using InputStream and then bufferedOutputStream save the file in the system.
I don´t know why, the file isn´t utterly transmited. I think this is because of network overload, anyway, i don´t know how to solve it.
Transmiter is:
Log.d(TAG,"Reading...");
bufferedInputStream.read(byteArrayFile, 0, byteArrayFile.length);
Log.d(TAG, "Sending...");
bufferedOutputStream.write(byteArrayFile,0,byteArrayFile.length);
bufferedOutputStream.flush();
Receiver is:
bufferedOutputStream=new BufferedOutputStream(new FileOutputStream(file));
byteArray=new byte[fileSize];
int currentOffset = 0;
bytesReaded = bufferedInputStream.read(byteArray,0,byteArray.length);
currentOffset=bytesReaded;
do {
bytesReaded = bufferedInputStream.read(byteArray, currentOffset, (byteArray.length-currentOffset));
if(bytesReaded >= 0){ currentOffset += bytesLeidos;
}
} while(bytesReaded > -1 && currentOffset!=fileSize);
bufferedOutputStream.write(byteArray,0,currentOffset);
You don't state where filesize came from, but there are numerous problems with this code. Too many to mention. Throw it all away and use DataInputStream.readFully(). Or use the following copy loop, which doesn't require a buffer the size of the file, a technique which does not scale, assumes that the file size fits into an int, and adds latency:
byte[] buffer = new byte[8192];
int count;
while ((count = in.read(buffer)) > 0)
{
out.write(buffer, 0, count);
}
Use this at both ends. If you're sending multiple files via the same connection it gets more complex, but you haven't stated that.
I am trying to create a client-server android app in which I want to transfer a file using a UDP protocol. Till now I am able to transfer the file and receive the acknowledgements for the packets.
Now I want to add the sequence numbers to the with the data in the packet. I have tried to do the following:
Create a ByteArrayOutputStream.
Wrap it in an ObjectOutputStream
Write data to the object using writeObject()
Serialized class includes:
public class Message implements Serializable {
private int seqNo;
private byte[] data;
private boolean ack;
public Message(int seqNo, byte[] data, boolean ack) {
this.seqNo = seqNo;
this.data = data;
this.ack = ack;
}
Client Side
byte[] fileBytes = new byte[500];
ByteArrayOutputStream outStream = new ByteArrayOutputStream();
ObjectOutputStream os = new ObjectOutputStream(outStream);
while((numBytesRead = inputBuf.read(fileBytes)) != -1) {
//DatagramPacket packet = new DatagramPacket(fileBytes, fileBytes.length);
if (os == null) {
os = new ObjectOutputStream(outStream);
}
Message msg = new Message(++seqNo, fileBytes, false);
os.writeObject(msg);
os.flush();
os.reset();
byte[] data = outStream.toByteArray();
atagramPacket packet = new DatagramPacket(data, data.length);
clientSocket.send(packet);
}
Server Side
byte[] incomingData = new byte[1024];
while (true) {
try{
DatagramPacket incomingPacket = new DatagramPacket(incomingData, incomingData.length);
serverSocket.receive(incomingPacket);
byte[] data = incomingPacket.getData();
ByteArrayInputStream in = new ByteArrayInputStream(data);
ObjectInputStream is = new ObjectInputStream(in);
if (is == null) {
is = new ObjectInputStream(in);
}
Message msg = (Message) is.readObject();
System.out.println(msg.getSeqNo());
out.write(msg.getData(),0,msg.getData().length);
}
The problem that I am facing is
I am receiving the same sequence number for each packet (i.e. 1)
I am not sure about the buffer size for the incoming packet, as I am using 500 bytes at Client side and 1024 at
the Sever. And if I take 500 bytes at both the sides I get an EOFexception.
I would really appreciate if you could suggest better ways to implement the same thing! Thanks :)
Message msg = new Message(++seqNo, fileBytes, false);
Here you are assuming that the prior read() filled the buffer. On the last read() before end of file it almost certainly won't, and it isn't guaranteed to fill it any time, only to transfer at least one byte.
You should be passing the read count 'numBytes' to this constructor, and it should create a byte array of that size, and copy only that many bytes into it.
Other issues:
It is impossible for 'os' to be null at the point you're testing it.
Ditto 'is'.
You should be creating a new ObjectOutputStream and ByteArrayOutputStream per datagram.
Java Datagrams keep shrinking to the size of the shortest datagram payload received so far. You must either create a new one per receive, or at least reset its length before each receive.
you need a larger buffer at the receiver because of ObjectOutputStream overheads.
I don't believe this code presently works at all, let alone that you keep getting the same sequence number. More likely you keep getting the same message, because you're ignoring an exception somewhere.
I am having some issues with bytes being dropped over a bluetooth connection between an android device (Gingerbread 2.3.1) and a PC. The way I receiving the data is in a 2 byte buffer. The values being received is streaming from the PC over a few minutes (values represent a waveform). Here are just a few snippets of code so you can get the idea. The base of my code is from the android bluetooth chat sample code.
BluetoothSocket socket;
...
mmInStream=socket.getInputStream;
...
byte[] buffer= new byte[2];
...
bytes = mmInStream.read(buffer);
Has anyone has issues with this type of thing? The dropped bytes seem to happen at random times while at other times the values received are as expected. I am using a 2 byte buffer because the values I am receiving are 16 bit signed integers. From the PC side of things I am using RealTerm to send the binary files of data.
Is it possible that my buffer is too small and that is causing the dropped bytes?
Thanks
Following up to your answer. You could just use a counter to remember how many bytes already read and compare it to the number wanted and also use it for the index to write the next byte(s). See a C# version at http://www.yoda.arachsys.com/csharp/readbinary.html
public static void ReadWholeArray (Stream stream, byte[] data)
{
int offset=0;
int remaining = data.Length;
while (remaining > 0)
{
int read = stream.Read(data, offset, remaining);
if (read <= 0)
throw new EndOfStreamException
(String.Format("End of stream reached with {0} bytes left to read", remaining));
remaining -= read;
offset += read;
}
}
I have found what the issue is. I want to thank alanjmcf for pointing me in the right direction.
I wasn't checking by bytes variable to see how many bytes were returned from the mmInStream.read(buffer). I was simply expecting that every buffer returned would contain 2 bytes. The way i solved the issue was with the following code after getting the buffer back from the InputStream:
//In the case where buffer returns with only 1 byte
if(lagging==true){
if(bytes==1){
lagging=false;
newBuf=new byte[] {laggingBuf, buffer[0]};
ringBuffer.store(newBuf);
}else if(bytes==2){
newBuf=new byte[] {laggingBuf, buffer[0]};
laggingBuf=buffer[1];
ringBuffer.store(newBuf);
}
}else if(lagging==false){
if(bytes==2){
newBuf = buffer.clone();
ringBuffer.store(newBuf);
}else if(bytes==1){
lagging=true;
laggingBuf=buffer[0];
}
}
This fixed my problem. Any suggestions on a better methodology?
I'm trying to transfer about a megabyte of arbitrary data at a time from one android phone to another. Currently, I write the size, a command code and the data to a DataOutputStream around a BufferedOutputStream, around the OutputStream returned from bluetoothSocketInstance.getOutputStream().
The receiving phone reads the size and command code and then reads from the input stream until it has gotten all the data it is expecting. This works for short strings, but for larger files not all the data is transferred. Running the app in the debugger shows that the write returns without any exceptions and the read reads a fraction of the bytes expected and then blocks indefinitely. It also does not throw any exceptions.
Is there a buffer somewhere that is filling up? Is there something else I need to do to ensure that all the data gets transferred?
My code for the sender and receiver are below:
Sender:
try {
DataOutputStream d = new DataOutputStream(new BufferedOutputStream(mmOutStream,buffer.length+8));
//int b= buffer.length;
d.writeInt(buffer.length);
d.writeInt(command);
d.write(buffer);
d.flush();
} catch (IOException e) {
Log.e(TAG, "Exception during write", e);
}
}
Receiver:
try {
// Read from the InputStream
int messageSize= inStream.readInt();
int messageCode = inStream.readInt();
bytes=0;
buffer =new byte[messageSize];
while(bytes < messageSize)
{
bytes += inStream.read(buffer,bytes,messageSize - bytes);
}
message = bytes;
} catch (IOException e) {
Log.e(TAG, "disconnected", e);
connectionLost();
break;
}
After some more testing on my end, I changed my sending code to look like this:
for(int i=0; i<buffer.length;i+=BIG_NUM)
{
int b = ((i+BIG_NUM) < buffer.length) ? BIG_NUM: buffer.length - i;
d.write(buffer,i,b);
d.flush();
}
The files now get sent. Does anyone have an idea why? Does the call to flush() block until the data has actually been transferred? Is there any documentation about the size of the send and receive buffers that would help me to decide how large I can safely make BIG_NUM?
I have similar problem, when sending file there are some parts missing. I try BufferedOutputStream but problem still exist.
Finally i find simple solution:
You don't need to send buffer length, just split sending buffer to byte array (for example [8192]) and on receive side make sure that this buffer is much bigger about 4 or 8 times than sending buffer. This worked for me and file is sent completed.