Hola Lorenzo!
Al igual que hice en el otro hilo repito lo mismo, se trata de una aportación valiosa porque se practica con muchos conceptos interesantes. Con tu permiso voy a trasladar el código que has creado para que esté visible dentro del hilo del foro y las personas que lo vean puedan hacerse una idea sobre el código sin tener que descargar el fichero como adjunto.
Clase ServidorDeFicheros
import java.io.*;
import java.net.*;
public class ServidorDeFicheros
{
    public static final int PORT = 4444;
    public static void main (String args[]) {
        int identificador = 1;
        ServerSocket servidor = null;
        Socket cliente = null;
        try{ servidor = new ServerSocket(PORT);
        }catch(IOException e){System.out.println("Error al conectar con el servidor"); System.exit(-1);}
        /* Se bloquea mientras escucha */
        System.out.println("Servidor escuchando: " + servidor + " " + servidor.getInetAddress());
        while(true){
            try { cliente = servidor.accept(); } catch (IOException e){ System.out.println("Error al conectar con cliente " + identificador); }
            new HiloPeticionCliente(cliente, identificador).start();
            identificador++;
        }
    }
}
Clase HiloPeticionCliente
import java.io.*;
import java.net.*;
import java.nio.channels.FileChannel; import java.nio.channels.FileLock; import java.nio.channels.OverlappingFileLockException; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Paths; import java.nio.file.StandardOpenOption;
public class HiloPeticionCliente extends Thread {
    Socket cliente;
    int identificador;
    BufferedReader entrada = null; 
    //PrintWriter salida = null;
    OutputStream sendChannel = null;
    String cadena = ""; String comando = ""; String orden = "";  //Strings para recibir y gestionar la orden del usuario.
    boolean primerEspacio = false;
    //Variables para la gestión del bloqueo de archivo seleccionado
    boolean bloqueado = false;
    FileChannel channel; FileLock lock; File archivoBloqueado;
    RandomAccessFile accesoABloqueo;
    public HiloPeticionCliente (Socket cliente, int identificador){
        this.cliente = cliente; this.identificador = identificador; }
    public void run(){
        while(!cliente.isClosed()){
            leerOrdenCliente();
            System.out.println("Buscando archivo solicitado en comando: " + cadena);
            procesarOrdenCliente();
            switch(orden){  //coge la orden y la compara con las aceptadas por la máquina, el resto indica que no son correctas en el default mandando -1
                case "bye": System.out.println("Finalizada la conexion con el cliente " + identificador); 
                try{ cliente.close();} catch(IOException e){}; interrupt(); break;
                case "get": getArchivo(); break;
                case "lock": lockArchivo(); break;
                case "put": putArchivoModificado(); break;
                case "unlock":  unlockArchivo(); break;
                default: try { sendChannel.write(-1); } catch (Exception e){}  comando = ""; orden = "";
            }
        }
    }
    /*lee la entrada del Cliente y devuelve String */
    private String leerOrdenCliente() {  try{   
            //Establece canal de entrada
            entrada = new BufferedReader (new InputStreamReader(cliente.getInputStream()));
            //Establece canal envio archivos
            sendChannel = cliente.getOutputStream();
            //lectura entrada usuario
            cadena = entrada.readLine(); 
        } catch(IOException e ) { System.out.println(e.getMessage()); }
        return cadena;
    }
    /*Traduce la entrada del cliente dividiendo la orden del fichero a tratar*/
    private void procesarOrdenCliente() { //comprueba si el comando introducido es mayor a 3 letras (get, put, bye) y si es así guarda hasta el primer espacio
        if(cadena.length() > 3){ 
            for (int x = 0; x < cadena.length(); x++){
                //guarda la orden hasta el espacio, get lock o unlock en el String orden
                if(cadena.substring(x, x+1).equals(" ") && !primerEspacio){ orden = cadena.substring(0, x); primerEspacio = true; }
                // elimina espacios y comillas de la orden recibida y los guarda en comando
                if(!cadena.substring(x, x+1).equals(" ") && !cadena.substring(x, x+1).equals("\"")){ comando = comando + cadena.substring(x, x+1); }//final for 
            }
            primerEspacio = false; //al salir del bucle for , iniciamos el boolean primer espacio a false para la proxima entrada.
        } else orden = cadena;  }
    /*Busca el archivo de la orden get */
    private void getArchivo(){ //crea instancia de clase buscar archivo y invoca a la funcion buscador para encontrar el archivo solicitado
        if(!bloqueado){   //si no está bloqueado significa que es un get archivo normal
            if(comando.length() > 3){ //comando debe contener get y además el archivo a buscar, más de 3 caracteres.
                File archivoEncontrado = buscarArchivo();
                if(archivoEncontrado != null){
                    new HiloEnvio (cliente, archivoEncontrado).start();  } else { try { sendChannel.write(-1); } catch (Exception e){} }
                comando = ""; orden = ""; } else try { sendChannel.write(-1); } catch (Exception e){}
        }else { 
            try{ 
                Path archivoModificado = Paths.get("C:/Mantequilla.txt"); 
                try{
                    desbloquearCanal(); //desbloqueo del archivo bloqueado 
                    byte[] contenedor = Files.readAllBytes(archivoBloqueado.toPath());
                    Files.write(archivoModificado, contenedor, StandardOpenOption.WRITE, StandardOpenOption.TRUNCATE_EXISTING);  
                    try{ sleep(1000); }catch (InterruptedException e) { e.printStackTrace(); }
                    lock = channel.lock();
                }catch (IOException e){  }
            }catch(Exception e){};
            cambioRealizado();  }
    }           
    /*Put sobreescribir archivo con archivo modificado*/
    private void putArchivoModificado(){ 
        if (bloqueado){ 
            Path archivoModificado = Paths.get("C:/Mantequilla.txt"); 
            try{
                desbloquearCanal(); //desbloqueo del archivo bloqueado 
                byte[] arrayBytes = Files.readAllBytes(archivoModificado); //pasamos el contenido del archivo modificado a un array de bytes
                // Escritura en el archivoBloqueado
                Files.write(archivoBloqueado.toPath(), arrayBytes, StandardOpenOption.WRITE, StandardOpenOption.TRUNCATE_EXISTING);  
                try{ sleep(1000); }catch (InterruptedException e) { e.printStackTrace(); }
                lock = channel.lock();
            }catch (IOException e){  }
            cambioRealizado(); 
        } else { try { sendChannel.write(-1); } catch (Exception e){}  }
    } 
    /*Desbloquear archivo */
    private void unlockArchivo(){ if(bloqueado){try {
                try {  lock = channel.tryLock(); }catch (IOException ioe){}
            } catch (OverlappingFileLockException e) {  try { lock.release();   channel.close(); }catch (IOException ioe2){} }
            archivoBloqueado = null; bloqueado = false; System.out.println("Archivo desbloquado"); 
            cambioRealizado();
        }else try { sendChannel.write(-1); } catch (Exception e){}
    }
    /*Busqueda y bloqueo de archivo con comando LOCK*/
    private void lockArchivo(){ 
        if(!bloqueado){ BuscadorArchivo find = new BuscadorArchivo();
            archivoBloqueado = find.buscador(comando.substring(orden.length(), comando.length() ), new File("C:\\")); 
            if(archivoBloqueado != null) {
                try{   
                    accesoABloqueo = new RandomAccessFile(archivoBloqueado, "rws"); //aqui podemos acceder al archivo para leer y escribir en el
                    channel = accesoABloqueo.getChannel();
                    lock = channel.lock(); }catch (IOException e){}
                bloqueado = true; System.out.println("Archivo bloqueado."); cambioRealizado();  }
            else{ try { sendChannel.write(-1); } catch (Exception e){} }
        }
    }
    private File buscarArchivo(){
        BuscadorArchivo find = new BuscadorArchivo(); 
        return find.buscador(comando.substring(orden.length(), comando.length() ), new File("C:\\"));  }
    /*Orden Realizada correctamente*/
    private void cambioRealizado(){
        comando = ""; orden = "";
        String textoEnvio = "Done.";
        byte[] arrayBytes = textoEnvio.getBytes(); 
        try{ sendChannel.write(arrayBytes);  } catch (IOException e) { System.out.println(e); }
    }
    void desbloquearCanal(){ try{ lock.release(); }catch(IOException e ){} }
}
Clase HiloEnvio
import java.net.*;
import java.io.*;
/**
 * Clase HiloEnvio extiende de Thread y es la encargada de recibir el archivo encontrado y el canal con el cliente, que utiliza para enviar el archivo a destino
 * 
 * @author Lorenzo31 
 * @version (a version number or a date)
 */
