1

I wrote some code for a server and a client to transfer a file from the server to the client and it worked like a charm; however I have few questions. I want to build this code under a GUI, and I want to list all the files on the folder, but how can I make the client choose the file he wants after he see the list of files offered (how can I send a string to the server in order to choose the file)?

Server Code

import java.io.*;
import java.net.*;



class TCPServer {

    public static void listfile(){

    File folder = new File("c:/");
    File[] listOfFiles = folder.listFiles();

    for (int i = 0; i < listOfFiles.length; i++) {
      if (listOfFiles[i].isFile()) {
        System.out.println("File " + listOfFiles[i].getName());
      } else if (listOfFiles[i].isDirectory()) {
        System.out.println("Directory " + listOfFiles[i].getName());
      }
    }
  }


    public static void main(String args[]) {

        listfile();

        while (true) {
            ServerSocket welcomeSocket = null;
            Socket connectionSocket = null;
            BufferedOutputStream outToClient = null;

            try {
                welcomeSocket = new ServerSocket(3248);
                connectionSocket = welcomeSocket.accept();
                outToClient = new BufferedOutputStream(connectionSocket.getOutputStream());
            } catch (IOException ex) {
                // Do exception handling
            }


            if (outToClient != null) {

                String FileName = "carexception.java";

                File myFile = new File("C:\\"+FileName);

                byte[] mybytearray = new byte[(int) myFile.length()];

                FileInputStream fis = null;

                try {
                    fis = new FileInputStream(myFile);
                } catch (FileNotFoundException ex) {
                    // Do exception handling
                }
                BufferedInputStream bis = new BufferedInputStream(fis);

                try {
                    bis.read(mybytearray, 0, mybytearray.length);
                    outToClient.write(mybytearray, 0, mybytearray.length);
                    outToClient.flush();
                    outToClient.close();
                    connectionSocket.close();

                    // File sent, exit the main method
                    return;
                } catch (IOException ex) {
                    // Do exception handling
                }
            }

        }
    }
}

Client Code

import java.io.*;
import java.net.*;
import java.util.*;

class TCPClient {

