Link to home
Start Free TrialLog in
Avatar of MarvynDT
MarvynDT

asked on

Socket Programming

I have a client and server. The client has to read a file in blocks and send it over as the block is filled. The server reads in the blocks and writes out the data to a file. There is no assumption that the data is ASCII text or contains any newlines.

A part of my code is for Server.java is:
DataInputStream input = new DataInputStream(mySocket.getInputStream());
File file = new File("whatever");
byte[] filebyte = new byte[16];
FileOutputStream fileOS = new FileOutputStream(file);
int length;

while((length = input.read(filebyte)) != -1)
{
    fileOS.write(filebyte);
}
fileOS.close();
input.close();

Part of the code from Client.java is:
byte[] buf = new byte[16];
File file = new File(filename);
FileInputStream fileIS = new FileInputStream(file);
while(fileIS.read(buf) != -1)
{
    output.write(buf);
}
output.writeByte(0);

I am getting a bad file number error on the server side. I'm not sure where to go from here.

-Marvyn
Avatar of Mick Barry
Mick Barry
Flag of Australia image

You are ignoring the number of bytes that are actually read on both the client and server sides, and instead writing the whole buffer array regardless.

Avatar of functionpointer
functionpointer

all of your read/write loops should look a bit like this:
int len = 0;
while ( ( len = is.read( buf ) > -1 )
{
  os.write( buf, 0, len );
}

otherwise you are writing a fistful of nulls to the file. 15:16, that is. you will occasionally get lucky. :-)
Avatar of MarvynDT

ASKER

Does it matter in this context, considering that both buffer sizes are equal?

I tried adjusting on both, i.e. fileIS.write(buf, offset, length), etc.., but it produced the same error:
java.net.SocketException: socket closed: Bad file number

I was thinking that because both java programs are executed within the same folder, it cannot read and write to the same file at the same time. But I changed the file name in the server program and had the same error produced.
As I had already stated :)

> otherwise you are writing a fistful of nulls to the file.

Not necesarily nulls (assuming you mean 0 when you say null).

from the javadoc for read():

"Reads some number of bytes from the input stream and stores them into the buffer array b. The number of bytes actually read is returned as an integer. "
Your code should look more like this:


A part of my code is for Server.java is:
DataInputStream input = new DataInputStream(mySocket.getInputStream());
File file = new File("whatever");
byte[] filebyte = new byte[16];
FileOutputStream fileOS = new FileOutputStream(file);
int length;

while((length = input.read(filebyte)) != -1)
{
   fileOS.write(filebyte, 0, length);
}
fileOS.close();
input.close();

Part of the code from Client.java is:
byte[] buf = new byte[16];
int length;
File file = new File(filename);
FileInputStream fileIS = new FileInputStream(file);
while((length = fileIS.read(buf)) != -1)
{
   output.write(buf, 0, length);
}

//output.writeByte(0); <- I'm not sure why you do this
functionpointer posted right before I got to read it, and it now makes sense. Taking that advice into my code still produces the bad file number error.

I took the code snippet and adjusted it to my own code. I am still running into the same error. The client is now producing an error: java.io.IOException: Broken Pipe.
And the Server still has the same error.

I'm trying to move at .77KB file across the socket with a 16 byte buffer. Shouldn't I be getting "lucky" about 48 times. I adjusted the client so that it shows the number of bytes read into the buffer are full at 16. It loops throught this twice. Server never loops. It has a problem when it hits the instruction:
while((len=input.read(filebyte)) != -1)

Thanks object. I just compared my code to the modified code that you submitted. But its still producing the errors I listed in my last comment.
better post your complete current code that handles the transfer on both end, everything from when you open the streams to when you close them.

There doesn't seem to be any reason for you to use a DataInputStream though it shouldn't create a problem in the way you use it.

//code for the client:
import java.io.*;
import java.net.*;
import java.lang.*;

public class Put
{
    static int port;
    static String host;
    static String filename;