public class HiloEnvio extends Thread
{
    private File archivo;
    private Socket socketCliente = null;
    private FileInputStream fileChannel = null;
    private BufferedInputStream lectorArchivo = null;
    private BufferedOutputStream sendChannel = null;
    public HiloEnvio(Socket socketCliente, File archivo){ this.socketCliente = socketCliente; this.archivo = archivo;} 
    public void run() {
        // send file
        try{
            byte [] mybytearray = new byte [(int)archivo.length()];
            fileChannel = new FileInputStream(archivo);
            lectorArchivo = new BufferedInputStream(fileChannel);
            lectorArchivo.read(mybytearray,0,mybytearray.length);
            sendChannel = new BufferedOutputStream(socketCliente.getOutputStream());
            System.out.println("Sending " + archivo + "(" + mybytearray.length + " bytes)");
            //envio archivo
            sendChannel.write(mybytearray,0,mybytearray.length);
            sendChannel.flush();
            System.out.println("Done.");
        }catch (IOException | NullPointerException e) { System.out.println("Interrumpido. ");
            interrupt(); 
        }
    }
}
Clase BuscadorArchivo
import java.io.*;
import java.net.*;
public class BuscadorArchivo {
    File archivoEncontrado = null; 
    boolean encontrado;
    public BuscadorArchivo()  { this.encontrado = false; /*siempre que se llama al buscador reinicia a false el boolean */ }
    public File buscador(String nombre, File raiz){
        if(!encontrado){ //si no esta encontrado entra en el for para buscar 
            File[] lista = raiz.listFiles();
            if(lista != null) {
                for(File elemento : lista) {
                    if (elemento.isDirectory())  {
                        buscador(nombre, elemento); 
                    } else if (nombre.equalsIgnoreCase(elemento.getName()))
                    { 
                        archivoEncontrado = elemento;
                        encontrado = true;   
                    }
                }
            } 
        } else { return archivoEncontrado; }   
        return archivoEncontrado;
    } //cierre buscador
}
Clase ClienteFicheros
import java.io.*;
import java.net.*;
import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; 
import java.nio.channels.FileChannel;
public class ClienteFicheros
{
    public static void main(String[] args) throws IOException {
        int bytesRead;
        BufferedOutputStream bos = null;
        Socket socketCliente = null;
        PrintWriter salida = null;
        String hostName = InetAddress.getLocalHost().getHostName();
        /* Creamos un socket en el lado cliente, enlazado con un servidor que está en la misma máquina
        que el cliente y que escucha en el puerto 4444 */ 
        try{ socketCliente = new Socket(hostName, 4444);
            System.out.println("servidor conectado:" + hostName);
            //Obtenemos el canal de salida
            salida = new PrintWriter( new BufferedWriter( new OutputStreamWriter(socketCliente.getOutputStream())),true);
        }catch(IOException e){
            System.err.println("No puede establecer conexion");
            System.exit(-1); }
        BufferedReader stdIn = new BufferedReader(new InputStreamReader(System.in));
        String linea = "";
        /*El programa cliente no analiza los mensajes enviados por el usuario, simplemente los
         * reenvia al servidor hasta que este se despide con Adios*/
        while(!linea.equalsIgnoreCase("bye")){   
            do{
                System.out.println("Introduce comando válido:");
                //Leo la entrada del usuario
                linea = stdIn.readLine();
                //parámetros permitidos en la introducción de datos del usuario: lock unlock get put get "archivo"
            }while (!linea.matches("[a-z][a-z][a-z] \".*\"") && !linea.matches("[a-z][a-z][a-z]") && !linea.matches("[l][o][c][k] \".*\"") 
            && !linea.matches("[u][n][l][o][c][k] \".*\""));
            //La envia al servidor
            salida.println(linea); 
            if(!linea.equalsIgnoreCase("bye")){
                OutputStream fos = null;
                try {
                    //reciibr archivo
                    //Creamos array de bytes
                    byte [] mybytearray  = new byte [1000000];
                    //Creamos objeto InputStream que abre la cadena de entrada para lectura del fichero que mande servidor
                    BufferedInputStream cadenaReceptor = new BufferedInputStream (socketCliente.getInputStream());
                    bytesRead = cadenaReceptor.read(mybytearray,0,mybytearray.length); 
                    //}
                    //Si al ejecutar la orden el servidor no reenvia ningun fichero ni un texto vacio, por error de sintaxis ,
                    //manda un "Done." y el valor de bytesRead es 0 para identificarlo
                    //BufferedReader comprobar = new BufferedReader( new InputStreamReader(cadenaReceptor));
                    switch(bytesRead){
                        case 1:   //1 valor que toma como mínimo un archivo Outputstream, significa archivo no encontrado o no buscado 
                        System.out.println("Archivo no encontrado o comando erroneo"); break;
                        case -1: //-1 valor que toma al no recibir ningun archivo erroneo ni correcto, solo con BYE
                        System.out.println("Sesion finalizada"); break; 
                        case 5:  //0 indica orden realizada correctamente
                        System.out.println("Done."); break; 
                        default: 
                        Path direccionDestino = (Paths.get("C:/Mantequilla.txt")).toAbsolutePath();
                        File file = new File(direccionDestino.toString());
                        fos = new FileOutputStream(file);  
                       
                        bos = new BufferedOutputStream(fos);
                        bos.write(mybytearray, 0 , bytesRead);
                        bos.flush();
                        fos.close();
                        System.out.println("File downloaded (" + bytesRead + " bytes read) at " + direccionDestino.toString() );  
                    } //end switch 
                }catch(IOException e){ System.out.println("Error en la transmisión.");
                    if (fos != null) fos.close();
                    if (bos != null) bos.close();
                    if (socketCliente != null) socketCliente.close(); }
            }
        }
        System.out.println("Sesión finalizada.");
        // if (fos != null) fos.close();
        if (bos != null) bos.close();
        if (socketCliente != null) socketCliente.close(); 
    }
}