I wish to send a JPG-image from a client to a server over a TCP socket connection. I convert the file to a byte array in the client then send it to the server after having sent the array length. The server reads the data one byte at a time from the socketinputstream to a new bytearray which is then written to a jpg-file. The data however is corrupted and this image cannot be viewed. Below is core java code for client and server.
Client:
Socket s = new Socket("localhost", 666);
DataOutputStream dos = new DataOutputStream(s.getOutputStream());
String imgPath = "C:/Users/huehuehue/Documents/Uni/D0036D/prick1.JPG";
File file = new File(imgPath);
byte[] b = new byte[(int) file.length()];
FileInputStream fis = new FileInputStream(file);
fis.read(b);
fis.close();
dos.writeInt((int) file.length());
dos.flush();
dos.write(b, 0, b.length);
dos.flush();
Server:
ServerSocket serverSocket = new ServerSocket(666);
Socket connect = serverSocket.accept();
DataInputStream dis = new DataInputStream(connect.getInputStream());
File file = new File("C:/Users/huehuehue/Documents/Uni/D0036D/PLAYERprick.JPG");
FileOutputStream fos = new FileOutputStream(file);
int arrlen = dis.readInt();
byte[] b = new byte[arrlen];
int i = 0;
for(i = 0; i < arrlen; i++) {
b[i] = dis.readByte();
i++;
}
fos.write(b, 0 , b.length);
I can't see why this wouldn't work and any help or suggestions is greatly appreciated.
int arrlen = dis.readInt();
byte[] b = new byte[arrlen];
int i = 0;
for(i = 0; i < arrlen; i++) {
b[i] = dis.readByte();
i++;
}
You're incrementing i twice so you're ignoring half the input, and writing what you don't ignore into the wrong slots. Fortunately there is an easier way, which is also much more efficient:
int arrlen = dis.readInt();
byte[] b = new byte[arrlen];
dis.readFully(b);
NB fis.read(b); isn't valid. You can't assume it fills the buffer. You should use DataInputStream.readFully() here too.
You need to match both ends of the socket and your client doesn't fill your byte[] fully; instead your client could send each byte as it reads it. And, since Java provides BufferedInputStream you don't have to manually use and tweak a byte[] buffer directly. Instead you could do something like,
Socket s = new Socket("localhost", 666);
String imgPath = "C:/Users/huehuehue/Documents/Uni/D0036D/prick1.JPG";
File file = new File(imgPath);
try (InputStream is = new BufferedInputStream(new FileInputStream(file));
DataOutputStream dos = new DataOutputStream(s.getOutputStream());) {
dos.writeLong(file.length()); // <-- remember to read a long on server.
int val;
while ((val = is.read()) != -1) {
dos.write(val);
}
dos.flush();
}
Also, your server could use the similar BufferedOutputStream to get the benefit of a buffer like
ServerSocket serverSocket = new ServerSocket(666);
File file = new File("C:/Users/huehuehue/Documents/Uni/D0036D/PLAYERprick.JPG");
try (Socket s = serverSocket.accept();
DataInputStream dis = new DataInputStream(
s.getInputStream());
OutputStream fos = new BufferedOutputStream(
new FileOutputStream(file));) {
long arrlen = dis.readLong();
for (long i = 0; i < arrlen; i++) {
fos.write(dis.read());
}
fos.flush();
}
without having to manually manage it. Notice the above use BufferedInputStream and BufferedOutputStream to manage the buffering as an optimization. You could remove them and read and write directly from the File streams.
Related
im having a trouble here, here i built this Socket server to download files, it downloades the files inside of a folder, but it doesnt download a specified file name an returns null, could you guys help me where should i change in this server to do so?
sOutput = new ObjectOutputStream(socket.getOutputStream());
sInput = new ObjectInputStream(socket.getInputStream());
// read the username
OutputStream os = socket.getOutputStream();
DataOutputStream dos = new DataOutputStream(os);
String Request = (String) sInput.readObject();
System.out.println("request is:"+Request);
String[] todoname=Request.split("\\#reza-hp");
String name=todoname[0];
System.out.println("Connecting...");
File filcheck = new File("D://Users//ProfileImages//"+name+"
//"+"ProfileImage,imagechange_1,"+name+",.jpg");
System.out.println(filcheck);
if (filcheck.exists()){
fil = new File("D://Users//ProfileImages//"+name+"
//"+"ProfileImage,imagechange_1,"+name+",.jpg");
}else{
fil = new File("D://Users//Default//");
}
System.out.println(fil);
File[] Files=fil.listFiles();
System.out.println(Files);
for (int count=0;count < Files.length;count ++){
System.out.println(Files[count].getName());
}
os = socket.getOutputStream();
dos = new DataOutputStream(os);
dos.writeInt(Files.length);
for (int count=0;count<Files.length;count ++){
dos.writeUTF(Files[count].getName());
}
for (int count=0;count<Files.length;count ++){
int filesize = (int) Files[count].length();
dos.writeInt(filesize);
}
for (int count=0;count<Files.length;count ++){
int filesize = (int) Files[count].length();
byte [] buffer = new byte [filesize];
FileInputStream fis = new FileInputStream(Files[count].toString());
BufferedInputStream bis = new BufferedInputStream(fis);
//Sending file name and file size to the server
bis.read(buffer, 0, buffer.length); //This line is important
dos.write(buffer, 0, buffer.length);
fis.close();
dos.flush();
//close socket connection
// socket.close();
}
// Toast.makeText(getApplicationContext(),"Transfer file is
completed!!", Toast.LENGTH_LONG).show();
dos.close();
os.close();
//socket.close();
thanks
Well fixed it , here i post the singe file downloader, and in the question is the File's containing's download from any type,image,music,video , both are very viable for who ever needs to use, good luck.
String Type;
Socket socket;
ObjectInputStream sInput;
ObjectOutputStream sOutput;
// my unique id (easier for deconnection)
int id;
// Constructore
ClientThread(Socket socket) throws InterruptedException {
// a unique id
id = ++uniqueId;
this.socket = socket;
/* Creating both Data Stream */
System.out.println("Thread trying to create Object Input/Output
Streams");
while (!jobdone){
try
{
// create output first
sOutput = new ObjectOutputStream(socket.getOutputStream());
sInput = new ObjectInputStream(socket.getInputStream());
// read the username
OutputStream os = socket.getOutputStream();
DataOutputStream dos = new DataOutputStream(os);
String Request = (String) sInput.readObject();
System.out.println("request is:"+Request);
String[] todoname=Request.split("\\#reza-hp");
String name=todoname[0];
System.out.println("Connecting...");
File filcheck = new File("D://Users//ProfileImages//"+name+"
//"+"ProfileImage,imagechange_1,"+name+",.jpg");
System.out.println(filcheck);
if (filcheck.exists()){
fil = new File("D://Users//ProfileImages//"+name+"
//"+"ProfileImage,imagechange_1,"+name+",.jpg");
}else{
fil = new File("D://Users//Default//");
}
System.out.println(fil);
// File[] Files=fil.listFiles();
// for (int count=0;count < Files.length;count ++){
System.out.println(fil.getName());
// }
os = socket.getOutputStream();
dos = new DataOutputStream(os);
dos.writeInt(1);
// for (int count=0;count<Files.length;count ++){
dos.writeUTF(fil.getName());
// }
// for (int count=0;count<Files.length;count ++){
int filesize = (int) fil.length();
dos.writeInt(filesize);
// }
// for (int count=0;count<Files.length;count ++){
byte [] buffer = new byte [filesize];
FileInputStream fis = new FileInputStream(fil.toString());
BufferedInputStream bis = new BufferedInputStream(fis);
//Sending file name and file size to the server
bis.read(buffer, 0, buffer.length); //This line is important
dos.write(buffer, 0, buffer.length);
fis.close();
dos.flush();
//close socket connection
// socket.close();
// Toast.makeText(getApplicationContext(),"Transfer file is
completed!!", Toast.LENGTH_LONG).show();
dos.close();
os.close();
//socket.close();
}
i want to send the file size and one other integer, the tabelet serial number from and android tablet to a server running windows 7:
what is wrong with my client code and how to make the server accept the to different byte streams of data?
client code of android tablet
try {
byte[] bytes = new byte[(int) length];
fis = new FileInputStream(file2);
bis = new BufferedInputStream(fis);
bos = new BufferedOutputStream(socket.getOutputStream());
int count;
// convert integer to byte
ByteArrayOutputStream baos = new ByteArrayOutputStream();
DataOutputStream dos = new DataOutputStream(baos);
dos.writeInt(1992);
byte[] preByte = baos.toByteArray();
// the first byte that sends the tablet serial number and the size of the next file it is going to send
bis.read(preByte);
// the next sent is the file itself which is a database file
while ((count = bis.read(bytes)) > 0) {
bos.write(bytes, 0, count);
}
fis.close();
bis.close();
fis = null;
bis = null;
socket.close();
server code that will receive the two files
fos = new FileOutputStream(file);
bos = new BufferedOutputStream(fos);
System.out.println("streams are setup from new thread\n");
is = socket.getInputStream();
bufferSize = socket.getReceiveBufferSize();
buffer = new byte[bufferSize];
while ((count = is.read(buffer)) > 0) {
bos.write(buffer, 0, count);
} // end while
bos.flush();
bos.close();
is.close();
try this
dos = new DataOutputStream(new BufferedOutputStream(socket.getOutputStream()));
dos.writeLong(length);
dos.writeInt(1992);
for (int b; (b = bis.read()) != -1;) {
dos.write(b);
}
...
dis = new DataInputStream(new BufferedInputStream(socket.getInputStream()));
long length = dis.readLong();
int serialNumber = dis.readInt();
... read bytes
It seems that you are reading into preByte array instead of writing it to the output stream:
bis.read(preByte);
You probably meant:
bos.write(preByte);
You don't need the ByteArrayOutputStream at all. Just wrap a DataOutputStream around the BufferedOutputStream, and do all the writes via the DataOutputStream.
I have another problem.
This is a part of my client:
Socket socket = new Socket("127.0.0.1", 3000);
OutputStream out = socket.getOutputStream();
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutput oo = null;
try {
oo = new ObjectOutputStream(bos);
oo.writeObject(mp3dataStrings);
byte[] serializedMP3 = bos.toByteArray();
out.write(serializedMP3);
out.flush();
} finally {
oo.close();
bos.close();
}
this is a part of my server:
ServerSocket clientConnect = new ServerSocket(port);
System.out.println("SimpleServer running on port" + port);
Socket clientSock = clientConnect.accept();
InputStream is = clientSock.getInputStream();
byte[] buffer = new byte[1024];
for (int i = 0; i < buffer.length; i++) {
int b = is.read();
buffer[i] = (byte) b;
if (b == -1 | b == 0) break;
}
ObjectInputStream stream = new ObjectInputStream(new ByteArrayInputStream(buffer));
String[][] songs = (String[][]) stream.readObject();
stream.close();
When I send my object (a String[][]) I get the exception invalid stream header: ACED0000.
I can't find what this means and what I have to do.
greets
Alex
You have made it far more complicated than you need to.
Socket socket = new Socket("127.0.0.1", 3000);
try {
ObjectOutputStream oo = new ObjectOutputStream(socket.getOutputStream());
oo.writeObject(mp3dataStrings);
oo.close();
} finally {
socket.close();
}
and
ServerSocket clientConnect = new ServerSocket(port);
System.out.println("SimpleServer running on port" + port);
Socket clientSock = clientConnect.accept();
try {
ObjectInputStream stream = new ObjectInputStream(clientSock.getInputStream());
String[][] songs = (String[][]) stream.readObject();
} finally {
clientSock.close();
}
I agree with Peter Lawrey's answer, however the problem in your original code stems from the exit condition in the byte buffer population code
byte[] buffer = new byte[1024];
for (int i = 0; i < buffer.length; i++) {
int b = is.read();
// THIS ARE PROBLEM LINES
buffer[i] = (byte) b;
if (b == -1 | b == 0) break;
}
ObjectInputStream stream =
new ObjectInputStream(new ByteArrayInputStream(buffer));
You should only exit this loop when you've detected End-Of-Stream condition. In other words you should never consider b==0, as it is a valid part of the ObjectInputStream.
Second, you should not assign the byte to the buffer before checking for break condition.
Third, if you initialize ByteArrayInputStream, you should pass only the number of bytes that contain input, not the whole buffer itself.
Corrected block should be like this:
// How do you know if 1024 is enough to get all data?
// For the sake of this example, assume it's enough
byte[] buffer = new byte[1024];
int count = 0;
for (; count < buffer.length; count++) {
int b = is.read();
if ( b == -1 )
{
// exit only on End-Of-Stream, and do not record
// this result into the buffer
break;
}
buffer[count] = (byte) b;
}
ObjectInputStream stream =
new ObjectInputStream(
// Note, that we are now passing the number of 'active' bytes in the buffer
new ByteArrayInputStream(buffer, 0, count)
);
Would you take a look over this?:
This is my client:
try {
Socket socket = new Socket("127.0.0.1", 3000);
OutputStream out = socket.getOutputStream();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(mp3data);
oos.close();
byte[] bytes = baos.toByteArray();
out.write(bytes);
}
catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
This is my server:
int port = 3000;
try {
ServerSocket clientConnect = new ServerSocket(port);
System.out.println("SimpleServer running on port" + port);
Socket clientSock = clientConnect.accept();
InputStream is = clientSock.getInputStream();
byte[] buffer = new byte[1024];
int read = is.read(buffer);
ObjectInputStream ois = new ObjectInputStream(is);
MP3[] songs = (MP3[])ois.readObject();
clientSock.close();
// HTML erzeugen
Website site = new Website("index2.html",songs);
} catch (Exception e) {
System.out.println (e);
}
It ain't work. I don't get any exceptions but the Website-Constructor isn't called.
You assume that the whole byte array is read in just one call to read(), and has a length of exactly 1024 bytes. That's not the case (unless you're extremely lucky). Moreover, your ObjectInputStream is wrapping the InputStream from which you have already read the bytes (or some of the bytes) constituting the message. And also, the bytes written by the sender are not flushed.
Don't ignore the result of the call to is.read(): It tells you how many bytes have actually been read. And until it's not -1, you should continue to read, in a loop.
Read the Java tutorial on byte streams.
That said, you're making things difficult. Why don't you write the object directly to the socket output stream, and read the object directly from the socket input stream at the other side?
int port = 3000;
try {
ServerSocket clientConnect = new ServerSocket(port);
System.out.println("SimpleServer running on port" + port);
Socket clientSock = clientConnect.accept();
InputStream is = clientSock.getInputStream();
byte[] buffer = new byte[1024];
for (int i = 0; i < buffer.length; i++) {
int b = is.read();
if (b ==-1) break;
buffer[i] = (byte) b;
}
ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(buffer));
MP3[] songs = (MP3[])ois.readObject();;
ois.close();
clientSock.close();
I tried to solve the problem in many ways but without success and I have also looked for information in this forum but with same results, so here we go.
I am actually doing a server daemon that accepts client requests and then it (the server) transfers all the files contained in a specific folder. I'm going to post the code of the sendFileData (on the server) and the receiveFileData (on the client).
The server uses:
public static void sendFileData(File file, Socket socket) throws FileNotFoundException, IOException, SocketException {
byte[] auxiliar = new byte[8192];
byte[] mybytearray = new byte[(int) file.length()];
int longitud = mybytearray.length;
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file));
bis.read(mybytearray, 0, longitud);
DataOutputStream os = new DataOutputStream(socket.getOutputStream());
int paquetes = longitud / 8187;
int resto = longitud % 8187;
int i = 0;
while(i<paquetes){//The length goes on the first 4 bytes and the 5th tells if there are more packets to send (8192 bytes or less).
byte[] bytes = ByteBuffer.allocate(4).putInt(8187).array();
auxiliar[0] = bytes[0];
auxiliar[1] = bytes[1];
auxiliar[2] = bytes[2];
auxiliar[3] = bytes[3];
auxiliar[4] = 1;
for(int j = 5; j < 8192; j++){
auxiliar[j] = mybytearray[i*8187+(j-5)];
}
os.write(auxiliar, 0, 8192);
i+=1;
}
if(resto > 0){
byte[] bytes = ByteBuffer.allocate(4).putInt(resto).array();
auxiliar[0] = bytes[0];
auxiliar[1] = bytes[1];
auxiliar[2] = bytes[2];
auxiliar[3] = bytes[3];
auxiliar[4] = 0;
for(int j = 5; j < resto+5; j++){
auxiliar[j] = mybytearray[i*8187+(j-5)];
}
os.write(auxiliar, 0, resto+5);
}
os.flush();
}
And in the client side:
public static void receiveFileData(String nombreFichero, Socket s) throws IOException{
File monitored = new File(nombreFichero);
if(monitored.exists() == false){
monitored.createNewFile();
}
byte[] mybytearray;
DataInputStream is = new DataInputStream(s.getInputStream());
FileOutputStream fos = new FileOutputStream(monitored);
BufferedOutputStream bos = new BufferedOutputStream(fos);
int bytesRead = 0;
int hasNext = 1;
do {
bytesRead = is.readInt();//Leo longitud
try {
Thread.sleep(1);// HERE!!!!
} catch (InterruptedException e) {
}
// System.out.println("Bytes read "+bytesRead );
if(bytesRead <= 8187 && bytesRead > 0){
// System.out.println("Bytes leídos "+bytesRead );
hasNext = is.readByte();//Leo si hay más datos por enviar
mybytearray = new byte[bytesRead];
is.read(mybytearray);
if(monitored.exists()){
synchronized(monitored){
bos.write(mybytearray, 0, mybytearray.length);
}
}
mybytearray = null;
}else{
System.out.println("Fuera de rango "+bytesRead);
}
}while(hasNext == 1);
bos.close();
mybytearray = null;
System.out.println("Fichero recibido: "+monitored.getAbsolutePath());
}
In the receiveFileData code, if I do not put a Thread.sleep(1) or a System.out.println() or whatever who takes time to execute, I am not receiving the data in the correct way on the client, because readInt() returns a very high number randomly negative or positive (which implies Heap out of memory and other exceptions).
Sure it's something about synchronization but I think the transfering schema between the two methods is correct (maybe the client is too slow and server too fast).
What is happening?? Because I do not want to put a Thread.sleep, this is not good programming here I think.
Thank you so much!
is.read(bytes) is not guaranteed to fill the supplied byte array. You need to check its return value to see how many bytes were read or (better) use readFully().
The sleep() probably just allows time for all bytes to have been returned from the socket.