javasocketsclient-serverlanwan

Sending file over WAN stuck (Java Socket)


I got a really weird problem by sending files over Internet with java Socket. I have a Java server that works pretty fine in LAN, it communicates and transfer files. The problem is in WAN: when I ran the Server on a remote PC, the Client can communicate with Server, but he'll stuck at 0% when it tries to sends file to the Server. It usually happens with large files (>= of 100 MB), but sometimes happens with small files too.

Please someone help me :), Thank you.

Server Receiving code:

public void ReceiveFile(int fileSize, Socket sock, String fileName, String cmrId, PrintWriter pw){
    folderCheck(cmrId);
    FileOutputStream fos= null;
    BufferedOutputStream bos= null;
    try {
        int ret;
        int bytesRead=0;
        fos= new FileOutputStream(cmrId+"/"+fileName);  //receive file to User Dedicated folder
        bos= new BufferedOutputStream(fos);
        //InputStream input= sock.getInputStream();
        byte[] bytesArray= new byte[fileSize];
        DataInputStream dis= new DataInputStream(sock.getInputStream());

        ret= dis.read(bytesArray, 0, bytesArray.length);
        bytesRead= ret;
        //System.out.println("CmrFoldMan -- Received " + bytesRead + " of " + fileSize);  //debug
        while(bytesRead<fileSize){
            ret= dis.read(bytesArray, bytesRead, (bytesArray.length-bytesRead));
            if(ret>=0) bytesRead+=ret;
            //System.out.println("CmrFoldMan -- Received " + bytesRead + " of " + fileSize);  //debug
        }
        bos.write(bytesArray, 0, bytesRead);
        bos.flush();

        upHist= new UpdateHistory(fileName, fileSize, cmrId);
        upHist.update();

        daysLimit.deleteFilesLimit(fileSize, cmrId);  //delete files that exceed memory limit


    } catch (IOException ex) {
        Logger.getLogger(CmrFolderManager.class.getName()).log(Level.SEVERE, null, ex);
    }
    finally{
        try {
            fos.close();
            bos.close();
        } catch (IOException ex) {
            Logger.getLogger(CmrFolderManager.class.getName()).log(Level.SEVERE, null, ex);
        }
    }
}

Client Sending Code:

public long upload(String fileToSend){
    long uploadTimerStart = System.currentTimeMillis();  //start timer
    if(contactServerCheckError()) return -1;
    try{
        pw.println(fileSize);
        pw.println(fileName);
        Socket sendSock= new Socket(ip, filePort);   //connecting to sending file port
        DataOutputStream dos= new DataOutputStream(sendSock.getOutputStream());
        File file= new File(fileToSend);
        int arraySize= (int)file.length();  //used for println only
        byte[] array= new byte[1024];  //array is 1024 to use progress bar
        fis= new FileInputStream(file);
        bis= new BufferedInputStream(fis);
        int len;
        int tmpBytes=0;
        while((len= bis.read(array))>0){
            //System.out.println("SendFile " + tmpBytes + " bytes " + "of " + arraySize);  //debug
            dos.write(array, 0, len);
            dos.flush();
            tmpBytes+=len;
            updateProgressBars(tmpBytes);
            updateLabelsPercentage(tmpBytes);
        }

    } catch (FileNotFoundException ex) {
        Logger.getLogger(SendFile.class.getName()).log(Level.SEVERE, null, ex);
        return -1;
    } catch (IOException ex) {
        Logger.getLogger(SendFile.class.getName()).log(Level.SEVERE, null, ex);
        return -1;
    }
    finally{
        try{
        if(bis!=null) bis.close();
        if(os!=null) os.close();
        //if(sock!=null) sock.close();
        } catch (IOException ex) {
            Logger.getLogger(SendFile.class.getName()).log(Level.SEVERE, null, ex);
            JOptionPane.showMessageDialog(null, "ERROR " + ex);
            return -1;
        }
    }

    long uploadTimerEnd = System.currentTimeMillis();  //end timer
    long uploadTimerDelta= uploadTimerEnd - uploadTimerStart;
    return uploadTimerDelta;
}

Solution

  • Well... for starters... as far as I can tell, in the receiving code you are creating a byte array that is the size of the destination file,

    byte[] bytesArray= new byte[fileSize];
    

    and you keep reading from the input stream, into the byte array, until it is full,

    while(bytesRead<fileSize){
            ret= dis.read(bytesArray, bytesRead, (bytesArray.length-bytesRead));
    

    then you write it in one go to the file

    bos.write(bytesArray, 0, bytesRead);
    

    For a 100MB file, as you describe, this means that you hold 100MB in memory. This is not... a great idea.

    fos= new FileOutputStream(cmrId+"/"+fileName);
    InputStream is = sock.getInputStream());
    
    int read = 0;
    byte[] buf = new byte[1024];
    while( (read = is.read(buf)) != -1) {
        fos.write(buf, 0, read);
    }
    

    The code above ditches the DataInputStream you are using (which adds nothing as far as I can tell) and reads up to 1024 bytes at a time and writes it in chunks to the FileOutputStream without ever holding more than a kilobyte in memory. Give that a try and see if it is more reliable.