3

I'm doing some socket work in Java, and am trying to encode the data it is sending. That part is working correctly, but for some reason it doesn't send the whole encoded String over the socket. It seems to send it in parts of 3.

The client performs a simple readLine() from the MyBufferedReader class (which is below), and the server sends it like this:

    private void sendFile(File f, String dest){ //The file to be sent and the destination
        System.out.println("Sending file started..");

        try {
            this.out.println("CMD MKFILE " + dest + "/" + f.getName());
            //TODO send the file
        }catch(Exception e){
            e.printStackTrace();
        }
    }

The client receives this: CMD MKFILE C:\Users\Lolmewn\Documents\test/dir/n/linebrea and after another read k!.txt

The MyBufferedReader and MyPrintWriter classes look like this:

MyBufferedReader:

    @Override
    public String readLine() throws IOException {
        String read = super.readLine();
        System.out.println("UNDEC: " + read);
        try {
            System.out.println("DEC: " + decode(read));
            return decode(read);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return read;
    }

    public static String decode(String b) throws Exception {
        ByteArrayInputStream bais = new ByteArrayInputStream(b.getBytes("UTF-8"));
        InputStream b64is = MimeUtility.decode(bais, "base64");
        byte[] tmp = new byte[b.length()];
        int n = b64is.read(tmp);
        byte[] res = new byte[n];
        System.arraycopy(tmp, 0, res, 0, n);
        return new String(res);
     }

and MyPrintWriter:

    private String hash(String x) {
        try {
            return encode(x);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return x;
    }

    public static String encode(String b) throws Exception {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        OutputStream b64os = MimeUtility.encode(baos, "base64");
        b64os.write(b.getBytes("UTF-8"));
        b64os.close();
        return new String(baos.toByteArray());
     }

What's happening and how can I fix this?

Please do note: I am doing async work on these Sockets, meaning I can't just use a while(read != null) statement. That would cause other data not supposed to be there being there too.

Lolmewn
  • 1,491
  • 4
  • 17
  • 38

1 Answers1

1

First this aside: If you are sending from multiple threads/clients then you should open one socket for each of them (like accept() does), that way messages from different clients cannot interleave one another.

Now i am assuming only a single client per socket (reasonable):

I can't just use a while(read != null)

You have to implement a simple protocol with which you can distinguish one message from the next, e.g. use a 0-byte if that can never be part of the data or prefix the message with the length of the message and send both; that way your server can distinguish one message from the next.

Bernd Elkemann
  • 23,242
  • 4
  • 37
  • 66
  • I'm distinguishing them by adding, in the particular case, "CMD" in front. I could probably create more then one socket, but each one would have to log in again. Password is SHA-512 encryped, and I'm sure there's a safe way to store it, but I don't like the idea of a password hanging around somewhere very much. – Lolmewn Feb 13 '12 at 12:52
  • I dont see why there would be another log-in. You dont need to create another listening socket on the server, just call ServerSocket's accept() method to effectively duplicate the socket. – Bernd Elkemann Feb 13 '12 at 12:59
  • The very first thing it does when a new socket gets created using accept() is checking the login credentials. This, of course, to keep things safe. – Lolmewn Feb 13 '12 at 13:02
  • 1
    I dont see your code, however i am guessing that you are misinterpreting what Sha does: It is not encryption. If you are hashing the password on the client and then send the Sha to the server you are inviting replay-attacks, if you are hashing the password on the server then you are sending user's password as plaintext. Typically you only use Sha for message-integrity and to avoid **storing** passwords as plaintext on the server. – Bernd Elkemann Feb 13 '12 at 13:15
  • Oh, I see. I thought, since it was a one-way encryption only, the password couldn't be retained, and therefore not really easily be used. How would I send it safely? – Lolmewn Feb 13 '12 at 13:19
  • 1
    To transmit anything safely you can use ssl, i will post 2 links in the following comment. Once you have an ssl connection you can just transmit the password, the server can then hash the password and compare the hash with the stored hash (e.g. stored in a DB). – Bernd Elkemann Feb 13 '12 at 13:56
  • 1
    http://stilius.net/java/java_ssl.php . . . http://docs.oracle.com/javase/6/docs/technotes/guides/security/jsse/JSSERefGuide.html – Bernd Elkemann Feb 13 '12 at 13:56
  • Thanks, I'll give it a go. I will also try to create multiple sockets. However, how can I avoid having to re-log-in every time I create a new socket instance? – Lolmewn Feb 13 '12 at 14:22
  • You are only logging in once per thread/client, it is no problem. – Bernd Elkemann Feb 13 '12 at 14:37