I want to implement a POP3 client. I know that in POP3 protocol, each message ends with \r\n. I want to write a recvuntil method in Java, which will be able to receive a whole message at once.
It super easy in Python:
def recv_all_until_ver3(s, crlf):
data = ""
while data[-len(crlf):] != crlf:
data += s.recv(1)
return data
However, when I tried to do the same in Java:
class JavaApplication1 {
/**
* #param args the command line arguments
*/
public static void main(String[] args) throws IOException {
// TODO code application logic here
String serverAddress = "interia.pl";
int port = 110;
Socket s = new Socket(serverAddress, port);
BufferedReader input = new BufferedReader(new InputStreamReader(s.getInputStream()));
String answer = "";
while (!answer.contains("\r\n")) {
answer = input.readLine();
System.out.println(answer);
}
}
}
It receives one message and hands instead of end itself. Is there any way to implement this method in Java? Can I just read characters one by one, instead of whole line until I find \r\n in my message? Thank you.
The Python code reads one octet at a time, immediately appends it to answer and signals it's done if at the current iteration answer ends with CRLF.
The Java code hangs because BufferedReader.readLine() returns the line content after stripping the line termination. At this point answer does not contain CRLF, so readLine() is called once again, and since the stream is not closed from the other end (your program would then throw a NullPointerException) and you have an infinite read timeout (you would see a SocketTimeoutException), it hangs, forever waiting for the next line.
The correct version is
Socket socket = connect(); // Implement this
Scanner scanner = new Scanner(new InputStreamReader(socket.getInputStream(), "ASCII"));
scanner.useDelimiter("\r\n");
String response = scanner.nextLine();
socket.close();
Notes:
always set a timeout on sockets.
you may want to account for multiline messages.
you can use the Scanner class (it accepts an InputStream as a constructor param ) and set the delimiter using useDelimiter method. to read messages that end with \r\n
Related
I have a server that sends a String to a socket client. When I try to get the contents of the String from the client it wouldn't read it.
The server sends via:
output.write(res); output.flush(); // I checked res content and it is all good
and this the Client receiving:
public class Client {
public static void main(String[] args) throws IOException{
Socket connexion = new Socket(InetAddress.getLocalHost(), 33333);
BufferedReader input = new BufferedReader(
new InputStreamReader(connexion.getInputStream(), "8859_1"), 1024);
String res="";
while(input.readLine()!=null){
res += input.readLine()+"\n";
}
System.out.println(res);
}}
Suggestions please ? Thank you
The problem is that you are reading the next line and then disregarding it, and then trying to read the next line again. The second time you read the next line, there is no data because the first read has already consumed it:
while(input.readLine()!=null){
res += input.readLine()+"\n";
}
Try something like the following:
String line = null;
while((line = input.readLine()) != null) {
res += line + "\n";
}
As #JB Nizet mentioned, this is contingent upon the server actually sending a newline character to the client to terminate the message. As stated in the documentation for readLine():
Reads a line of text. A line is considered to be terminated by any one
of a line feed ('\n'), a carriage return ('\r'), or a carriage return
followed immediately by a linefeed.
In my application, there is a separate thread, ran by ScheduledExecutorService.scheduleAtFixedRate() every minute, which parses rss feeds from multiple websites. I am using Apache HttpClient to receive xml.
Sample code:
InputStream inputStream = HTTPClient.get(url);
String xml = inputStreamToString(inputStream, encoding, websiteName);
public static String inputStreamToString(InputStream inputStream, String encoding, String websiteName)
{
BufferedReader bufferedReader = null;
PrintWriter printWriter = null;
StringBuilder stringBuilder = new StringBuilder();
int letter;
try
{
bufferedReader = new BufferedReader(new InputStreamReader(inputStream, encoding));
printWriter = new PrintWriter(new File("src/doclog/"
+ websiteName + "_"
+ new SimpleDateFormat("MM_dd_yyyy_hh_mm_ss").format(new Date(System.currentTimeMillis()))
+ "_" + encoding + ".txt"), encoding);
while((letter = bufferedReader.read()) != -1)
{
char character = (char) letter;
printWriter.print(character);
stringBuilder.append(character);
}
}
catch(IOException e)
{
throw new RuntimeException(e);
}
finally
{
try
{
if(bufferedReader != null)
{
bufferedReader.close();
}
if(printWriter != null)
{
printWriter.close();
}
}
catch(IOException e)
{
e.printStackTrace();
}
}
System.out.println("String built");
return stringBuilder.toString();
}
And HTTPClient class:
public class HTTPClient
{
private static final HttpClient CLIENT = HttpClientBuilder.create().build();
public static InputStream get(String url)
{
try
{
HttpGet request = new HttpGet(url);
HttpResponse response = CLIENT.execute(request);
System.out.println("Response Code: " + response.getStatusLine().toString());
return response.getEntity().getContent();
}
catch(IOException | IllegalArgumentException e)
{
throw new RuntimeException(e);
}
}
}
As the title says, sometimes there is a chance that bufferedReader.readLine() will hang forever. I've seen another answers on this topic, and they suggest to check if bufferedReader.ready() returns true. The problem is that there are websites, which will always return false in bufferedReader.ready(), while processing them, however they parse just fine.
How can i prevent my thread from hanging on bufferedReader.readLine()?
If it matters, response.getStatusLine().toString() always returns HTTP/1.1 200 OK
EDIT
I just found out that bufferedReader.ready() is actually true when hang happens.
EDIT 2
BufferedReader.read() hangs as well. It is strange that hang happens only when dealing with one single website, and it's occurrence is absolutely random. Application either could be working for 15 hours, receiving hundreds of non-problematic responses, or hang just in 10 minutes after launch. I've started to write all characters of every single update into separate file, and found out that nothing special really happens. Xml reading simply stops forever in the middle of document, the last characters were <p dir="ltr"&g. Updated the code.
Also, it's noteworthy to mention that there can't be any unhandled exceptions, because at the highest level of my ScheduledExecutorService.scheduleAtFixedRate() runnable i catch Throwable, and print it's stackTrace.
The ready() method returns true telling you that there are characters available for reading. The problem is that readLine() blocks until it finds an end-of-line in the input.
public String readLine()
throws IOException
Reads a line of text. A line is considered to be terminated by any one
of a line feed ('\n'), a carriage return ('\r'), or a carriage return
followed immediately by a linefeed.
As you are reading from a stream there is no guarantee that the data will come in at line boundaries so the readLine() call blocks.
You can use the read method which will not block, but you will have to check for EOL yourself.
public int read(char[] cbuf, int off, int len) throws IOException
Reads characters into a portion of an array.
This method implements the general contract of the corresponding read
method of the Reader class. As an additional convenience, it attempts
to read as many characters as possible by repeatedly invoking the read
method of the underlying stream. This iterated read continues until
one of the following conditions becomes true:
The specified number of characters have been read,
The read method of the underlying stream returns -1, indicating end-of-file, or
The ready method of the underlying stream returns false, indicating that further input requests would block.
If the first read on the underlying stream returns -1 to indicate
end-of-file then this method returns -1. Otherwise this method returns
the number of characters actually read.
Also you will have to reconstruct the line from the characters read. It is not ss convenient as reading the entire line at once but it is the way it must be done.
Server Side:
try {
ServerSocket server_socket=new ServerSocket(port_number);
Socket client_socket= server_socket.accept();
PrintWriter output = new PrintWriter(client_socket.getOutputStream(),true);
BufferedReader input = new BufferedReader(new InputStreamReader(client_socket.getInputStream()));
BufferedReader stdIn=new BufferedReader(new InputStreamReader(System.in));
String userInput, clientOutput;
while ((userInput=stdIn.readLine())!="EXIT") {
if ((clientOutput=input.readLine())!=null) {
System.out.println("Client: "+clientOutput);
} if (userInput!=null) {
output.println(userInput);
output.flush();
}
}
}
Client Side:
try {
Socket client_socket= new Socket(hostname,port_number);
PrintWriter output = new PrintWriter(client_socket.getOutputStream(),true);
BufferedReader input = new BufferedReader(new InputStreamReader(client_socket.getInputStream()));
BufferedReader stdIn=new BufferedReader(new InputStreamReader(System.in));
String userInput,serverOutput;
while ((userInput=stdIn.readLine())!="EXIT") {
if ((serverOutput=input.readLine())!=null) {
System.out.println("Server: "+serverOutput);
} if (userInput!=null) {
output.println(userInput);
output.flush();
}
}
}
The code in my case makes sense to me, I cant seem to figure out why an enter still needs to be pressed, does it have something to do with .readLine()?
I checked out the following post Server Client in Java only displays message when I press enter, however the solution provided does not fix the situation.
Note: Initially there were no if statements in the while loop. The way I saw this to be an issue was that the while loop may get stuck on one of the lines, waiting for user/server input. Therefore implementing if statements allowed it to skip the waiting portion and re-run the loop.
Turns out I mixed my variables up.
The while loops should be:
while ((userInput=input.readLine())!="EXIT") {
It fixed it, but there are some other issues still present
does it have something to do with .getLine()?
Yes. If you look at the Javadoc for BufferedReader#readLine(), it clearly states that an end of line character terminates the String to be read:
Reads a line of text. A line is considered to be terminated by any one of a line feed ('\n'), a carriage return ('\r'), or a carriage return followed immediately by a linefeed.
I am writing a multithread chat server/client, I have been using SocketTest V 3 to test the server and it seems to be working fine but the client I have written only updates in the console when I write a new line, I am using my own client up against the socket test, and the socket updates every time something is written but my client does not
public class clientV2 {
public static final int PORT = 5019;
public static InetAddress host;
public static void main(String[] args) throws IOException {
try {
host = InetAddress.getLocalHost();
Socket socket = new Socket(host, PORT);
Scanner in = new Scanner(System.in);
Scanner inputFromServer = new Scanner(socket.getInputStream());
PrintWriter outputToServer = new PrintWriter(socket.getOutputStream());
while(true) {
if(inputFromServer.hasNext()) {
System.out.println(inputFromServer.nextLine());
}
String input = in.nextLine();
outputToServer.println(input);
outputToServer.flush();
}
} catch (Exception e) {
}
}
}
Your client calls nextLine() on its scanner, and this method, as it names indicates, returns the next line. So your client can't print anything until the complete next line is available.
Here's what the javadoc says about nextLine():
Advances this scanner past the current line and returns the input that
was skipped. This method returns the rest of the current line,
excluding any line separator at the end. The position is set to the
beginning of the next line.
Since this method continues to search through the input looking for a
line separator, it may buffer all of the input searching for the line
to skip if no line separators are present.
This is because System.out.println(inputFromServer.nextLine()); does exactly that. It waits for a full line and then prints that out. It will not print partial lines.
If the output is missing more than just the last partial line (line-break to line-break; line wraps don't count), look for the buffers along the way.
You can use an InputStreamReader to read single characters from the input stream (which is a byte stream). You can specify the charset in the constructor.
InputStreamReader inputFromServer =
new InputStreamReader(socket.getInputStream(), "UTF-8");
System.out.print((char) inputFromServer.read());
I am building a simple client-server program , I have in main :
FTPClient ftp = new FTPClient("www.kernel.org");
ftp.getReply();
ftp.sendCommand("USER " + "anonymous");
ftp.getReply();
ftp.sendCommand("PASS " + "anonymous");
ftp.getReply();
String com="";
while (!com.equalsIgnoreCase("quit")){
System.out.println("Enter your Commands . or Enter quit");
BufferedReader Keyboard = new BufferedReader(new InputStreamReader(System.in));
com = Keyboard.readLine();
ftp.sendCommand((com));
ftp.getReply();
System.out.println("===============");
}
ftp.close();
the problem is in the getReply() function, this function is :
public void getReply() throws IOException {
String line="";
while (br.ready())
{
line = br.readline();
System.out.println(line);
System.out.flush();
}
}
br is a BufferedReader.Now all the problem is that when the program starts it doesn't show the welcome message from the Server until I press Enter or any command, when I Debug the program Step by Step every thing is working perfectly.So is the problem in the readline and I should use something else or what?
The problem is likely that the end of the server response does not contain a newline character. The BufferedReader's readLine method will block until a line of data is received, where "a line" consists of some characters followed by a newline character (or the end of the stream). Consequently, the readLine call will not return if no newline is received.
In this situation then, the BufferedReader isn't doing you any good. You'd be better off using the underlying Reader yourself, reading into an array and emitting the output as soon as it comes in, such as the following:
final char[] buffer = new char[256]; // or whatever size you want
int nRead;
while ((nRead = reader.read(buffer)) != -1)
{
System.out.println(new String(buffer, 0, nRead));
System.out.flush();
}
The condition in the while loop there might look confusing if you're not used to it before, but it combines the read operation (which reads into the buffer) with the check that the end of the stream has not been reached. Likewise, the construction of the String within the while loop takes into account the fact that the buffer may not have been filled entirely, so only as many characters as were supplied are used.
Note that this particular snippet keeps looping until the stream is empty; you may wish to add another exit condition in your particular case.