Hello I am currently working with sockets and input/output streams. I have a strange problem with my loop I use to send bytes. For some reason it get stuck when it tries to read from the inputstream when it is supposed to stop. Does anyone have any idea whats wrong?
int bit;
final byte[] request = new byte[1024];
if (response instanceof InputStream)
{
while ((bit = response.read(request)) > 0) { // <-- Stuck here
incoming.getOutputStream().write(request,0,bit);
incoming.getOutputStream().flush();
}
}
incoming.close();
InputStream.read blocks until input data is available, end of file is detected, or an exception is thrown.
You don't catch the exception, and don't check for EOF.
What I've done in the past to leave each side open is to add a termination character to the end of each message that you wouldn't expect to see in the message. If you are building the messages yourself then you could use a character such as a ; or maybe double pipes or something ||. Then just check for that character on the receiving end. Just a workaround. Not a solution. It was necessary in my case but may not be for you.
Related
I know that similar questions have been asked before, but not exactly what I'm asking. To begin with, let me explain my purpose. I'm trying to write a kind of "remote shell" that will take in characters from the console (System.in) on character at a time and then send those to a remote session on another machine, write them to that machine and gather any characters it might output to return to my shell to display back to the user.
So, the issue is that System.in, no matter what I do, doesn't really support a "raw" mode where any type of reader is able to read just one character at a time UNTIL a terminator character is entered, typically new line.
Things I have tried, Using Scanner, using a buffered reader, creating a FileDescriptor.in and creating a fileInputStream from that, using a FileChannel and reading into a ByteBuffer that is one character long, etc. In all cases, it seems, System.in only makes characters available to the java application after a terminator character has been entered by the user. I'm convinced there is not a "java" way to do this, so the question is, does anyone have some native code, wrapped in a java library to do this? Its hard to find such a thing just searching GitHub.
BTW, for the remote console, I'm using the pty4J package. I've seen sample projects that connect to that code using other langauages, for example javaScript running in a browser to create a web based shell. Other languages all you to do a simple "get_char" on standard in.
Some examples of the code I've tried:
Scanner scanner = new Scanner(System.in);
FileDescriptor fd = FileDescriptor.in;
FileInputStream fis = new FileInputStream(fd);
FileChannel fc = fis.getChannel();
while(process.isAlive()) {
System.out.println(scanner.next());
// ByteBuffer bb = ByteBuffer.allocate(1);
// int c = fc.read(bb);
// int c = fis.read();
// System.err.println("Read " + c);
// if (c == 1) {
// os.write(bb.get());
// }
}
You can see that I've tried various methods to read the input: scanner.next(), fc.read(byteBuffer), fileInputStream.read(), etc. All attempts "wait" till a terminator character is entered.
Additionally, I have tried using the "useDelimiter" and "next(pattern)" methods on the scanner too. That's still not working.
Any pointer or help is much appreciated.
Below is an example of reading one character at a time until end of stream is reached. On linux, you type control-d to signal the end input. I think on Windows, you type control-c to end of input.
import java.io.*;
class Test {
public static void main(String[] args) throws IOException {
int c = 0;
while( (c=System.in.read()) != -1){
System.out.println((char) c);
}
}
}
I am trying to download web page with all its resources . First i download the html, but when to be sure to keep file formatted and use this function below .
there is and issue , i found 10 in the final file and when i found that hexadecimal code of the LF or line escape . and this makes troubles to my javascript functions .
Example of the final result :
<!DOCTYPE html>10<html lang="fr">10 <head>10 <meta http-equiv="content-type" content="text/html; charset=UTF-8" />10
Can someone help me to found the real issue ?
public static String scanfile(File file) {
StringBuilder sb = new StringBuilder();
try {
BufferedReader bufferedReader = new BufferedReader(new FileReader(file));
while (true) {
String readLine = bufferedReader.readLine();
if (readLine != null) {
sb.append(readLine);
sb.append(System.lineSeparator());
Log.i(TAG,sb.toString());
} else {
bufferedReader.close();
return sb.toString();
}
}
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
There are multiple problems with your code.
Charset error
BufferedReader bufferedReader = new BufferedReader(new FileReader(file));
This isn't going to work in tricky ways.
Files (and, for that matter, data given to you by webservers) comes in bytes. A stream of numbers, each number being between 0 and 255.
So, if you are a webserver and you want to send the character รถ, what byte(s) do you send?
The answer is complicated. The mapping that explains how some character is rendered in byte(s)-form is called a character set encoding (shortened to 'charset').
Anytime bytes are turned into characters or vice versa, there is always a charset involved. Always.
So, you're reading a file (that'd be bytes), and turning it into a Reader (which is chars). Thus, charset is involved.
Which charset? The API of new FileReader(path) explains which one: "The system default". You do not want that.
Thus, this code is broken. You want one of two things:
Option 1 - write the data as is
When doing the job of querying the webserver for the data and relaying this information onto disk, you'd want to just store the bytes (after all, webserver gives bytes, and disks store bytes, that's easy), but the webserver also sends the encoding, in a header, and you need to save this separately. Because to read that 'sack of bytes', you need to know the charset to turn it into characters.
How would you do this? Well, up to you. You could for example decree that the data file starts with the name of a charset encoding (as sent via that header), then a 0 byte, and then the data, unmodified. I think you should go with option 2, however
Option 2
Another, better option for text-based documents (which HTML is), is this: When reading the data, convert it to characters, using the encoding as that header tells you. Then, to save it to disk, turn the chars back to bytes, using UTF-8, which is a great encoding and an industry standard. That way, when reading, you just know it's UTF-8, period.
To read a UTF-8 text file, you do:
Files.newBufferedReader(Paths.get(file));
The reason this works, is that the Files API, unlike most other APIs (and unlike FileReader, which you should never ever use), defaults to UTF_8 and not to platform-default. If you want, you can make it more readable:
Files.newBufferedReader(Paths.get(file), StandardCharsets.UTF_8);
same thing - but now in the code it is clear what's happening.
Broken exception handling
} catch (IOException e) {
e.printStackTrace();
return null;
}
This is not okay - if you catch an exception, either [A] throw something else, or [B] handle the problem. And 'log it and keep going' is definitely not 'handling' it. Your strategy of exception handling results in 1 error resulting in a thousand things going wrong with a thousand stack traces, and all of them except the first are undesired and irrelevant, hence why this is horrible code and you should never write it this way.
The easy solution is to just put throws IOException on your scanFile method. The method inherently interacts with files, it SHOULD be throwing that. Note that your psv main(String[] args) method can, and usually should, be declared to throws Exception.
It also makes your code simpler and shorter, yay!
Resource Management failure
a filereader is a resource. You MUST close it, no matter what happens. You are not doing that: If .readLine() throws an exception, then your code will jump to the catch handler and bufferedReader.close is never executed.
The solution is to use the ARM (Automatic Resource Management) construct:
try (var br = Files.newBufferedReader(Paths.get(file), StandardCharsets.UTF_8)) {
// code goes here
}
This construct ensures that close() is invoked, regardless of how the 'code goes here' block exits. Even if it 'exits' via an exception or a return statement.
The problem
Your 'read a file and print it' code is other than the above three items mostly fine. The problem is that the HTML file on disk is corrupted; the error lies in your code that reads the data from the web server and saves it to disk. You did not paste that code.
Specifically, System.lineSeparator() returns the actual string. Thus, assuming the code you pasted really is the code you are running, if you are seeing an actual '10' show up, then that means the HTML file on disk has that in there. It's not the read code.
Closing thoughts
More generally the job of 'just print a file on disk with a known encoding' can be done in far fewer lines of code:
public static String scanFile(String path) throws IOException {
return Files.readString(Paths.get(path));
}
You should just use the above code instead. It's simple, short, doesn't have any bugs, cannot leak resources, has proper exception handling, and will use UTF-8.
Actually, there is no problem in this function I was mistakenly adding 10 using another function in my code .
I'm getting an strange issue in a loop that is reading a BufferedReader and never ends...
This is the code:
BufferedReader in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
int b;
StringBuilder buff = new StringBuilder();
while ((b = in.read()) != -1 ) {
buff.append((char) b);
}
System.out.println(buff.toString());
But never arrives to the last line to print buff.toString().
There's anything wrong in this code?
Thanks.
Can you try changing the while condition like this.
while ((b = in.read()) > -1 ) {
buff.append((char) b);
}
Your loop is trying to read until EOF (that is the only reason for an input stream/reader to return -1 for the read() method).
The problem is that your HTTP connection (and your socket) might be left open (for a while), also leaving your input stream/reader open. So instead of reaching the end condition for your loop, the in.read() call will just block, waiting for input.
If you control the "other side" of the connection, you could close it, to see what happens. But I doubt that would work for the use case in general. Instead, you need to parse the message according to the HTTP protocol (see HTTP/1.1 RFC2616).
If you only need to parse the headers, then you could use your BufferedReader, and read only full lines, until you find an empty line. This will work, because HTTP uses linefeeds (linefeed being CRLF in this case) after each header name/value pair, and end the header part with exactly two linefeeds. Everything after that will be the message body.
PS: This is the easy/happy case. Note that a single connection/socket may be re-used for multiple HTTP requests/responses. You may have handle this as well, depending on your requirements.
Here I have the following bit of code taken from this oracle java tutorial:
// Defaults to READ
try (SeekableByteChannel sbc = Files.newByteChannel(file)) {
ByteBuffer buf = ByteBuffer.allocate(10);
// Read the bytes with the proper encoding for this platform. If
// you skip this step, you might see something that looks like
// Chinese characters when you expect Latin-style characters.
String encoding = System.getProperty("file.encoding");
while (sbc.read(buf) > 0) {
buf.rewind();
System.out.print(Charset.forName(encoding).decode(buf));
buf.flip();//LINE X
}
} catch (IOException x) {
System.out.println("caught exception: " + x);
So basically I do not get any output out of it.
I have tried to put some flags in the while loop to check whether or not it gets into, and it gets into. I also changed the encoding in Charset.defaultCharset().decode(buf), result : no output.
Of course there is text in the file passed to newByteChannel(file);
Any idea?
Thanks a lot in advance.
**
EDIT:
** Solved, it was just the file I was trying to access that had been previously accidentally corrupted. After having changed file, everything is working.
The code looks wrong. Try changing the rewind() to flip(), and the flip() to compact().
I'm working with Netty and it seems that a FrameDecoder in a ChannelPipeline isn't invoked unless/until a carriage return is received. For example, I have the following decoder that I've written to attempt to detect when a complete JSON string has been received:
public class JsonDecoder extends FrameDecoder {
#Override
protected Object decode(ChannelHandlerContext ctx, Channel channel, ChannelBuffer buf) {
char inChar = 0;
ChannelBuffer origBuffer = buf.copy();
StringBuilder json = new StringBuilder();
int ctr = 0;
while(buf.readable()) {
inChar = (char) buf.readByte();
json.append(inChar);
if (inChar == '{') {
ctr++;
} else if (inChar == '}') {
ctr--;
}
}
if (json.length() > 0 && ctr == 0) {
return origBuffer;
}
buf.resetReaderIndex();
return null;
}
}
(Please pardon the somewhat sloppy code - this is my first attempt using Netty and a bit of a learning experience.)
What I see happen is that this works fine when I test it by connecting to the server using telnet, paste in some valid JSON and press return. However, if I do not press return after the final closing '}' in the JSON string, the decoder never gets called with an updated buffer.
Is there a way to configure the channel pipeline to work differently? I've Googled for this and looked through the Netty documentation. I feel like I'm missing something basic and I just am not looking in the right place or searching for the right thing. Thanks for any help.
Is your telnet client reverting to 'old line by line' mode whereby only completed lines are sent to the server (telnet man page)? Try writing a simple Java client to send the message instead.
I guess reading a JSON stream is more akin to reading an HTTP stream, since you will have to keep track of the opening and closing braces (and brackets as well, should the JSON string be an array). If you look at the source for the HTTP decoder, you'll see that it is using a ReplayingDecoder. Using a replaying decoder is not necessary, but it helps a lot if the entire message is split in more than one buffer.
FrameDecoders are meant for reading messages that are "framed" by special characters (hence the name of the decoder) or prepended with a length field.
I would also highly recommend using the DecoderEmbedder helper class so that you can unit test your JSON decoder without doing actual I/O.