locked
FileStream exception - illegal characters RRS feed

  • Question

  • I am attempting to create a client/server file transfer connection between two computers (or for testing purposes, my own computer).  The client will request a file from the server, and the server will send the file to the client.  After the server receives the file requested by the client, I am trying to open the file using FileStream to read the data from the file.  However, the FileStream constructor is throwing an ArgumentException claiming that the file name/path contains illegal characters.  Code below:

    main form excerpt:

    test_client.TCPClient ("127.0.0.1", "C:\\Test Song.mp3");



    client class excerpt:

    // create an instance of TCPClient
    TcpClient tcp_client = new TcpClient ();
    tcp_client.Connect (server_name, TCP_PORT);
       
    // create a new network stream for this client (only required for tcp)
    NetworkStream tcp_stream = tcp_client.GetStream ();

    if (tcp_stream.CanWrite) {
        Byte[] input_to_be_sent = System.Text.Encoding.UTF8.GetBytes (filename.ToCharArray ());
        tcp_stream.Write (input_to_be_sent, 0, input_to_be_sent.Length);
        tcp_stream.Flush ();
    }


    server class excerpt:

    // program blocks on accept until a client connects
    Socket so_tcp = tcp_listener.AcceptSocket ();
    NetworkStream tcp_stream = new NetworkStream (so_tcp);
    m_cc.Invoke (m_delegate, "Connected through TCP.\n");
    Byte[] received = new Byte[256];
    tcp_stream.Read (received, 0, received.Length);
    String filename = System.Text.Encoding.UTF8.GetString (received);
    StringBuilder sb_filename = new StringBuilder (filename);
    m_cc.Invoke (m_delegate, "Request for file: " + sb_filename.ToString () + "\n");
                       
    // start file transfer
    try{
        FileStream fs = new FileStream (sb_filename.ToString (), FileMode.Open, FileAccess.Read);
        BinaryReader reader = new BinaryReader (fs);
        Byte[] bytes = new Byte[1024];
        int read;
                           
        while ((read = reader.Read (bytes, 0, bytes.Length)) != 0) {
            tcp_stream.Write (bytes, 0, read);
        }
                           
        reader.Close ();
    } catch (ArgumentException ae) {
        m_cc.Invoke (m_delegate, "An argument exception has occurred: " + ae.ToString () + "\n");
    }


    Whenever the code reaches the line:  FileStream fs = new FileStream... the ArgumentException error is thrown.  I have tried formatting the request file all sorts of ways, but I think there may be a problem with extra byte characters existing in the filename once it is received by the server.  Can anyone help me with this problem?  It has definitely become a roadblock for me.
    Thursday, July 19, 2007 5:47 PM

Answers

  • You've got embedded zeros in your filename.  The debugger doesn't see them, Windows doesn't see them but .NET does.  That's one reason you don't get the line feed in your output.

    That problem started with your UTF8.GetString() call, you didn't specify how many bytes to convert.  Use the return value of tcp_stream.Read() and pass it to the GetString(byte[], int, int) overload.
    Monday, July 23, 2007 2:55 PM
  • Great.  Beware that your "received" buffer is too small.  Allocate 4 * 259 bytes to be 100% sure you can always receive the UTF8 encoded path.
    Monday, July 23, 2007 4:56 PM
  • UTF8 can use up to 4 octets to encode a Unicode codepoint.  Very rare, but possible.  Using two bytes would certainly be common.  Trip your assertion with

        String text = new String('©', 259);

    Check this web page for reference.

    Monday, July 23, 2007 6:30 PM

All replies

  • Looks to me like your client isn't sending the filename and the server is expecting the first characters (although it doesn't check so it may use *all* characters) to be the filename.  It looks like it's using the file's data (i.e. the first bytes in the file) as the filename.  If that's binary data, it will likely contain characters that are not valid in filenames.
    Thursday, July 19, 2007 6:42 PM
  • I am checking to make sure that the filename is, in fact, passed to the server.  If you take a look at the bit of server code I provided, you'll see:

    m_cc.Invoke (m_delegate, "Request for file: " + sb_filename.ToString () + "\n");

    That is outputting the name of the file to a textbox on the main form so that I could see that the filename is correct.  It correctly displays the name of the file that was passed from the main form (C:\Test Song.mp3).  Plus, the file isn't even open when the program thows the exception, so there's no way the program could be using the file's data as the filename.
    Thursday, July 19, 2007 8:25 PM
  • Please try using System.Text.Encoding.ASCII instead of System.Text.Encoding.UTF8.
    Monday, July 23, 2007 8:23 AM
  • I replaced UTF8 with ASCII in all areas of the project.  The error remains.  Here is the exact output of the ArgumentException, just in case it helps reveal anything:

    Request for file: C:\Test Song.mp3An argument exception has occurred: System.ArgumentException: Illegal characters in path.
       at System.IO.Path.CheckInvalidPathChars(String path)
       at System.IO.Path.GetFileName(String path)
       at System.IO.FileStream..ctor(String path, FileMode mode, FileAccess access)
       at OurMusic.SocketServer.StartListen() in C:\Users\J-Dubbs\Documents\Visual Studio 2005\Projects\OurMusic\OurMusic\SocketServer.cs:line 52


    One thing I can't figure out is why there is no line break after C:\Test Song.mp3.  I included "\n" in the string, but it doesn't appear to be doing anything.
    Monday, July 23, 2007 1:46 PM
  • You've got embedded zeros in your filename.  The debugger doesn't see them, Windows doesn't see them but .NET does.  That's one reason you don't get the line feed in your output.

    That problem started with your UTF8.GetString() call, you didn't specify how many bytes to convert.  Use the return value of tcp_stream.Read() and pass it to the GetString(byte[], int, int) overload.
    Monday, July 23, 2007 2:55 PM
  • thank you =)

    problem solved
    Monday, July 23, 2007 3:04 PM
  • Great.  Beware that your "received" buffer is too small.  Allocate 4 * 259 bytes to be 100% sure you can always receive the UTF8 encoded path.
    Monday, July 23, 2007 4:56 PM
  • 4 * 259?  Encoding.UTF8.GetBytes and Encoding.ASCII.GetBytes uses 8-bit characters.  If your input string is always less than 260 Unicode characters, 259 would suffice.  For example, the following causes no asserts:

    Code Snippet

      MemoryStream stream = new MemoryStream();
       String text = new String('a', 259);
       Byte[] bytes = System.Text.Encoding.UTF8.GetBytes(text.ToCharArray());
       Debug.Assert(bytes.Length == 259);
       stream.Write(bytes, 0, bytes.Length);
       Debug.Assert(stream.Length == 259);

     

    ...If you want to support NT Unicode filenames, then allocate 64k bytes and use Unicode not UTF8 or ASCII with Encoding.  .NET doesn't suport NT Unicode filenames but data going over a socket doesn't know the other side isn't .NET...

    Monday, July 23, 2007 6:10 PM
  • UTF8 can use up to 4 octets to encode a Unicode codepoint.  Very rare, but possible.  Using two bytes would certainly be common.  Trip your assertion with

        String text = new String('©', 259);

    Check this web page for reference.

    Monday, July 23, 2007 6:30 PM