    public static void main(String[] args)
    {
        String usage = "usage: java Put [-h hostname] [-p portname] filename";

        if(args.length == 0)
        {
            System.err.println(usage);
            System.exit(1);
        }
        else if(args.length == 1
                && !(args[0].equalsIgnoreCase("-h")
                || args[0].equalsIgnoreCase("-p")))
        {
            //check if filename is ASCII compliant
            if(!(isprint(args[0])))
            {
                System.err.println("error in filename: " + args[0]);
                System.exit(1);
            }
            filename = args[0];
            port = 43210;
            host = "localhost";
        }
        else
        {
            int portfoundflag = 0;
            int hostfoundflag = 0;
            for(int argsindex = 0; argsindex < args.length; argsindex++)
            {
                if(args[argsindex].equalsIgnoreCase("-P") && portfoundflag == 0)
                {
                    // -p port
                    portfoundflag = 1;      //set port to found
                    if(++argsindex >= args.length || (argsindex + 1) >= args.length)
                    {
                        System.err.println(usage);
                        System.exit(1);
                    }
                    //check if port number is valid
                    if((port = isportvalid(args[argsindex])) == -1)
                    {
                        System.err.println("port range out of bounds: " + args[1]);
                        System.err.println("range: {1024-65535}");
                        System.exit(1);
                    }
                }
                else if(args[argsindex].equalsIgnoreCase("-H") && hostfoundflag ==0)
                {
                    // -h host
                    hostfoundflag = 1;
                    if(++argsindex >= args.length || (argsindex +1) >= args.length
                        || args[argsindex].equalsIgnoreCase("-P"))
                    {
                        System.err.println(usage);
                        System.exit(1);
                    }
                    host = args[argsindex];
                }
                else if((argsindex + 1) == args.length)
                {
                    // filename
                    //check if filename is ASCII compliant
                    if(!(isprint(args[argsindex])))
                    {
                        System.err.println("error in filename: " + args[argsindex]);
                        System.exit(1);
                    }
                    filename = args[argsindex];
                }
                else
                {
                    System.err.println(usage);
                    System.exit(1);
                }
            }
        }
        senddata();
    }
 
    // send put command to server
    static void senddata()
    {
        try
        {
            // establish connection
            Socket s = new Socket(host, port);
            System.out.println("connecting to " + host + " on " + port);          
 
            DataOutputStream output;
            DataInputStream input;
            byte bytein;
            int resultreceived = 0;
            byte[] buf = new byte[16];
            byte[] filebyte = new byte[50];
            int index = 0;
 
            try
            {
                output = new DataOutputStream(s.getOutputStream());
                output.writeBytes("P");            //instruction
                output.writeBytes(filename);      //filename
                output.writeByte(0);            //eof

                //get confirmation from server
                input = new DataInputStream(s.getInputStream());
                while((bytein = input.readByte()) != 0)
                {
                    if(!(resultreceived == 0))
                    {
                        filebyte[index++] = bytein;
                    }
                    if(bytein == 89 || bytein == 121)
                    {
                        //filename accepted
                        resultreceived = 1;      //answer found
                    }
                    else if(bytein == 78 || bytein == 110)
                    {
                        //filename not accepted
                        System.out.println("server error: " + new String(filebyte));
                        System.exit(1);
                    }
                }

                // read file and send over socket to server
                File file = new File(filename);
                FileInputStream fileIS = new FileInputStream(file);
                int len = 0;
                while((len=fileIS.read(buf)) != -1)
                {
                    System.out.println(len);
                    System.out.println(""+fileIS.available());
                    output.write(buf,0,len);
                }
 
            }
            catch(IOException e)
            {
                System.out.println(e);
            }
        }
        catch(UnknownHostException e)
        {
            System.err.println("unknown host: " + host);
            System.exit(1);
        }
        catch(IOException e)
        {
            System.out.println(e.getMessage());
        }
    }

    // returns true if all chars in string are ASCII compliant
    public static boolean isprint(String s)
    {
        for(int k = 0; k < s.length(); k++)
        {
            char currentchar = s.charAt(k);
            int i = (int)currentchar;
            if(i > 128)
            {
                return false;
            }
        }
        return true;
    }