    public static void main(String args[]) {
        Scanner s = new Scanner(System.in);
        byte[] aByte = new byte[1];
        int bytesRead;

        Socket clientSocket = null;
        InputStream is = null;

        try {
            clientSocket = new Socket("127.0.0.1", 3248);
            is = clientSocket.getInputStream();
        } catch (IOException ex) {
            // Do exception handling
        }
        ByteArrayOutputStream baos = new ByteArrayOutputStream();

        if (is != null) {

            FileOutputStream fos = null;
            BufferedOutputStream bos = null;
            try {
                fos = new FileOutputStream("E:\\sss.java");
                bos = new BufferedOutputStream(fos);
                bytesRead = is.read(aByte, 0, aByte.length);

                do {
                        baos.write(aByte);
                        bytesRead = is.read(aByte);
                } while (bytesRead != -1);

                bos.write(baos.toByteArray());
                bos.flush();
                bos.close();
                clientSocket.close();
            } catch (IOException ex) {
                // Do exception handling
            }
        }
    }
}
syb0rg
  • 8,057
  • 9
  • 41
  • 81
  • btw, you should [reference your sources](http://stackoverflow.com/a/4687706/116249) if you simply copy someone elses code. – Patrick Jan 07 '13 at 17:49

4 Answers4

3

To accomplish what you're after you have to change quite a few things.

You can assume a specific protocol order, in the sense that the client needs to send a request to the server in order for the server to do anything, so the server is always in a listening state when the connection has been established.

You should,

  1. Introduce a loop of sending requests and receiving responses
  2. Figure out how to send a string object
  3. Break up the file sending part so you don't allocate larger byte arrays than the OS can hand you (consider a file being 4GB for instance, allocating a byte array for the whole file can be troublesome)

So, with this in mind we can get going. Regarding step 1, this can be accomplished using a while loop. If we assume that the server always listens for a request, the server "request loop" can look something like this.

ClientRequest request;
while (request.getType() != RequestType.Complete) {
    // receive new request
    // depending on type, send response
}

We simply added two classes here, one ClientRequest that encapsulates a message from the client, and an enum RequestType that defines the type of request the client is interested in, for instance a file list or file contents.

public enum RequestType {
   None, Complete, RequestFileList, RequestFileContent
}

public class ClientRequest {
   private RequestType type;
   public ClientRequest() {
       type = RequestType.None;
   }

   public RequestType getType() {
       return type;
   }
}

Now we need to attach this to the socket somehow, so we add a method for receiving a request, and assign that request to the current request instance.

ClientRequest request = new ClientRequest();
while (request.getType() != RequestType.Complete) {
    // receive new request
    receiveRequest(clientSocket.getInputStream(), request);
    if (request.getType() != RequestType.Complete) {
        // pick a response
    }
}

private void receiveRequest(DataInputStream socketStream, ClientRequest request) {
    // get a type of request
    byte type = socketStream.readByte();
    request.setType(RequestType.from(type));
    // get parameters for request, depending on type
    if (request.getType() == RequestType.RequestFileContent) {
        // receive file id (file name for instance, or some other id that you prefer)
        String argument = readString(socketStream);
        request.setArgument(argument);
    }
}

This adds a from method in RequestType, to convert a byte to a request, a setType method in ClientRequest, and a readString method. We also add a new field and corresponding get and set methods in ClientRequest.

public enum RequestType {
    // types as before
    ;
    public static RequestType from(byte b) {
        switch (b) {
            case 1: return RequestType.Complete;
            case 2: return RequestType.RequestFileList;
            case 3: return RequestType.RequestFileContent;
            default: return RequestType.None;
         }
    }
}

public class ClientRequest {
    private String argument;
    public void setType(RequestType value) {
        type = value;
    }

    public String getArgument() {
        return argument;
    }

    public void setArgument(String value) {
        this.argument = value;
    }
}

private String readString(DataInputStream socketStream) {
    int length = socketStream.readInt();
    byte[] stringBytes = new byte[length];
    socketStream.read(stringBytes);
    return new String(stringBytes, "UTF-8");
}

Now we get to the next step, responding to the request. Simply add a switch case and handle the type of request.

{
    // depending on type, send response
    handleRequest(clientSocket.getOutputStream(), request);
}

private void handleRequest(DataOutputStream socketStream, ClientRequest request) {
    switch (request.getType()) {
        case RequestType.RequestFileList: {
            String[] fileList = getFileList(getCurrentDirectory());
            // send response type
            socketStream.write(ResponseType.ResponseFileList.getByte());
            // send number of files
            socketStream.writeInt(fileList.length);
            // send each string
            for (String fileName : fileList) {
                sendString(socketStream, fileName);
            }
        }
        break;
        case RequestType.RequestFileContent: {
            // send response type ResponseType.ResponseFileContent
            // send length of file so other party can determine number of bytes to receive
            // send file contents in chunks of a fixed byte array length
            // send last part of file contents, if length of file is not evenly divided by array chunk size
        }
        break;
    }
}

the sendString method is simply a "reversed order" of the readString method.

private void sendString(DataOutputStream socketStream, String value) {
    int length = value.length();
    socketStream.writeInt(length);
    byte[] stringBytes = value.getBytes("UTF-8");
    socketStream.write(stringBytes);
}

The ResponseType is an enum of values similar to the ones in RequestType, so the client can handle the type of response that the server sends.

With these changes, you will be able to request a file list and present the response of files that the server sends. When the user picks a file to receive, the client can send a new request to the server and the server can send the appropriate file contents to the client.

The client application will have to define a similar ClientRequest class (perhaps with the name ServerResponse) with corresponding methods that the server specified for reading from and writing to the socket streams. This can be abstracted further by encapsulating the socket in a class, with a listener pattern for when a request or response is received that the GUI can subscribe to, ..although this goes beyond my example.

If you feel I need to clarify anything, please leave a comment and I'll do my best to answer.

Patrick
  • 17,669
  • 6
  • 70
  • 85
  • i lost you on the first step , can you try to add it to the original code because am lost were to place it all and i would be very thankfull .. – Raymond Bou Chaaya Jan 07 '13 at 22:46
  • What about the first step? Do you mean the loop code? You should add it after a connection has been established. – Patrick Jan 08 '13 at 14:03
1

How do you ask files? By name! I think that server accepts commands and responds with responses. You may use the format for your commands to server: CMD_NAME, arg1, arg2, ... Arguments can be binary, dependent on command. By CMD_NAME, your server will distinguish what you want from it (either accept the file or provide one).

You have a problem that you accept only one type of request. You need a command mechanism to ask different requests from server. Server needs to parse those requests rather than give a hardcoded answer immediately. This will make it flexible.

http://www.javamex.com/tutorials/networking/simple_server_s_side.shtml I am sure that there a tons of other examples like this. Java sockets are reliable, there cannot be problem. Just start learning basics, how to communicate different messages between client and server. But, your problem is not Sockets-related at all. Imagine that you communicate over files: read requests from one and write responses into another. Which messages do you write then? This is called a protocol. You need to design a simple one.

Val
  • 1
  • 8
  • 40
  • 64
0

Have you tried creating an array, so each file has it own index... When the client chooses the file he wants, then you return the file on certain array index.

~ btw you can serialize your array and send it to the client.

monkshorin
  • 21
  • 2
0

You can use ObjectOutputStream to send Strings or any other kind of Object via writeObject.

ldam
  • 4,412
  • 6
  • 45
  • 76