java - Empty Socket InputStream

23
2014-04
  • David Kirby

    I'm trying to build a simple web server in Java as practice but I'm running into a very strange issue. When I attempt to read the InputStream from the Socket, sometimes there is no data to be read. The process I'm following is this:

    I create a ServerSocket, listening on port 80, and call accept() to get a Socket. I then send a request from my browser (Firefox) to localhost, which triggers the accept() to return the Socket.

    Sometimes, it will read the HTTP request perfectly. Other times, it fails to read any data (read() returns a -1).

    Here is some sample code to illustrate what I'm doing, without any exception handling thrown in:

    ServerSocket serv = new ServerSocket(80);
    
    while (true)
    {
      Socket con = ServerSocket.accept();
    
      InputStream input = con.getInputStream();
    
      bytes[] bytes = new bytes[4000000]; // for simplicity, I figured I'd 
                                          // just make the array huge for now
    
      int bytesRead = input.read(bytes);
      if (bytesRead > 0)
      {
        StringBuffer sBuffer = new StringBuffer(bytesRead);
    
        for (int i = 0; i < bytesRead; i++)
        {
          sBuffer.append((char) bytes[i]);
        }
    
        System.out.println(sBuffer.toString());
      }
    }
    

    EDIT: I've also tried using a BufferedInputStream and BufferedReader to not avail.

  • Answers
  • Jochem Gruter

    One socket can receive more packets. So you should create a new thread for every socket. Something like:

    while(true){
        Socket socket = serv.accept();
        Client client = new Client(socket);
        new Thread(client).start();
    }
    

    Then you need to create the Client class which implements the runable interface.

    Socket socket;
    public Client(Socket socket){
        this.socket = socket;
    }
    public void run(){
        while(socket.isConnected()){
            //here reads the packets from the inputstream
        }
    }
    
  • jtahlborn

    You should always loop when reading data from an InputStream. there is no guarantee that all the data will be returned from a single read() call (loop until you receive -1).


  • Related Question

    java - Read/convert an InputStream to a String
  • Johnny Maelstrom

    If you have java.io.InputStream object, how should you process that object and produce a String?


    Suppose I have an InputStream that contains text data, and I want to convert this to a String (for example, so I can write the contents of the stream to a log file).

    What is the easiest way to take the InputStream and convert it to a String?

    public String convertStreamToString(InputStream is) { 
        // ???
    }
    

  • Related Answers
  • Chuk Diesel

    A nice way to do this is using Apache commons IOUtils to copy the InputStream into a StringWriter... something like

    StringWriter writer = new StringWriter();
    IOUtils.copy(inputStream, writer, encoding);
    String theString = writer.toString();
    

    Alternatively, you could use ByteArrayOutputStream if you don't want to mix your Streams and Writers

  • Pavel Repin

    Here's a way using only standard Java library (note that the stream is not closed, YMMV).

    static String convertStreamToString(java.io.InputStream is) {
        java.util.Scanner s = new java.util.Scanner(is).useDelimiter("\\A");
        return s.hasNext() ? s.next() : "";
    }
    

    I learned this trick from "Stupid Scanner tricks" article. The reason it works is because Scanner iterates over tokens in the stream, and in this case we separate tokens using "beginning of the input boundary" (\A) thus giving us only one token for the entire contents of the stream.

    Note, if you need to be specific about the input stream's encoding, you can provide the second argument to Scanner constructor that indicates what charset to use (e.g. "UTF-8").

    Hat tip goes also to Jacob, who once pointed me to the said article.

    EDITED: Thanks to a suggestion from Patrick, made the function more robust when handling an empty input stream. One more edit: nixed try/catch, Patrick's way is more laconic.

  • Sitansu

    Apache Commons allows:

    String myString = IOUtils.toString(myInputStream, "UTF-8");
    

    Of course, you could choose other character encodings besides UTF-8.

    Also see: (Docs)

  • sampathpremarathna

    Taking into account file one should first get a java.io.Reader instance. This can then be read and added to a StringBuilder (we don't need StringBuffer if we are not accessing it in multiple threads, and StringBuilder is faster). The trick here is that we work in blocks, and as such don't need other buffering streams. The block size is parameterized for run-time performance optimization.

    public static String slurp(final InputStream is, final int bufferSize)
    {
      final char[] buffer = new char[bufferSize];
      final StringBuilder out = new StringBuilder();
      try {
        final Reader in = new InputStreamReader(is, "UTF-8");
        try {
          for (;;) {
            int rsz = in.read(buffer, 0, buffer.length);
            if (rsz < 0)
              break;
            out.append(buffer, 0, rsz);
          }
        }
        finally {
          in.close();
        }
      }
      catch (UnsupportedEncodingException ex) {
        /* ... */
      }
      catch (IOException ex) {
          /* ... */
      }
      return out.toString();
    }
    
  • ralfoide

    If you are using Google-Collections/Guava you could do the following:

    InputStream stream = ...
    String content = CharStreams.toString(new InputStreamReader(stream, Charsets.UTF_8));
    Closeables.closeQuietly(stream);
    

    Note that the second parameter (i.e. Charsets.UTF_8) for the InputStreamReader isn't necessary, but it is generally a good idea to specify the encoding if you know it (which you should!)

  • TacB0sS
    public String readFully(InputStream inputStream, String encoding)
            throws IOException {
        return new String(readFully(inputStream), encoding);
    }    
    
    private byte[] readFully(InputStream inputStream)
            throws IOException {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        byte[] buffer = new byte[1024];
        int length = 0;
        while ((length = inputStream.read(buffer)) != -1) {
            baos.write(buffer, 0, length);
        }
        return baos.toByteArray();
    }
    
  • Drew Noakes

    Here's the most elegant, pure-Java (no library) solution I came up with after some experimentation:

    public static String fromStream(InputStream in) throws IOException
    {
        BufferedReader reader = new BufferedReader(new InputStreamReader(in));
        StringBuilder out = new StringBuilder();
        String newLine = System.getProperty("line.separator");
        String line;
        while ((line = reader.readLine()) != null) {
            out.append(line);
            out.append(newLine);
        }
        return out.toString();
    }
    
  • Jon Moore

    How about:

    import java.io.BufferedInputStream;
    import java.io.ByteArrayOutputStream;
    import java.io.InputStream;
    import java.io.IOException;    
    
    public static String readInputStreamAsString(InputStream in) 
        throws IOException {
    
        BufferedInputStream bis = new BufferedInputStream(in);
        ByteArrayOutputStream buf = new ByteArrayOutputStream();
        int result = bis.read();
        while(result != -1) {
          byte b = (byte)result;
          buf.write(b);
          result = bis.read();
        }        
        return buf.toString();
    }
    
  • user359996

    As an alternative to the Commons libraries, Google's excellent guava-libraries let you do this fairly concisely; given an InputStream named inputStream:

    import com.google.common.io.CharStreams;
    
    CharStreams.toString( new InputStreamReader( inputStream ));
    
  • TKH

    Here's more-or-less sampath's answer, cleaned up a bit and represented as a function:

    String streamToString(InputStream in) throws IOException {
      StringBuilder out = new StringBuilder();
      BufferedReader br = new BufferedReader(new InputStreamReader(in));
      for(String line = br.readLine(); line != null; line = br.readLine()) 
        out.append(line);
      br.close();
      return out.toString();
    }
    
  • DJDaveMark

    If you can't use Commons IO (FileUtils/IOUtils/CopyUtils) here's an example using a BufferedReader to read the file line by line:

    public class StringFromFile {
        public static void main(String[] args) /*throws UnsupportedEncodingException*/ {
            InputStream is = StringFromFile.class.getResourceAsStream("file.txt");
            BufferedReader br = new BufferedReader(new InputStreamReader(is/*, "UTF-8"*/));
            final int CHARS_PER_PAGE = 5000; //counting spaces
            StringBuilder builder = new StringBuilder(CHARS_PER_PAGE);
            try {
                for(String line=br.readLine(); line!=null; line=br.readLine()) {
                    builder.append(line);
                    builder.append('\n');
                }
            } catch (IOException ignore) { }
            String text = builder.toString();
            System.out.println(text);
        }
    }
    

    or if you want raw speed I'd propose a variation on what Paul de Vrieze suggested (which avoids using a StringWriter (which uses a StringBuffer internally) :

    public class StringFromFileFast {
        public static void main(String[] args) /*throws UnsupportedEncodingException*/ {
            InputStream is = StringFromFileFast.class.getResourceAsStream("file.txt");
            InputStreamReader input = new InputStreamReader(is/*, "UTF-8"*/);
            final int CHARS_PER_PAGE = 5000; //counting spaces
            final char[] buffer = new char[CHARS_PER_PAGE];
            StringBuilder output = new StringBuilder(CHARS_PER_PAGE);
            try {
                for(int read = input.read(buffer, 0, buffer.length);
                        read != -1;
                        read = input.read(buffer, 0, buffer.length)) {
                    output.append(buffer, 0, read);
                }
            } catch (IOException ignore) { }
    
            String text = output.toString();
            System.out.println(text);
        }
    }
    
  • JacobusR

    If you were feeling adventurous, you could mix Scala and Java and end up with this:

    scala.io.Source.fromInputStream(is).mkString("")
    

    Mixing Java and Scala code and libraries has it's benefits.

    See full description here: Idiomatic way to convert an InputStream to a String in Scala

  • BennX

    I ran some timing tests because time matters, always.

    I attempted to get the response into a String 3 different ways. (shown below)
    I left out try/catch blocks for the sake readability.

    To give context, this is the preceding code for all 3 approaches:

       String response;
       String url = "www.blah.com/path?key=value";
       GetMethod method = new GetMethod(url);
       int status = client.executeMethod(method);
    

    1)

     response = method.getResponseBodyAsString();
    

    2)

    InputStream resp = method.getResponseBodyAsStream();
    InputStreamReader is=new InputStreamReader(resp);
    BufferedReader br=new BufferedReader(is);
    String read = null;
    StringBuffer sb = new StringBuffer(read);
    while((read = br.readLine()) != null) {
        sb.append(read);
    }
    response = sb.toString();
    

    3)

    InputStream iStream  = method.getResponseBodyAsStream();
    StringWriter writer = new StringWriter();
    IOUtils.copy(iStream, writer, "UTF-8");
    response = writer.toString();
    

    So, after running 500 tests on each approach with the same request/response data, here are the numbers. Once again, these are my findings and your findings may not be exactly the same, but I wrote this to give some indication to others of the efficiency differences of these approaches.

    Ranks:
    Approach #1
    Approach #3 - 2.6% slower than #1
    Approach #2 - 4.3% slower than #1

    Any of these approaches is an appropriate solution for grabbing a response and creating a String from it.

  • Matt Shannon
    import java.io.ByteArrayOutputStream;
    import java.io.InputStream;
    
    ...
    
    InputStream is = ....
    ByteArrayOutputStream baos = new ByteArrayOutputStream(8192);
    byte[] buffer = new byte[8192];
    int count = 0;
    try {
      while ((count = is.read(buffer)) != -1) {
        baos.write(buffer, 0, count);
      }
    }
    finally {
      try {
        is.close();
      }
      catch (Exception ignore) {
      }
    }
    
    String charset = "UTF-8";
    String inputStreamAsString = baos.toString(charset);
    
  • Thamme Gowda

    make sure to close the streams at end if you use Stream Readers

        private String readStream(InputStream iStream) throws IOException {
            //build a Stream Reader, it can read char by char
            InputStreamReader iStreamReader = new InputStreamReader(iStream);
            //build a buffered Reader, so that i can read whole line at once
            BufferedReader bReader = new BufferedReader(iStreamReader);
            String line = null;
            StringBuilder builder = new StringBuilder();
            while((line = bReader.readLine()) != null) {  //Read till end
                builder.append(line);
            }
            bReader.close();         //close all opened stuff
            iStreamReader.close();
            iStream.close();
            return builder.toString();
        }
    
  • Omkar Khot
    InputStreamReader i=new InputStreamReader(s);
    BufferedReader str=new BufferedReader(i);
    String msg=str.readLine();
    System.out.println(msg);
    

    here s is your InputStream object which will get convert into String :)

  • Victor

    Well you can program it for yourself.. it's not complicated..

    String Inputstream2String (InputStream is) throws IOException 
        {
            final int PKG_SIZE = 1024;
            byte[] data = new byte [PKG_SIZE];
            StringBuilder buffer = new StringBuilder(PKG_SIZE * 10);
            int size;
    
            size = is.read(data, 0, data.length);
            while (size > 0)
            {
                String str = new String(data, 0, size);
                buffer.append(str);
                size = is.read(data, 0, data.length);
            }
            return buffer.toString();
        }
    
  • Anand N

    The below code worked for me.

        URL url = MyClass.class.getResource("/" + configFileName);
        BufferedInputStream bi = (BufferedInputStream) url.getContent();
        byte[] buffer = new byte[bi.available() ];
        int bytesRead = bi.read(buffer);
        String out = new String(buffer);
    

    Please note, according to Java docs, the available() method might not work with InputStream but always works with BufferedInputStream. In case you don't want to use available() method we can always use the below code

        URL url = MyClass.class.getResource("/" + configFileName);
        BufferedInputStream bi = (BufferedInputStream) url.getContent();
        File f = new File(url.getPath());
        byte[] buffer = new byte[ (int) f.length()];
        int bytesRead = bi.read(buffer);
        String out = new String(buffer);
    

    I am not sure if there will be any encoding issues. Please comment, if there will be any issues with the code

  • Ben Barkay

    I have written a class that does just that, so I figured I'd share it with everyone. Sometimes you don't want to add Apache Commons just for one thing, and want something dumber than Scanner that doesn't examine the content.

    Usage is as follows

    // Read from InputStream
    String data = new ReaderSink(inputStream, Charset.forName("UTF-8")).drain();
    
    // Read from File
    data = new ReaderSink(file, Charset.forName("UTF-8")).drain();
    
    // Drain input stream to console
    new ReaderSink(inputStream, Charset.forName("UTF-8")).drainTo(System.out);
    

    Here is the code for ReaderSink:

    import java.io.*;
    import java.nio.charset.Charset;
    
    /**
     * A simple sink class that drains a {@link Reader} to a {@link String} or
     * to a {@link Writer}.
     *
     * @author Ben Barkay
     * @version 2/20/2014
     */
    public class ReaderSink {
        /**
         * The default buffer size to use if no buffer size was specified.
         */
        public static final int DEFAULT_BUFFER_SIZE = 1024;
    
        /**
         * The {@link Reader} that will be drained.
         */
        private final Reader in;
    
        /**
         * Constructs a new {@code ReaderSink} for the specified file and charset.
         * @param file      The file to read from.
         * @param charset   The charset to use.
         * @throws FileNotFoundException    If the file was not found on the filesystem.
         */
        public ReaderSink(File file, Charset charset) throws FileNotFoundException {
            this(new FileInputStream(file), charset);
        }
    
        /**
         * Constructs a new {@code ReaderSink} for the specified {@link InputStream}.
         * @param in        The {@link InputStream} to drain.
         * @param charset   The charset to use.
         */
        public ReaderSink(InputStream in, Charset charset) {
            this(new InputStreamReader(in, charset));
        }
    
        /**
         * Constructs a new {@code ReaderSink} for the specified {@link Reader}.
         * @param in    The reader to drain.
         */
        public ReaderSink(Reader in) {
            this.in = in;
        }
    
        /**
         * Drains the data from the underlying {@link Reader}, returning a {@link String} containing
         * all of the read information. This method will use {@link #DEFAULT_BUFFER_SIZE} for
         * its buffer size.
         * @return  A {@link String} containing all of the information that was read.
         */
        public String drain() throws IOException {
            return drain(DEFAULT_BUFFER_SIZE);
        }
    
        /**
         * Drains the data from the underlying {@link Reader}, returning a {@link String} containing
         * all of the read information.
         * @param bufferSize    The size of the buffer to use when reading.
         * @return  A {@link String} containing all of the information that was read.
         */
        public String drain(int bufferSize) throws IOException {
            StringWriter stringWriter = new StringWriter();
            drainTo(stringWriter, bufferSize);
            return stringWriter.toString();
        }
    
        /**
         * Drains the data from the underlying {@link Reader}, writing it to the
         * specified {@link Writer}. This method will use {@link #DEFAULT_BUFFER_SIZE} for
         * its buffer size.
         * @param out   The {@link Writer} to write to.
         */
        public void drainTo(Writer out) throws IOException {
            drainTo(out, DEFAULT_BUFFER_SIZE);
        }
    
        /**
         * Drains the data from the underlying {@link Reader}, writing it to the
         * specified {@link Writer}.
         * @param out           The {@link Writer} to write to.
         * @param bufferSize    The size of the buffer to use when reader.
         */
        public void drainTo(Writer out, int bufferSize) throws IOException {
            char[] buffer = new char[bufferSize];
            int read;
            while ((read = in.read(buffer)) > -1) {
                out.write(buffer, 0, read);
            }
        }
    }
    
  • Brian Pontarelli

    JDK 7/8 answer that closes the stream and still throws an IOException:

    StringBuilder build = new StringBuilder();
    byte[] buf = new byte[1024];
    int length;
    try (InputStream is = getInputStream()) {
      while ((length = is.read(buf)) != -1) {
        build.append(new String(buf, 0, length));
      }
    }
    
  • user2217011

    You can use apache commons. In the IOUtils you can find the toString metod with 3 helpfull implementations.

    public static String toString(InputStream input) throws IOException {
            return toString(input, Charset.defaultCharset());
    }
    
    public static String toString(InputStream input) throws IOException {
            return toString(input, Charset.defaultCharset());
    }
    
    public static String toString(InputStream input, String encoding)
                throws IOException {
            return toString(input, Charsets.toCharset(encoding));
    }
    
  • Fred

    This snippet was found in \sdk\samples\android-19\connectivity\NetworkConnect\NetworkConnectSample\src\main\java\com\example\android\networkconnect\MainActivity.java which is licensed under Apache License, Version 2.0 and written by Google.

    /** Reads an InputStream and converts it to a String.
     * @param stream InputStream containing HTML from targeted site.
     * @param len Length of string that this method returns.
     * @return String concatenated according to len parameter.
     * @throws java.io.IOException
     * @throws java.io.UnsupportedEncodingException
     */
    private String readIt(InputStream stream, int len) throws IOException, UnsupportedEncodingException {
        Reader reader = null;
        reader = new InputStreamReader(stream, "UTF-8");
        char[] buffer = new char[len];
        reader.read(buffer);
        return new String(buffer);
    }
    
  • JavaTechnical

    Try these 4 statements..

    As per the point recalled by Fred, it is not recommended to append a String with += operator since every time a new char is appended to the existing String creating a new String object again and assigning its address to st while the old st object becomes garbage.

    public String convertStreamToString(InputStream is)
    {
    int k;
         StringBuffer sb=new StringBuffer();
         while((k=fin.read())!=-1)
         {
         sb.append((char)k);
         }
    return sb.toString();
    }
    

    Not recommended, but this is also a way

    public String convertStreamToString(InputStream is) { 
        int k;
        String st="";
        while((k=is.read())!=-1)
        {
        st+=(char)k;
        }
    return st;
    }
    
  • laksys
    public static String getString( InputStream is) throws IOException {
        int ch;
        StringBuilder sb = new StringBuilder();
        while((ch = is.read())!= -1)
            sb.append((char)ch);
        return sb.toString();
    }
    
  • Mercy

    First ,you have to know the encoding of string that you want to convert.Because the java.io.InputStream operates an underlying array of bytes,however,a string is composed by a array of character that needs an encoding, e,g. UTF-8,the JDK will take the default encoding that is taken from System.getProperty("file.encoding","UTF-8");

    byte[] bytes=new byte[inputStream.available()];
    inputStream.read(bytes);
    String s = new String(bytes);
    

    If inputStream's byte array is very big, you could do it in loop.

    :EOF

  • user2370932
      InputStream IS=new URL("http://www.petrol.si/api/gas_prices.json").openStream();   
    
      ByteArrayOutputStream BAOS=new ByteArrayOutputStream();
      IOUtils.copy(IS, BAOS);
      String d= new String(BAOS.toByteArray(),"UTF-8");           
    
    System.out.println(d);
    
  • soBinary

    Quick and easy:

    String result = (String)new ObjectInputStream( inputStream ).readObject();