    // returns -1 if port number is invalid, else returns port number
    public static int isportvalid(String s)
    {
        int port = -1;
        try
        {
            port = (new Integer(s)).intValue();
            if(port > 65535 || port < 1024)
            {
                return -1;
            }
        }
        catch (NumberFormatException e)
        {
            System.err.println("bad number format: " + s);
            System.err.println("usage: java Put [-h hostname] [-p portname] filename");
            System.exit(1);
        }
        return port;
    }
}
//code for the server:

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

public class Server
{
    static int port;      //server port

    public static void main(String[] args)
    {
        String usage = "usage: java Server [-p portnumber]\n";

        if(args.length == 0)
        {
            port = 43210;      //hard-wired port number
        }
        else if(args.length == 2 && args[0].equals("-p"))
        {
            try
            {
                port = (new Integer(args[1])).intValue();
                if(port > 65535 || port < 1024)
                {
                    System.err.println("port range out of bounds: " + port);
                    System.err.println("range: {1024-65535}");
                    System.exit(1);
                }
            }
            catch (NumberFormatException e)
            {
                System.err.println("bad number format: " + args[1]);
                System.err.println(usage);
                System.exit(1);
            }
        }
        else
        {
            System.err.println(usage);
            System.exit(1);
        }
        serve();
    }

    // create socket and wait for connections
    public static void serve()
    {
        try
        {
            DataInputStream input;
            DataOutputStream output;
            String filename;
            String command;

            ServerSocket ss = new ServerSocket(port, 5);

            while(true)
            {
                System.out.println("waiting for connection on port " + port);
                Socket s = ss.accept();      //wait instruction
                System.out.println("got a connection from "
                    + s.getInetAddress().getHostAddress()
                    + ", port: " + s.getPort());
                try
                {
                    command = "invalid";      //reset command
                    input = new DataInputStream(s.getInputStream());
                    byte bytein;
                    byte[] filebyte = new byte[16];
                    int index = 0;
                    while((bytein=input.readByte()) != 0)
                    {
                        if(!(command.equals("invalid")))
                        {
                            filebyte[index++] = bytein;
                        }
                        else if(bytein == 80 || bytein == 112)
                        {
                            //put instruction
                            command = "put";
                        }
                        else if(bytein == 71 || bytein == 103)
                        {
                            //get instruction;
                            command = "get";
                        }
                    }
                    filename = new String(filebyte);

                    //check that filename is legit
                    if(isprint(filename))
                    {
                        if(command.equals("put"))
                        {
                            //create new file
                            File file = new File("NEW" + filename);
                            file.createNewFile();
                            sendmessage(s, "Y");
           
FileOutputStream fileOS = new FileOutputStream(file);

int len = 0;
System.out.println("before loop");
while((len=input.read(filebyte)) != -1)
{
    System.out.println(len);
    fileOS.write(filebyte,0,len);
}
fileOS.close();
input.close();

                        }
                        else if(command.equals("get"))
                        {
                            //check that file exists
                            File file = new File(filename);
                            if(file.exists())
                            {
                                sendmessage(s, "Y");
                                sendfile(s, filename);
                            }
                            else
                            {
                                sendmessage(s, "N", "File does not exist");
                            }
                        }
                    }
                    else
                    {
                        sendmessage(s, "N", "Filename not acceptable");
                    }
                }
                catch(IOException e)
                {
                    System.out.println(e);
                }
            }
        }
        catch (java.io.IOException e)
        {
            System.out.println(e.getMessage());
        }
    }

    // returns true if all chars in string are ASCII compliant
    public static boolean isprint(String s)
    {
        for(int k = 0; k < s.length(); k++)
        {
            char currentchar = s.charAt(k);
            int i = (int)currentchar;
            if(i > 128 || i == 47)
            {
                return false;
            }
        }
        return true;
    }

    // send to client, answer with message
    public static void sendmessage(Socket s, String result, String message)
    {
        try
        {
            DataOutputStream output = new DataOutputStream(s.getOutputStream());
            output.writeBytes(result);      //request
            output.writeBytes(message);      //optional message
            output.writeByte(0);            //eof
            output.close();
        }
        catch(IOException e)
        {
            System.out.println(e);
        }
    }
 
