I'm writing a Server/Client application, where the server will be communicating with many different clients. The communication with each client is taking place in a separate thread on the machine running the server.
So far I have been using the BufferedReader class in order to read data from the client sockets using the readLine() method. The problem I have with readLine() is that it stops reading when it finds a new line character. Due to the nature of my program I would like to substitute the new line limitation with a sequence of characters like $^%, so with a readLine() call the BufferedReader will keep reading unitl it finds &^%. For example if a client tries to send a url or a filepath where \n can be found as part of the natural path the readLine() method will read the \n and stop reading further.
I have created the following class in an attempt to solve this problem. But I have now created an even bigger one. When I use the BufferedReader class and the readLine() method my Server can service a lot of clients, but when I use CustomBufferedReader and readCustomLine() the Server crashes after the 4 or 5 threads start running. I'm pretty sure my class is consuming lots of resources compared to readLine() but I have no idea, why or how.
I would appreciate any insight on the matter.
public class CustomBufferedReader extends BufferedReader {
public CustomBufferedReader(Reader reader) {
super(reader);
}
/**
* Keeps reading data from a socket and stores them into a String buffer until
* the combination of $^% is red.
*
* #return A String containing the buffer red without the $^% ending.
* #throws IOException
*/
public String readCustomLine() throws IOException {
//$^%
String buffer="";
try
{
if(super.ready())
{
//First I'm reading 3 bytes in order to have at least 3 bytes
//in the buffer to compare them later on.
try
{
buffer = buffer + (char)super.read();
buffer = buffer + (char)super.read();
buffer = buffer + (char)super.read();
}
catch (IOException e)
{
e.printStackTrace();
System.out.println(e.getMessage());
}
int i=0;
//This while well keep reading bytes and adding the to the buffer until it reads
//$^% as the terminating sequence of bytes.
while (!(buffer.charAt(i)=='$' && buffer.charAt(i+1)=='^' && buffer.charAt(i+2)=='%')){
try
{
buffer = buffer + (char)super.read();
i++;
}
catch (IOException e)
{
e.printStackTrace();
System.out.println(e.getMessage());
}
}
// Returns the saved buffer after subtracting the $^% ending.
return buffer.substring(0, buffer.length() - 3);
}
}
catch (IOException e)
{
//e.printStackTrace();
}
return buffer;
}
}
I think this is an easier way to achieve what you are looking for:
String line = new Scanner(reader).useDelimiter("$^%").next();
About why your implementation of readCustomLine is not working, you may have a concurrency problem. If you take a look at the readLine implementation of BufferedReader you may notice that all its code runs enclosed in a synchronized block. So you may try that in your code.
Also, If an Exception is thrown from super.read() you just catch it and keep going even though the resulting buffer will have errors, you can try removing the inner try/catch blocks.
Finally as EJP has pointed out, you should remove the ready() call and check every super.read() for a -1 (meaning EOF).
import java.io.BufferedReader;
import java.io.IOException;
import java.io.Reader;
public class CustomBufferedReader extends BufferedReader {
public CustomBufferedReader(final Reader reader) {
super(reader);
}
/**
* Keeps reading data from a socket and stores them into a String buffer
* until the combination of $^% is read.
*
* #return A String containing the buffer read without the $^% ending.
* #throws IOException
*/
public synchronized String readCustomLine() throws IOException {
// $^%
String buffer = "";
try {
// First I'm reading 3 bytes in order to have at least 3 bytes
// in the buffer to compare them later on.
buffer = buffer + safeRead();
buffer = buffer + safeRead();
buffer = buffer + safeRead();
int i = 0;
// This while will keep reading bytes and adding them to the
// buffer until it reads $^% as the terminating sequence of bytes.
while (!(buffer.charAt(i) == '$' && buffer.charAt(i + 1) == '^'
&& buffer.charAt(i + 2) == '%')) {
buffer = buffer + safeRead();
i++;
}
// Returns the saved buffer after subtracting the $^% ending.
return buffer.substring(0, buffer.length() - 3);
} catch (IOException e) {
/*
* Personally, I would remove this try/catch block and let the
* exception reach the caller
*/
e.printStackTrace();
System.out.println(e.getMessage());
}
return buffer;
}
private char safeRead() throws IOException {
int value = super.read();
if (value == -1) {
throw new EOFException();
}
return (char) value;
}
}
Related
I'm trying to implement an interruptible readline method [1] in Java , because the standard routine (using a buffered reader around System.in) isn't. I've come up with a routine that works pretty well on Linux, but when you run it in Windows-land, the key-presses are not echoed to the console until the user presses enter - gah!
After some experimenting, it seems the key-presses are only echo'd if there's a blocking call to System.in.read() while the user types.
Does anyone know if this is fixable? I tried to reflect my way in to using the private java.ui.Console#echo method, but it didn't seem to do anything. My next port of call will be to have a look at the JNA API and see if I can get direct access to the console from there.
1 - For reference, here's my interruptible readline implementation. I capture ctrl-c and interrupt the thread that's calling readline - handled elsewhere, working, and probably not relevant for this example
import java.io.EOFException;
import java.io.IOException;
import java.io.InputStream;
public class EchoTest {
private static final long MILLIS_BETWEEN_STDIN_CHECKS = 10;
public static void main(String[] args) throws IOException, InterruptedException {
System.out.println("Type something");
// on windows, there's no echo until enter, dammit!
String input = readline(System.in);
System.out.println("Hello, " + input);
}
/**
* Read a line of input from an input stream, presumably stdin.
*
* #param stdin An InputStream to read a line from.
*
* #return the string that was read, with the line terminator removed.
*
* #throws IOException if there was an io error reading from stdin
* #throws InterruptedException if the thread was interrupted while waiting to receive keypresses
*/
public static String readline(InputStream stdin) throws IOException, InterruptedException {
// we use mark to peek ahead for \r\n
if (!stdin.markSupported()) {
throw new IllegalArgumentException("stdin must support mark");
}
// result is stored in here
StringBuffer stringBuffer = new StringBuffer(512);
// a byte buffer as we read a byte at a time from stdin
byte[] bytes = new byte[64];
// number of bytes we've read in to the buffer
int bytesRead = 0;
while (true) {
// check whether read should block
if (stdin.available() > 0) {
int byteRead = stdin.read();
if (byteRead == -1) {
// end of stream - not expected for readline
throw new EOFException();
}
// check for line ending character sequences, we need to detect \r, \n or \r\n
if (byteRead == '\n') {
// we're done!
break;
} else if (byteRead == '\r') {
// we're done, but we might need to consume a trailing \n
if (stdin.available() == 0) {
// nothing is ready, we presume that if \r\n was sent, then they'd be sent as one and the buffer would
// already have the \n
// worst case, the next call to readline will exit with an empty string - if this appears to happen, we
// could detect a \n being in stdin at the start and drop it on the floor
break;
} else {
// there is a byte there - mark our position and check if it's \n
stdin.mark(1);
if (stdin.read() == '\n') {
// it is we're done
break;
} else {
// it isn't \n, reset position for the next call to readline or read
stdin.reset();
break;
}
}
} else {
bytes[bytesRead++] = (byte)byteRead;
// flush buffer if it's full
if (bytesRead == bytes.length) {
stringBuffer.append(new String(bytes, 0, bytesRead));
bytesRead = 0;
}
}
} else {
if (Thread.interrupted()) {
throw new InterruptedException();
} else {
Thread.sleep(MILLIS_BETWEEN_STDIN_CHECKS);
}
}
}
stringBuffer.append(new String(bytes, 0, bytesRead));
return stringBuffer.toString();
}
}
I'm using the class DataInputStream to read from a Socket. I must use the readByte (not readLine) because the input does not necessarily in String format. The problem is I must wait the end of the stream to call a function and I couldn't detect it.
The code:
reader = new DataInputStream(socket.getInputStream());
byte dt;
String cmd = "";
while( (dt = reader.readByte()) >= 0){
cmd += (char)dt;
if( /* end of the stream reached */ ){
processInput(cmd);
}
}
System.out.println("End of the loop");
The problem is I don't know how to write this if. And, the end of the loop is not being reached when the Stream ends, the proof of this is that the phrase "End of the loop" is never being printed, so it is just stuck on the loop
This is documented at readByte()
Throws:
EOFException - if this input stream has reached the end.
So in order to see end of file, you must use try/catch
try {
while(true){
cmd += (char) reader.readByte();
}
} catch (EOFException e) {
// handle EOF
}
/* end of the stream reached */
processInput(cmd);
System.out.println("End of the loop");
An alternative would be to use one of the read() functions, which return -1 when reaching end of file, e.g.
int dt;
while ((dt = reader.read()) >= 0) {
// ...
}
Using one of the other read(...) functions could be more efficient, since they return a buffer full of input.
First of all I have a couple hours experience with Java so, If its a bit simple question, sorry about that.
Now, I want to read a file but I dont want to start at the beginning of file instead I want to skip first 1024 bytes and than start reading. Is there a way to do that ? I realized that RandomAccessFile might be useful but I'm not sure.
try {
FileInputStream inputStream=new FileInputStream(fName);
// skip 1024 bytes somehow and start reading .
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
You will want to use the FileInputStream.skip method to seek to the point you want and then begin reading from that point. There is more information in the javadocs about FileInputStream that you might find interesting.
You could use a method like skipBytes() here
/**
* Read count bytes from the InputStream to "/dev/null".
* Return true if count bytes read, false otherwise.
*
* #param is
* The InputStream to read.
* #param count
* The count of bytes to drop.
*/
private static boolean skipBytes(
java.io.InputStream is, int count) {
byte[] buff = new byte[count];
/*
* offset could be used in "is.read" as the second arg
*/
try {
int r = is.read(buff, 0, count);
return r == count;
} catch (IOException e) {
e.printStackTrace();
}
return false;
}
I am developing a tool to get client information, send to a server, and receive the information again (a proxy). I'm also trying to dump the data being received from the server. I can read the Integer representation of the inputStream, but I am not able to read the String format. I've tried the below example, but it hangs and never connects to the server. Also, System.out.println(inputStream.nextLine()) displays only one line and hangs.
public void run() {
try {
int i;
while ((i = inputStream.read()) != -1){
System.out.println(IOUtils.toString(inputStream));
outputStream.write(i);
}
} catch (IOException e) {
System.out.println("Lost connection to the client.");
}
}
My guess at this is that you're reading from the input stream, and then using the IOUtils library to read from the stream too. My suspicion is that your application is reading the first byte from the input stream, then reading the remainder of the inputstream with the IOUtils library, and then printing out the initial byte that was read.
It doesn't make any sense to call IOUtils.toString(inputstream) from within a loop. That method call will put all the data from the inputstream into a string. Why have the loop at all in this case?
You might want to try not using the IOUtils library for this. Just read a byte of data, push it into a StringBuilder, and then print that byte. In this approach, the loop would be necessary, and you'll probably get what you're looking for.
Try something like this, but modify it as necessary to print the data at the same time to your output stream:
public static String inputStreamToString(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();
}
The code you posted doesn't attempt to connect to the server, but if any of it executes you must already have connected.
If your program is hanging in this code, either the server hasn't sent any data yet, or the IOUtils.toString() method probably tries to read to EOS, so if the peer doesn't close the connection you will block here forever.
If your program hangs at a readLine() call it means the peer hasn't sent a line to read.
Basically, I have a URL that streams xml updates from a chat room when new messages are posted. I'd like to turn that URL into an InputStream and continue reading from it as long as the connection is maintained and as long as I haven't sent a Thread.interrupt(). The problem I'm experiencing is that BufferedReader.ready() doesn't seem to become true when there is content to be read from the stream.
I'm using the following code:
BufferedReader buf = new BufferedReader(new InputStreamReader(ins));
String str = "";
while(Thread.interrupted() != true)
{
connected = true;
debug("Listening...");
if(buf.ready())
{
debug("Something to be read.");
if ((str = buf.readLine()) != null) {
// str is one line of text; readLine() strips the newline character(s)
urlContents += String.format("%s%n", str);
urlContents = filter(urlContents);
}
}
// Give the system a chance to buffer or interrupt.
try{Thread.sleep(1000);} catch(Exception ee) {debug("Caught thread exception.");}
}
When I run the code, and post something to the chat room, buf.ready() never becomes true, resulting in the lines never being read. However, if I skip the "buf.ready()" part and just read lines directly, it blocks further action until lines are read.
How do I either a) get buf.ready() to return true, or b) do this in such a way as to prevent blocking?
Thanks in advance,
James
How to create a Java non-blocking InputStream
You can't. Your question embodies a contradiciton in terms. Streams in Java are blocking. There is therefore no such thing as a 'non-blocking InputStream'.
Reader.ready() returns true when data can be read without blocking. Period. InputStreams and Readers are blocking. Period. Everything here is working as designed. If you want more concurrency with these APIs you will have to use multiple threads. Or Socket.setSoTimeout() and its near relation in HttpURLConnection.
For nonblocking IO don't use InputStream and Reader (or OutputStream/Writer), but use the java.nio.* classes, in this case a SocketChannel (and additional a CharsetDecoder).
Edit: as an answer to your comment:
Specifically looking for how to create a socket channel to an https url.
Sockets (and also SocketChannels) work on the transport layer (TCP), one (or two) level(s) below application layer protocols like HTTP. So you can't create a socket channel to an https url.
You would instead have to open a Socket-Channel to the right server and the right port (443 if nothing else given in the URI), create an SSLEngine (in javax.net.ssl) in client mode, then read data from the channel, feeding it to the SSL engine and the other way around, and send/get the right HTTP protocol lines to/from your SSLEngine, always checking the return values to know how many bytes were in fact processed and what would be the next step to take.
This is quite complicated (I did it once), and you don't really want to do this if you are not implementing a server with lots of clients connected at the same time (where you can't have a single thread for each connection). Instead, stay with your blocking InputStream which reads from your URLConnection, and put it simply in a spare thread which does not hinder the rest of your application.
You can use the Java NIO library which provides non-blocking I/O capabilities. Take a look at this article for details and sample code: http://www.drdobbs.com/java/184406242.
There is no HTTP/HTTPS implementation using Channels. There is no way to read the inputstream from a httpurlconnaction in a non-blocking way. You either have to use a third party lib or implement http over SocketChannel yourself.
import java.io.InputStream;
import java.util.Arrays;
/**
* This code demonstrates non blocking read from standard input using separate
* thread for reading.
*/
public class NonBlockingRead {
// Holder for temporary store of read(InputStream is) value
private static String threadValue = "";
public static void main(String[] args) throws InterruptedException {
NonBlockingRead test = new NonBlockingRead();
while (true) {
String tmp = test.read(System.in, 100);
if (tmp.length() > 0)
System.out.println(tmp);
Thread.sleep(1000);
}
}
/**
* Non blocking read from input stream using controlled thread
*
* #param is
* — InputStream to read
* #param timeout
* — timeout, should not be less that 10
* #return
*/
String read(final InputStream is, int timeout) {
// Start reading bytes from stream in separate thread
Thread thread = new Thread() {
public void run() {
byte[] buffer = new byte[1024]; // read buffer
byte[] readBytes = new byte[0]; // holder of actually read bytes
try {
Thread.sleep(5);
// Read available bytes from stream
int size = is.read(buffer);
if (size > 0)
readBytes = Arrays.copyOf(buffer, size);
// and save read value in static variable
setValue(new String(readBytes, "UTF-8"));
} catch (Exception e) {
System.err.println("Error reading input stream\nStack trace:\n" + e.getStackTrace());
}
}
};
thread.start(); // Start thread
try {
thread.join(timeout); // and join it with specified timeout
} catch (InterruptedException e) {
System.err.println("Data were note read in " + timeout + " ms");
}
return getValue();
}
private synchronized void setValue(String value) {
threadValue = value;
}
private synchronized String getValue() {
String tmp = new String(threadValue);
setValue("");
return tmp;
}
}