This question already has answers here:
Java multiple file transfer over socket
(3 answers)
Closed 5 years ago.
I'm trying to send a file through Java sockets from one program to the other. My code is based off of this question. The issue is that on the Ubuntu machine hosting the server, the file sent over is 0 bytes in size. The code has worked on a local connection on my Windows laptop, so perhaps the issue has to do with the remote connection?
Server code:
int g = Integer.parseInt(in.readLine());
if(g > -1) {
InputStream fIn = client.getInputStream();
for(int i = 0; i < g; i++) {
String name = in.readLine();
byte[] bytes = new byte[16*1024];
File f = new File("plugins/" + name + ".jar");
if(!f.exists()) f.createNewFile();
FileOutputStream fOut = new FileOutputStream(f);
int count;
while ((count = fIn.read(bytes)) >= 0) {
fOut.write(bytes, 0, count);
}
fOut.flush();
fOut.close();
}
System.out.println("[" + client.getRemoteSocketAddress().toString() + "] added " + g + " new plugins.");
client.close();
}else{
client.close();
}
Client code:
JFileChooser fd = new JFileChooser("C:\\");
fd.setFileSelectionMode(JFileChooser.FILES_ONLY);
fd.setMultiSelectionEnabled(true);
fd.setFileFilter(new FileNameExtensionFilter(null,"jar"));
int r = fd.showOpenDialog(null);
if(r == JFileChooser.APPROVE_OPTION) {
File[] files = fd.getSelectedFiles();
OutputStream fOut = sock.getOutputStream();
out.println(files.length);
for(File f : files) {
out.println(f.getName().split("\\.")[0]);
byte[] bytes = new byte[16 * 1024];
FileInputStream fIn = new FileInputStream(f);
int count;
while ((count = fIn.read(bytes)) >= 0) {
fOut.write(bytes, 0, count);
}
fIn.close();
}
}else{
out.println(-1);
}
sock.close();
As said in the comments there are different issues in you code.
To recognise the end of single file you can simply add the file size after the filename separated by a Semicolon;
out.println(f.getName().split("\\.")[0] + "; size" + f.length());
On the Server side you accumulate the file size read / written stop read ing the content of the file close it and read the next line (filename / file size).
As said in the comments there are different issues.
You use fIn and fOut which is not clear how they are defined.
On the client side you use fOut while the Socket OutputStream is named 'out'.
Similar thing on the server side. You read the number of files from in but afterwards you get the InputStream (again?) from the Socket and use that as fIn. Depending on your in that may or may not work.
Also reading a line from via readLine from an InputStream and also binary content is not that trivial. In my example code I'm using the DataInputStream for that even the readLine method is deprecated.
The Example also uses the "try with resources" Syntax to ensure that all resources (Streams, Sockets) get closed in the case of an Exception.
The Example does not show how to deal with the file size on the server side That's up to you.
The example is completely runnable on a single machine and shows how to copy a single file via socket based on your code.
import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
public class ClientServerSocketExample {
private static class Server {
public void run() throws IOException {
try (ServerSocket serverSocket = new ServerSocket(41000, 10, InetAddress.getLocalHost())) {
try (Socket client = serverSocket.accept()) {
try (DataInputStream in = new DataInputStream(client.getInputStream())) {
int g = Integer.parseInt(in.readLine());
if(g > -1) {
for(int i = 0; i < g; i++) {
String[] filenameAndSize = in.readLine().split(";");
String name = filenameAndSize[0];
byte[] bytes = new byte[16*1024];
File f = new File("/tmp/" + name + ".jar");
if(!f.exists()) f.createNewFile();
try (FileOutputStream fOut = new FileOutputStream(f)) {
int count;
while ((count = in.read(bytes)) >= 0) {
fOut.write(bytes, 0, count);
}
fOut.flush();
}
}
System.out.println("[" + client.getRemoteSocketAddress().toString() + "] added " + g + " new plugins.");
}
}
}
}
}
}
private static class Client {
public void run() throws IOException {
try (Socket socket = new Socket()) {
socket.connect(new InetSocketAddress(InetAddress.getLocalHost(), 41000) );
sendFiles(socket);
}
}
private static void sendFiles(Socket sock) throws IOException {
File[] files = new File[]{new File("some.jar")};
OutputStream fOut = sock.getOutputStream();
PrintStream out = new PrintStream(fOut);
out.println(files.length);
for(File f : files) {
out.println(f.getName().split("\\.")[0] + "; size" + f.length());
byte[] bytes = new byte[16 * 1024];
try (FileInputStream fIn = new FileInputStream(f)) {
int count;
while ((count = fIn.read(bytes)) >= 0) {
out.write(bytes, 0, count);
}
out.flush();
}
}
}
}
public static void main(String ... args) throws IOException, InterruptedException {
new Thread(new Runnable() {
#Override
public void run() {
try {
new Server().run();
} catch (IOException ioe) {
System.out.println(ioe);
}
}
}).start();
System.out.println("Waiting for the Server to come up");
Thread.sleep(500);
new Client().run();
}
}
There may be an issue in this solution when the client sends a \r as new line separator and the file starts with \n. The server may count the \n as part of the line separator and the file will be corrupt (one byte will be missing).
Related
Ok, trying to transfer a specified directory of files over a socket, remove the directory objects from the arraylist, so only files are left, and transfer them 1 by 1 over the same socket. The arraylist here is filled with ONLY files, no directories. Heres the receive and send code for the client and server respectively . The code runs fine without errors, except for ALL the data is being written to the first file. The subsequent files are created in the server folder, but they are 0 bytes. Any input would be greatly appreciated.
THIS IS THE SERVER CODE FOR RECEIVING THE FILES
public void receive(){
try {
DataInputStream dis = new DataInputStream(new BufferedInputStream(socket.getInputStream()));
DataOutputStream dos = new DataOutputStream(new BufferedOutputStream(socket.getOutputStream()));
//read the number of files from the client
int number = dis.readInt();
ArrayList<File>files = new ArrayList<File>(number);
System.out.println("Number of Files to be received: " +number);
//read file names, add files to arraylist
for(int i = 0; i< number;i++){
File file = new File(dis.readUTF());
files.add(file);
}
int n = 0;
byte[]buf = new byte[4092];
//outer loop, executes one for each file
for(int i = 0; i < files.size();i++){
System.out.println("Receiving file: " + files.get(i).getName());
//create a new fileoutputstream for each new file
FileOutputStream fos = new FileOutputStream("C:\\users\\tom5\\desktop\\salestools\\" +files.get(i).getName());
//read file
while((n = dis.read(buf)) != -1){
fos.write(buf,0,n);
fos.flush();
}
fos.close();
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
THIS IS THE CLIENT CODE FOR SENDING THE FILES
public void send(ArrayList<File>files){
try {
DataInputStream dis = new DataInputStream(new BufferedInputStream(socket.getInputStream()));
DataOutputStream dos = new DataOutputStream(new BufferedOutputStream(socket.getOutputStream()));
System.out.println(files.size());
//write the number of files to the server
dos.writeInt(files.size());
dos.flush();
//write file names
for(int i = 0 ; i < files.size();i++){
dos.writeUTF(files.get(i).getName());
dos.flush();
}
//buffer for file writing, to declare inside or outside loop?
int n = 0;
byte[]buf = new byte[4092];
//outer loop, executes one for each file
for(int i =0; i < files.size(); i++){
System.out.println(files.get(i).getName());
//create new fileinputstream for each file
FileInputStream fis = new FileInputStream(files.get(i));
//write file to dos
while((n =fis.read(buf)) != -1){
dos.write(buf,0,n);
dos.flush();
}
//should i close the dataoutputstream here and make a new one each time?
}
//or is this good?
dos.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
You are reading the socket until read() returns -1. This is the end-of-stream condition (EOS). EOS happens when the peer closes the connection. Not when it finishes writing one file.
You need to send the file size ahead of each file. You're already doing a similar thing with the file count. Then make sure you read exactly that many bytes for that file:
String filename = dis.readUTF();
long fileSize = dis.readLong();
FileOutputStream fos = new FileOutputStream(filename);
while (fileSize > 0 && (n = dis.read(buf, 0, (int)Math.min(buf.length, fileSize))) != -1)
{
fos.write(buf,0,n);
fileSize -= n;
}
fos.close();
You can enclose all this in a loop that terminates when readUTF() throws EOFException. Conversely of course you have to call writeUTF(filename) and writeLong(filesize) at the sender, before sending the data.
I did it like this, it is working perfectly, you can take a look:
Send
byte[] done = new byte[3];
String str = "done"; //randomly anything
done = str.getBytes();
for (int i = 0; i < files.size(); i++) {
System.out.println(files.get(i).getName());
FileInputStream fis = new FileInputStream(files.get(i));
while ((n = fis.read(buf)) != -1) {
dos.write(buf, 0, n);
System.out.println(n);
dos.flush();
}
//should i close the dataoutputstream here and make a new one each time?
dos.write(done, 0, 3);
dos.flush();
}
//or is this good?
dos.close();
Receive
for (int i = 0; i < files.size(); i++) {
System.out.println("Receiving file: " + files.get(i).getName());
//create a new fileoutputstream for each new file
fos = new FileOutputStream("C:\\users\\tom5\\desktop\\salestools\\" + files.get(i).getName());
//read file
while ((n = dis.read(buf)) != -1 && n != 3) {
fos.write(buf, 0, n);
fos.flush();
}
fos.close();
}
I have created a server and client. They are establishing connection and after that server is sending 1MB text file in every second. Server and client code is below. I have tested long duration and saw that there is no data lost. I have modified the above answered little bit.
SERVER CODE:
package com.dd.server;
import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.concurrent.TimeUnit;
public class Server {
public static void main(String[] args) throws InterruptedException {
try {
byte[] done = new byte[3];
String str = "done"; //randomly anything
done = str.getBytes();
ServerSocket ss = new ServerSocket(5000);
Socket socket = ss.accept();
byte[] mybytearray = new byte[4096];
OutputStream os = socket.getOutputStream();
TimeUnit.SECONDS.sleep(5);
while(true) {
DataOutputStream dos = new DataOutputStream(os);
File myFile= new File("I:\\MY-LEARNINGS\\JAVA\\Workspace\\server\\src\\com\\dd\\server\\gistfile1.txt");
dos.writeUTF(myFile.getName());
dos.writeLong(myFile.length());
FileInputStream fis = new FileInputStream(myFile);
BufferedInputStream bis = new BufferedInputStream(fis);
DataInputStream dis = new DataInputStream(bis);
int read;
System.out.println("---------File Writing started----------");
int count = 0;
while((read = dis.read(mybytearray)) != -1){
dos.write(mybytearray, 0, read);
dos.flush();
++count;
System.out.println("Writing sub component of file. Step : "+count);
}
System.out.println("---------File Writing ended----------");
System.out.println("Flushing data DONE command sent.");
dis.close();
bis.close();
fis.close();
TimeUnit.SECONDS.sleep(1);
System.out.println("File transfer has been completed.");
dos.write(done, 0, 3);
dos.flush();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
CLIENT CODE:
package clientcom.dd.clent;
import java.io.DataInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
public class Client {
public static void main(String[] args) {
try {
Socket socket = new Socket("127.0.0.1", 5000);
InputStream in = socket.getInputStream();
DataInputStream dis = new DataInputStream(in);
while(true) {
FileOutputStream fos =
new FileOutputStream("I:\\MY-LEARNINGS\\JAVA\\Workspace\\client\\"+System.currentTimeMillis()+"-data.txt");
int read = 0;
byte[] mybytearray = new byte[4096];
while ((read = dis.read(mybytearray)) != -1 && read != 3) {
fos.write(mybytearray, 0, read);
fos.flush();
}
fos.close();
//System.out.println("The value of read : "+read);
// System.out.println("File has been received successfully.");
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
Ok, trying to transfer a specified directory of files over a socket, remove the directory objects from the arraylist, so only files are left, and transfer them 1 by 1 over the same socket. The arraylist here is filled with ONLY files, no directories. Heres the receive and send code for the client and server respectively . The code runs fine without errors, except for ALL the data is being written to the first file. The subsequent files are created in the server folder, but they are 0 bytes. Any input would be greatly appreciated.
THIS IS THE SERVER CODE FOR RECEIVING THE FILES
public void receive(){
try {
DataInputStream dis = new DataInputStream(new BufferedInputStream(socket.getInputStream()));
DataOutputStream dos = new DataOutputStream(new BufferedOutputStream(socket.getOutputStream()));
//read the number of files from the client
int number = dis.readInt();
ArrayList<File>files = new ArrayList<File>(number);
System.out.println("Number of Files to be received: " +number);
//read file names, add files to arraylist
for(int i = 0; i< number;i++){
File file = new File(dis.readUTF());
files.add(file);
}
int n = 0;
byte[]buf = new byte[4092];
//outer loop, executes one for each file
for(int i = 0; i < files.size();i++){
System.out.println("Receiving file: " + files.get(i).getName());
//create a new fileoutputstream for each new file
FileOutputStream fos = new FileOutputStream("C:\\users\\tom5\\desktop\\salestools\\" +files.get(i).getName());
//read file
while((n = dis.read(buf)) != -1){
fos.write(buf,0,n);
fos.flush();
}
fos.close();
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
THIS IS THE CLIENT CODE FOR SENDING THE FILES
public void send(ArrayList<File>files){
try {
DataInputStream dis = new DataInputStream(new BufferedInputStream(socket.getInputStream()));
DataOutputStream dos = new DataOutputStream(new BufferedOutputStream(socket.getOutputStream()));
System.out.println(files.size());
//write the number of files to the server
dos.writeInt(files.size());
dos.flush();
//write file names
for(int i = 0 ; i < files.size();i++){
dos.writeUTF(files.get(i).getName());
dos.flush();
}
//buffer for file writing, to declare inside or outside loop?
int n = 0;
byte[]buf = new byte[4092];
//outer loop, executes one for each file
for(int i =0; i < files.size(); i++){
System.out.println(files.get(i).getName());
//create new fileinputstream for each file
FileInputStream fis = new FileInputStream(files.get(i));
//write file to dos
while((n =fis.read(buf)) != -1){
dos.write(buf,0,n);
dos.flush();
}
//should i close the dataoutputstream here and make a new one each time?
}
//or is this good?
dos.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
You are reading the socket until read() returns -1. This is the end-of-stream condition (EOS). EOS happens when the peer closes the connection. Not when it finishes writing one file.
You need to send the file size ahead of each file. You're already doing a similar thing with the file count. Then make sure you read exactly that many bytes for that file:
String filename = dis.readUTF();
long fileSize = dis.readLong();
FileOutputStream fos = new FileOutputStream(filename);
while (fileSize > 0 && (n = dis.read(buf, 0, (int)Math.min(buf.length, fileSize))) != -1)
{
fos.write(buf,0,n);
fileSize -= n;
}
fos.close();
You can enclose all this in a loop that terminates when readUTF() throws EOFException. Conversely of course you have to call writeUTF(filename) and writeLong(filesize) at the sender, before sending the data.
I did it like this, it is working perfectly, you can take a look:
Send
byte[] done = new byte[3];
String str = "done"; //randomly anything
done = str.getBytes();
for (int i = 0; i < files.size(); i++) {
System.out.println(files.get(i).getName());
FileInputStream fis = new FileInputStream(files.get(i));
while ((n = fis.read(buf)) != -1) {
dos.write(buf, 0, n);
System.out.println(n);
dos.flush();
}
//should i close the dataoutputstream here and make a new one each time?
dos.write(done, 0, 3);
dos.flush();
}
//or is this good?
dos.close();
Receive
for (int i = 0; i < files.size(); i++) {
System.out.println("Receiving file: " + files.get(i).getName());
//create a new fileoutputstream for each new file
fos = new FileOutputStream("C:\\users\\tom5\\desktop\\salestools\\" + files.get(i).getName());
//read file
while ((n = dis.read(buf)) != -1 && n != 3) {
fos.write(buf, 0, n);
fos.flush();
}
fos.close();
}
I have created a server and client. They are establishing connection and after that server is sending 1MB text file in every second. Server and client code is below. I have tested long duration and saw that there is no data lost. I have modified the above answered little bit.
SERVER CODE:
package com.dd.server;
import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.concurrent.TimeUnit;
public class Server {
public static void main(String[] args) throws InterruptedException {
try {
byte[] done = new byte[3];
String str = "done"; //randomly anything
done = str.getBytes();
ServerSocket ss = new ServerSocket(5000);
Socket socket = ss.accept();
byte[] mybytearray = new byte[4096];
OutputStream os = socket.getOutputStream();
TimeUnit.SECONDS.sleep(5);
while(true) {
DataOutputStream dos = new DataOutputStream(os);
File myFile= new File("I:\\MY-LEARNINGS\\JAVA\\Workspace\\server\\src\\com\\dd\\server\\gistfile1.txt");
dos.writeUTF(myFile.getName());
dos.writeLong(myFile.length());
FileInputStream fis = new FileInputStream(myFile);
BufferedInputStream bis = new BufferedInputStream(fis);
DataInputStream dis = new DataInputStream(bis);
int read;
System.out.println("---------File Writing started----------");
int count = 0;
while((read = dis.read(mybytearray)) != -1){
dos.write(mybytearray, 0, read);
dos.flush();
++count;
System.out.println("Writing sub component of file. Step : "+count);
}
System.out.println("---------File Writing ended----------");
System.out.println("Flushing data DONE command sent.");
dis.close();
bis.close();
fis.close();
TimeUnit.SECONDS.sleep(1);
System.out.println("File transfer has been completed.");
dos.write(done, 0, 3);
dos.flush();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
CLIENT CODE:
package clientcom.dd.clent;
import java.io.DataInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
public class Client {
public static void main(String[] args) {
try {
Socket socket = new Socket("127.0.0.1", 5000);
InputStream in = socket.getInputStream();
DataInputStream dis = new DataInputStream(in);
while(true) {
FileOutputStream fos =
new FileOutputStream("I:\\MY-LEARNINGS\\JAVA\\Workspace\\client\\"+System.currentTimeMillis()+"-data.txt");
int read = 0;
byte[] mybytearray = new byte[4096];
while ((read = dis.read(mybytearray)) != -1 && read != 3) {
fos.write(mybytearray, 0, read);
fos.flush();
}
fos.close();
//System.out.println("The value of read : "+read);
// System.out.println("File has been received successfully.");
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
This question already has answers here:
Java sending and receiving file (byte[]) over sockets
(6 answers)
Closed 7 years ago.
I was experimenting with JAVA and found this question online.
Java sending and receiving file (byte[]) over sockets.
Just for curiosity i played with the code in the accepted answer, and with other code i found similar to the question. I tried the accepted answer,yes it works and is very fast. But the problem is Archive files are getting corrupted. So here is other code i tried. The downfall of my experimental code is it consume CPU cycles and takes more time than accepted answer (And i have no idea why it is happening so). So here is my code. Can somebody help me to optimize and improve this code more.
Time Taken by accepted Answer = 11ms for 4 Mb file.
Time taken by my experiment= 4 seconds for same file.
Server.java
public class Server implements Runnable {
private ServerSocket serverSocket = null;
private Socket socket = null;
private ObjectInputStream inStream = null;
public Server() {
}
#Override
public void run() {
try {
serverSocket = new ServerSocket(4445);
socket = serverSocket.accept();
DataInputStream dIn = new DataInputStream(socket.getInputStream());
OutputStream os = socket.getOutputStream();
DataOutputStream outToClient = new DataOutputStream(os);
System.out.println("Connected");
File myFile = new File("lib1.zip");
long flength = myFile.length();
System.out.println("File Length"+flength);
outToClient.writeLong(flength);
FileInputStream fis;
BufferedInputStream bis;
byte[] mybytearray = new byte[8192];
fis = new FileInputStream(myFile);
bis = new BufferedInputStream(fis);
int theByte = 0;
System.out.println("Sending " + myFile.getAbsolutePath() + "(" + myFile.length() + " bytes)");
while ((theByte = bis.read()) != -1) {
outToClient.write(theByte);
// bos.flush();
}
/*int count;
BufferedOutputStream bos= new BufferedOutputStream(os);
while ((count = bis.read(mybytearray))>0) {
bos.write(mybytearray, 0, count);
}*/
bis.close();
socket.close();
} catch (SocketException se) {
System.exit(0);
} catch (IOException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
Thread t = new Thread(new Server());
t.start();
}
}
ReceiveFile.java
public class RecieveFile {
public final static int SOCKET_PORT = 4445; // you may change this
String SERVER = "127.0.0.1"; // localhost
ArrayList<String> logmsg = new ArrayList<>();
public static void main(String[] args) {
new RecieveFile();
}
public RecieveFile() {
try (Socket sock = new Socket(SERVER, SOCKET_PORT)) {
System.out.println("Connecting...");
try (OutputStream os = sock.getOutputStream(); DataOutputStream outToServer = new DataOutputStream(os)) {
try (DataInputStream dIn = new DataInputStream(sock.getInputStream())) {
long fileLen, downData;
int bufferSize = sock.getReceiveBufferSize();
long starttime = System.currentTimeMillis();
File myFIle = new File("lib1.zip");
try (FileOutputStream fos = new FileOutputStream(myFIle); BufferedOutputStream bos = new BufferedOutputStream(fos)) {
fileLen = dIn.readLong();
/*for (long j = 0; j <= fileLen; j++) {
int tempint = is.read();
bos.write(tempint);
}*/
downData = fileLen;
int n = 0;
byte[] buf = new byte[8192];
while (fileLen > 0 && ((n = dIn.read(buf, 0, buf.length)) != -1)) {
bos.write(buf, 0, n);
fileLen -= n;
// System.out.println("Remaining "+fileLen);
}
/*while ((n = dIn.read(buf)) > 0) {
bos.write(buf, 0, n);
}*/
bos.flush();
long endtime = System.currentTimeMillis();
System.out.println("File " + myFIle.getAbsolutePath()
+ " downloaded (" + downData + " bytes read) in " + (endtime - starttime) + " ms");
}
}
}
} catch (IOException ex) {
Logger.getLogger(RecieveFile.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
You're copying a byte at a time. This is slow. You're also declaring a byte array but not using it. Try this:
int count;
byte[] buffer = new byte[8192]; // or more, double or quadruple it
while ((count = in.read(buffer)) > 0)
{
out.write(buffer, 0, count);
}
Your solution takes a lot of time probably because you are reading a character at time, instead of all the buffer.
The solution is to use a construct similar to the linked question; the problem you got about corrupted file is really improbable, a malformed TCP packed that pass CRC check is really rare occurrence, and I would blame a bug instead. try to post the code you used. But you can add some hash check on the file and some part of it, if you are concerned about this
Here is a cleaned up version of your code, it should perform faster as it avoids single byte operations:
public class Server implements Runnable {
#Override
public void run() {
try {
ServerSocket serverSocket = new ServerSocket(4445);
Socket socket = serverSocket.accept();
OutputStream os = socket.getOutputStream();
DataOutputStream dos = new DataOutputStream(os);
File myFile = new File("lib1.zip");
long flength = myFile.length();
dos.writeLong(flength);
InputStream fis = new FileInputStream(myFile);
byte[] buf = new byte[16*1024]; // 16K
long written = 0;
while ((count = fis.read(buf))>0) {
dos.write(buf, 0, count);
written+=count;
}
if (written != flength)
System.out.println("Warning: file changed");
dos.close();
socket.close();
} catch (IOException e) {
e.printStackTrace();
System.exit();
}
}
An possible improvement would be to use NIO with channel.sendTo() but this should already have an acceptable performance. Note you do not need to use buffered streams on reading or writing as you use a larger byte array buffer anyway.
One possible improvement would be to not use the DataOutputStream for the long but poke the 8 bytes of it into the first buffer (array) write.
BTW: writing 4MB in 11ms is 390MB/s, that would be faster than most desktop disks can read and write.
I have two problems with my java server-client file communication,
I have the CLIENT sends files over to the server and the SERVER receives the files.
My 2 issues are:
1) whenever i send a file, it is 8 bytes less (i do not know why)
2) the file transfer is not complete (with 8 bytes less) unless i close the socket, which i do not want. i want my connection to be persistent, so how can i send a EOF from the client to the server.
here is my client who sends files
public void sendFiles(String file) {
try {
File myFile = new File(file);
long length = myFile.length();
byte[] buffer = new byte[8192];
System.out.println(length);
FileInputStream in = new FileInputStream(myFile);
BufferedInputStream bis = new BufferedInputStream(in);
BufferedOutputStream outF = new BufferedOutputStream(sock.getOutputStream());
out.print("%SF%" + length + "$" + myFile.getName() + "#");
out.flush();
int count;
while ((count = in.read(buffer)) > 0) {
outF.write(buffer, 0, count);
}
outF.flush();
in.close();
bis.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
SERVER who receives files.
I'm passing the name and the length of the file but only using the name of the file. however, i don't know if i need to use the length of the file or not, in case of EOF or something. Please advice
Also, The code hangs in
while ((count = this.sock.getInputStream().read(buffer)) > 0) {
due to no EOF which i do not know how to implement
public void recvFile(String fileName, int length) {
try {
byte[] buffer = new byte[8192];
FileOutputStream outF = new FileOutputStream("/Users/Documents" +fileName);
BufferedOutputStream bos = new BufferedOutputStream(outF);
int count = length;
while ((count = this.sock.getInputStream().read(buffer)) > 0) {
bos.write(buffer, 0, count);
}
bos.close();
} catch (IOException ex) {
ex.printStackTrace();
}
}
UPDATE: I have removed the flush() as advised that it is not needed. Also, i have tested this code in a different class and it worked but it doesn't work here with client-server chat. Could anyone tell me why?
Any help or hints would be appreciated.
Thank you.
I would suggest to you send the file size first and/or properties of the file... You can try HTTP which is wide use for this task...
Another suggestion would be for you to open another connection on other TCP port just to send the file (this is actually how FTP sends files)
I suspect the problem you have is in code you haven't shown.
In this example you can send multiple messages or files over the same stream.
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.nio.channels.SocketChannel;
/**
* Created by peter on 1/25/15.
*/
public class DataSocket implements Closeable {
private final Socket socket;
private final DataOutputStream out;
private final DataInputStream in;
public DataSocket(Socket socket) throws IOException {
this.socket = socket;
this.out = new DataOutputStream(new BufferedOutputStream(socket.getOutputStream()));
this.in = new DataInputStream(new BufferedInputStream(socket.getInputStream()));
}
#Override
public void close() throws IOException {
out.flush();
socket.close();
}
// message format is length as UTF-8 encoded name, 32-bit int followed by data.
public void writeMessage(String description, byte[] bytes) throws IOException {
out.writeUTF(description);
out.writeInt(bytes.length);
out.write(bytes);
out.flush();
}
public byte[] readMessage(String[] description) throws IOException {
description[0] = in.readUTF();
int length = in.readInt();
byte[] bytes = new byte[length];
in.readFully(bytes);
return bytes;
}
public void writeFile(File file) throws IOException {
long length = file.length();
if (length > Integer.MAX_VALUE) throw new IllegalArgumentException("length=" + length);
out.writeUTF(file.toString());
out.writeInt((int) length);
byte[] buffer = new byte[(int) Math.min(length, 32 * 1024)];
try (FileInputStream fis = new FileInputStream(file)) {
for (int len; (len = fis.read(buffer)) > 0; ) {
out.write(buffer, 0, len);
}
}
out.flush();
}
public void readFile(File dir) throws IOException {
String fileName = in.readUTF();
int length = in.readInt();
byte[] buffer = new byte[(int) Math.min(length, 32 * 1024)];
try (FileOutputStream fos = new FileOutputStream(new File(dir, fileName))) {
while (length > 0) {
int len = in.read(buffer);
fos.write(buffer, 0, len);
length -= len;
}
}
}
// todo convert to a unit test
public static void main(String[] args) throws IOException {
// port 0 opens on a random free port.
ServerSocket sc = new ServerSocket(0);
DataSocket ds1 = new DataSocket(new Socket("localhost", sc.getLocalPort()));
DataSocket ds2 = new DataSocket(sc.accept());
sc.close();
// now ds1 and ds2 are connected.
File f = File.createTempFile("deleteme","");
f.deleteOnExit();
try (FileOutputStream fos = new FileOutputStream(f)) {
fos.write(new byte[10001]);
}
// send a request
ds1.writeMessage("Send me the file", new byte[0]);
String[] desc = { null };
byte[] data = ds2.readMessage(desc);
if (!desc[0].equals("Send me the file")) throw new AssertionError();
// return a response
ds2.writeFile(f);
f.delete();
if (f.exists()) throw new AssertionError();
ds1.readFile(new File(""));
if (f.length() != 10001) throw new AssertionError("length="+f.length());
ds1.close();
ds2.close();
System.out.println("Copied a "+f.length()+" file over TCP");
}
}
I want to send a file through a socket. The "server" is sitting on another computer than mine and the client in another computer as well. The file can go to and fro server and client but only in their current directory. The approach i have taken up to now is by using a file input stream and writing the file on a file output stream neverthelees this doesnt work as far as i understand.. Is there another way to send files through sockets?
Here is my code what could be wrong here?
public class Copy {
private ListDirectory dir;
private Socket socket;
public Copy(Socket socket, ListDirectory dir) {
this.dir = dir;
this.socket = socket;
}
public String getCopyPath(String file) throws Exception {
String path = dir.getCurrentPath();
path += "\\" + file;
return path;
}
public void copyFileToClient(String file, String destinationPath)
throws Exception {
byte[] receivedData = new byte[8192];
BufferedInputStream bis = new BufferedInputStream(
new FileInputStream(getCopyPath(file)));
String findDot = file;
String extension = "";
for (int i = 0; i < findDot.length(); i++) {
String dot = findDot.substring(i, i + 1);
if (dot.equals(".")) {
extension = findDot.substring(i + 1);
}
}
if (extension.equals("")) {
extension = "txt";
}
BufferedOutputStream bos = new BufferedOutputStream(
new FileOutputStream(new File(destinationPath + "\\"
+ "THECOPY" + "." + extension)));
int len;
while ((len = bis.read(receivedData)) > 0) {
bos.write(receivedData, 0, len);
}
// in.close();
bis.close();
// output.close();
bos.close();
}
// public static void main(String args[]) throws Exception {
// Copy copy = new Copy();
// System.out.print(copy.getCopyPath("a"));
// }
}
And some client code :
...
DataOutputStream outToServer = new DataOutputStream(
clientSocket.getOutputStream());
BufferedReader inFromServer = new BufferedReader(
new InputStreamReader(clientSocket.getInputStream()));
boolean exit = false;
else if (sentence.length() > 3 && sentence.substring(0, 3).equals("get")) {
String currPath = dir.getCurrentPath();
outToServer.writeBytes(sentence + "_" + currPath + "\n");
} else {
...
Your copyFileToClient method uses a FileInputStream and FileOutputStream directly, i.e. it does not transfer anything to/from the client at all, only from one local file to another. This is fine if you want to manage remotely files on the server, but does not help for sending data between different computers.
You have to somehow send the data through the OutputStream/InputStream of the Socket - i.e. use the FileInputStream at the sending side and the FileOutputStream at the receiving one.