    // send to client, answer w/o message
    public static void sendmessage(Socket s, String result)
    {
        try
        {
            DataOutputStream output = new DataOutputStream(s.getOutputStream());
            output.writeBytes(result);      //request
            output.writeByte(0);            //eof
            output.close();
        }
        catch(IOException e)
        {
            System.out.println(e);
        }
    }

    // get file from client on Socket s
    public static void retrievefile(Socket s, String filename)
    {
        byte[] buf = new byte[16];
        int length;

        try
        {
            DataInputStream input = new DataInputStream(s.getInputStream());
            File file = new File(filename);
            FileOutputStream fileOS = new FileOutputStream(file);

            while((length = input.read(buf)) != -1)
            {
                fileOS.write(buf);
            }
            fileOS.close();
            input.close();
        }  
        catch(IOException e)
        {
            System.out.println(e);
        }
    }

    // get file and send to client on Socket s
    public static void sendfile(Socket s, String filename)
    {
    }
}
>> Shouldn't I be getting "lucky" about 48 times?

Shouldn't we all...

what i meant was: unless your reading really slow, the only place you will have less than 16 bytes read into the buffer is on the very last read.  hence, the 1 out of every 16 times the amount will be correct.

>> Server never loops.

hmmm. your file.write() is in that block. try writing each byte to a ByteArrayInputStream instead
while ( ( b = is.read() ) != -1 ) baos.write( b );
dump this to check it out, then write the result to the file later. btw, what is the timeout on the socket?
sorry, was writing while you were posting...

from what you've posted, is seems your sending some ascii on every request/response. HTTP has a decent model of this. It uses CRLF as a header delimiter and CRLF CRLF to delimit data. If you use a model like this, you could use a BufferedReader to strip off ascii header data, then read from the socket's input stream to get the data ( if the header said so ). The nice thing about this is you can know how much data to read by some header like "Content-length: 2435".  Once the header is past, CRLF CRLF, use a BufferedInputStream to read the bytes.
can u also post the debug messages that are displayed when u run it.
You are closing the stream in sendMessage on the server, resulting in the socket getting closed.
Try removing the close().
remus% java Server
waiting for connection on port 43210
got a connection from 127.0.0.1, port: 59152
before loop
java.net.SocketException: socket closed: Bad file number
waiting for connection on port 43210

--------------------

remus% java Put note.txt
connecting to localhost on 43210
16
777
16
761
java.io.IOException: Broken pipe
remus%
objects, would it even matter considering the scope of both objects? Regardless, tried it and still no go...
functionpointer, as stated in the guidelines by my professor, I cannot assume that the data transferred is ASCII or contains any delimiters. That made me decide to stay away from BufferedReader as I have no idea what the server may have to read. Or is my understanding of BufferedReader incorrect?
> would it even matter considering the scope of both objects?

Yes. Closing the stream will close the underlying socket.
Scope has nothing to do with it.

> Regardless, tried it and still no go...

Whats the error now?
Check you aren't prematurely closing the socket elsewhere.

> Or is my understanding of BufferedReader incorrect?

No you aren't. It is for reading text data.


did you remove the close() call from both sendMessage() methods.
Yes, I commented out the close instructions from both sendMessage() methods. I am still producing the same errors.
ASKER CERTIFIED SOLUTION
Avatar of Mick Barry
Mick Barry
Flag of Australia image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
your retrievefile method in the code you posted is still doing some funky writing.
and if u close the output stream on the client after the file is sent it all works fine.
> your retrievefile method in the code you posted is still doing some funky writing.

That method never gets called.
I just tried it again and it works. VERY WEIRD, since I had to stop the server program everytime to recompile it. Well whatever...

Thank you objects & functionpointer
I'm not sure which comment from objects to accept as an answer, considering that there were quite a few fixes to the program and the one that solved it all was just executing the code again. So I'll accept that particular comment as the answer.

Although the solution to my particular problem may have been shuttind down the socket prematurely or writing nulls to the file. (In case anybody with similar problems is reading this)
Thanks for sticking around to see this through.
> I just tried it again and it works.

woo hoo :-)
> Thanks for sticking around to see this through.

No worries, thanks for the points :)