Strange behavior in a simple chat using Java - java

I started to study java last month, and now I'm trying to write a simple chat program but I encountered something strange, and I was curious for the reason behind it.
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;
public class Server {
public static void main(String[] args) throws IOException {
String text = "";
ServerSocket ss = new ServerSocket(21025);
while (true){
System.out.println("Waiting...");
Socket s1 = ss.accept();
System.out.println("Connection accepted from "+s1.getInetAddress());
PrintStream pout = new PrintStream(s1.getOutputStream());
pout.println("Connected to the server");
new Thread(new Ricevitore(s1)).start();
}
}
}
public class Ricevitore implements Runnable {
String text = "";
Socket skt;
public Ricevitore(Socket skt){
this.skt = skt;
}
#Override
public void run() {
while (!text.equalsIgnoreCase("end")) {
try {
InputStream in = skt.getInputStream();
BufferedReader br = new BufferedReader(new InputStreamReader(in));
text = br.readLine();
if (!text.equalsIgnoreCase("end"))
System.out.println(text);
}
catch (IOException e){}
}
}
}
public class Client {
public static void main(String[] args) throws IOException {
//Create a socket
try (Socket s = new Socket("127.0.0.1", 21025)) {
String text="";
while(!text.equalsIgnoreCase("end")) {
//Allows messages from server
InputStream in = s.getInputStream();
BufferedReader br = new BufferedReader(new InputStreamReader(in));
if (br.ready()) {
Scanner server = new Scanner(br);
String testoServer = br.readLine();
System.out.println(testoServer);
}
//Allows to send text to the server
OutputStream out = s.getOutputStream();
PrintStream pout = new PrintStream(out);
Scanner tastiera = new Scanner(System.in);
text = tastiera.nextLine();
pout.println(text);
}
}
}
}
This is the complete program for the moment, my question is this: Since I wanted to avoid printing the word "end" to close the program, I inserted
if (!text.equalsIgnoreCase("end"))
but after that the server does not display the message "connected to server" unless I first input something through the client.
If I comment out that if statement, both messages "Connection accepted" and "Connected to server" got printed at the same time as intended.
I don't know if my question is clear, and I'm rather interested in learning why something like this happens.
If there are other things which any of you think is wrong I'll be happy to here about them.

I have only a wild guess which looks probable though.
In your client you read the message from the server if br.ready() returns true. It may happen that this function returns false, and the client goes to waiting for user's input.
After you send a message from the client to the server, the client repeats the test and now gets the message from the server.
I cannot explain why removing if (!text.equalsIgnoreCase("end")) in server's code makes the issue go away. That line isn't even executed until you send a message from the client.
So I think it's just a coincidence. There are two processes involved, and the outcome depends on how fast code executes in either process.
I ran your example many times, and once I did not receive the greeting from the server even though the if above was in its place.
A general suggestion for your code: you don't need to create input/output streams as well as Scanner on each iteration, you should do it only once.
To end communication session, you can just close PrintStream in your client as soon as you receive end from user. Your server will get null from br.readLine(). At this point, you close br and complete run().

Related

BufferedReader readLine() blocking until buffer is full

I'm writing a client/server pair of applications. The server runs multiple threads that collect data and adds it to a BlockingQueue. The socket code loops over the queue and sends whatever data it finds to the client. The data is a string and I append a line separator so that the client can read it using BufferedReader.readLine().
My problem is that instead of readLine() returning on each line that's available it waits until the entire buffer is full before spitting out all the complete lines in the buffer. With the default 8K buffer this means I get data via the client in 8K chunks, which is highly undesirable. I've attached MRE code that represents this. I have confirmed via logging in my actual application that the BufferedWriter is writing the data as soon as it's available from the queue, but to be honest I don't know if the delay is coming after this on the sending side, or is truly on the reading side. If you run the MRE you'll see that the data is displayed approximately 170 lines at a time by the client.
I've searched online for this phenomenon for a couple of days and the one snippet that I could find of a similar issue suggests that maybe it's something to do with the underlying InputStreamReader and/or StreamDecoder, but that is starting to get beyond my expertise. (See this link)
So my question is whether I'm implementing the BufferedReader correctly and how can I resolve the issue I'm seeing so that I get each incoming line without unnecessary delays.
package serverTest;
import java.util.concurrent.BlockingQueue;
public class ServerTest {
public static void main(String[] args) {
int port = 54321;
ServerSocketComms server = new ServerSocketComms(port);
BlockingQueue<String> queue = server.getQueue();
new Thread(server).start();
ClientSocketComms client = new ClientSocketComms("localhost", port);
new Thread(client).start();
for(int i = 0; i < 1000; i++) { // should give about 10 seconds of output
try {
queue.put("" + i + " - All work and no play makes Jack a dull boy");
// Slow things down enough to show what's happening
Thread.sleep(10);
// 48 characters should fill the 8K buffer in approximately 2 seconds
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
package serverTest;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
public class ServerSocketComms implements Runnable {
private final BlockingQueue<String> queue = new LinkedBlockingQueue<>();
private final int port;
public ServerSocketComms(int port) {
this.port = port;
}
#Override
public void run() {
// Open server socket and wait for connection
try {
ServerSocket serverSocket = new ServerSocket(port);
Socket socket = serverSocket.accept();
// Continually loop over blocking data queue until stopped
BufferedWriter dataOut = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
while(socket.isConnected()) {
dataOut.write(queue.take());
dataOut.newLine(); // delimit strings with a line separator
}
// Loop never exits because client socket never completes because of BufferedReader issue
// so sockets never close and application never terminates
socket.close();
serverSocket.close();
} catch (IOException | InterruptedException e) {
e.printStackTrace();
}
}
public BlockingQueue<String> getQueue() {
// Return a reference to the sending queue to be populated by other threads
return this.queue;
}
}
package serverTest;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.Socket;
public class ClientSocketComms implements Runnable {
private final String server;
private final int port;
public ClientSocketComms(String server, int port) {
this.server = server;
this.port = port;
}
#Override
public void run() {
// Open socket to server and wait for incoming data
try {
Socket socket = new Socket(server, port);
BufferedReader dataIn = new BufferedReader(new InputStreamReader(socket.getInputStream()));
// Continually loop over incoming data until stopped
String data;
while((data = dataIn.readLine()) != null) {
// Should print out every line as it's received,
// but instead waits until buffer is full
// (outputs about 170 lines at a time)
System.out.println(data);
}
// Close socket and thread will die
// (but loop never ends because buffer doesn't get completely refilled)
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
Your server is using a BufferedWriter:
BufferedWriter dataOut = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
This one does the buffering that you do not like. It seems the default buffer size is the 8k that you are seeing although that is not documented in the API and could change. Try flushing the buffer using dataOut.flush() if at some point in time you want to ensure everything stored in the buffer so far is sent out to the client immediately. Have a look at the BufferedWriter API for details.
BTW, I have not checked whether there are any other problems in your code. But the above is definitely one.

Java While(boolean) how this code work?

This is the code from lesson on Udemy. I don't understand how the boolean stop; work in this while loop. I don't see any changes of stop inside the loop.
Can some one explain me please how while loop work in this particular case.
package socket;
import java.io.*;
import java.net.*;
public class MultiUserService
{
public static void main(String [] args) //throws Exception
{
try
{
ServerSocket serverSocket = new ServerSocket(9090);
System.out.println("wainting for clients...");
boolean stop = false;
while(!stop)
{
Socket socket = serverSocket.accept();
PrintWriter out = new PrintWriter(socket.getOutputStream(), true);
out.println("Hello client!");
BufferedReader input = new BufferedReader(new InputStreamReader(socket.getInputStream()));
String clientInput = input.readLine();
System.out.println(clientInput);
input.close();
out.close();
socket.close();
}
serverSocket.close();
} catch (Exception e)
{
System.out.println(e.toString());
}
}
}
Indeed, there is no change of stop in the loop, which means the loop won't ever stop, unless an exception is thrown.
Since it is a server, listening for sockets, this is an expected behavior: the server keeps listening and serving clients.
Note that in production code, there should be a way to stop the server, which, depending on the approach, may or may not require actually stopping that loop (there are asynchronous ways available).
That's the point; once it starts, the loop won't stop unless something goes very wrong.

Send text to serversocket after connecting to it

I have a problem that I can't send text to ChatServer. My code:
...
try {
socket.connect(address);
System.out.println("Successfully connected to server!");
Thread fromServerToConsole = new TelnetThread(socket.getInputStream(), System.out);
Thread fromConsoleToServer = new TelnetThread(System.in, socket.getOutputStream());
fromConsoleToServer.setDaemon(true);
fromServerToConsole.start();
fromConsoleToServer.start();
fromServerToConsole.join();
socket.close();
} catch() ...
Where in this try block should I put something like socket.getOutputStream().write("mystring".bytes()) if I want to send this text "mystring" to the SocketServer after connecting to it?
I tried to put this everywhere and it always didn't work, to send "mystring" I always had to press Enter in console of this client (console is for sending messages from console to the server). And the "mystring" was also printed to client's console, but it has to be printed only on the server's side.
I need to send "mystring" not from console, but automatically after connecting socket to the server.
Please help. Thanks.
You should try to flush() the stream after writing, it may help.
As far as i know, telnet sends every singly received bytes back as an echo, so if you don't want to display your echo, you should retrieve the inputstream of the connection, and drop all the echoes manually.
It seems like you are overdoing it a bit. I would recommend a simpler and more efficient method that doesn't have as much overhead, such as using a plain socket.
Client Snippet:
public static void main(String[] args) {
String ip = "Your IP/Hostname";
//set your port
int port = 7391;
Socket s = new Socket(ip, port);
DataOutputStream out = new DataOutputStream(new BufferedOutputStream(s.getOutputStream()));
s.writeUTF("String To Write To SocketServer");
s.flush();
out.close();
s.close();
}
Server Snippet:
public static void main(String[] args) {
ServerSocket s = new ServerSocket(7391);
while (true) {
new SocketWorker(s.accept()).start();
}
}
public static class SocketWorker extends Thread {
Socket s;
public SocketWorker(Socket s) {
this.s = s;
}
#Override
public void run() {
DataInputStream in = new DataInputStream(new BufferedInputStream(s.getInputStream()));
//Receive String sent via the client
String response = in.readUTF();
in.close();
s.close();
}
}
Hopefully this helps!
Edit: You will also have to handle Exceptions. I didn't add any handling obviously.

Java, Telnet, check for String in an InputStream

I am fairly raw. I am trying to write a Java class to interact with Telnet. I saw that Apache Commons and Jsacpe had APIs. I am using Jscape's Sinetfactory. The Telnet I am connecting to sends a prompt to enter 'User name?:' as soon as telnet.connect() occurs. I am required to verify that this prompt is actually happening so I do not just write the answer when something else may happen. I am inexperienced with this and am sure there is a simple answer, just wondering if anyone might be able to help.
Here is what I have, its a bit sloppy because I've been playing around for awhile not sure how to actually read the last characters from the stream.
import com.jscape.inet.telnet.*;
public class TelnetTest extends TelnetAdapter {
private final static String USER = "xxx\r";
private final static String PWORD = "yyy\r";
private final static String COMMAND = "zzz\r";
private final static byte[] USER_BYTE = USER.getBytes();
private final static byte[] PWORD_BYTE = PWORD.getBytes();
private final static byte[] COMMAND_BYTE = COMMAND.getBytes();
private Telnet telnet = null;
private OutputStream output = null;
private static BufferedReader reader = null;
private boolean connected = false;
private String hostname = "qqq";
//TelnetInputStream tis = null; NOT IN USE AS OF NOW
public TelnetTest() throws IOException, TelnetException {
// create new Telnet instance
telnet = new Telnet(hostname);
// register this class as TelnetListener
telnet.addTelnetListener(this);
// establish Telnet connection
telnet.connect();
connected = true;
output = telnet.getOutputStream();
// HERE IS WHERE I NEED HELP, NOT SURE HOW TO CHECK STREAM
String str = null;
if ((str = reader.readline()).equals("User name?:")) {
telnet.getOutputStream().write(USER_BYTE);
}
// SAME CHECK WOULD HAPPEN HERE FOR "Password"
telnet.getOutputStream().write(PWORD_BYTE);
// ANOTHER SIMILAR CHECK HERE
telnet.getOutputStream().write(COMMAND_BYTE);
// sends all data entered at console to Telnet server
String input = null;
while ((input = reader.readLine()) != null) {
if (connected) {
((TelnetOutputStream) output).println(input);
} else {
break;
}
}
}
public boolean streamContainsString(Reader reader, String searchString)
throws IOException {
Scanner streamScanner = new Scanner(reader);
if (streamScanner.findWithinHorizon(searchString, 0) != null) {
return true;
} else {
return false;
}
}
// Invoked when Telnet socked is connected.
public void connected(TelnetConnectedEvent event) {
System.out.println("Connected");
}
// Invoked when Telnet socket is disconnected. Disconnect can
public void disconnected(TelnetDisconnectedEvent event) {
connected = false;
System.out.print("Disconnected. Press enter key to quit.");
}
// Invoked when Telnet server requests that the Telnet client begin performing specified TelnetOption.
public void doOption(DoOptionEvent event) {
// refuse any options requested by Telnet server
telnet.sendWontOption(event.getOption());
}
// Invoked when Telnet server offers to begin performing specified TelnetOption.
public void willOption(WillOptionEvent event) {
// refuse any options offered by Telnet server
telnet.sendDontOption(event.getOption());
}
// Invoked when data is received from Telnet server.
public void dataReceived(TelnetDataReceivedEvent event) {
// print data recevied from Telnet server to console
System.out.print(event.getData());
}
public Telnet getTelnet() {
return telnet;
}
// starts console program
public static void main(String[] args) {
try {
// create BufferedReader to read data from console
reader = new BufferedReader(new InputStreamReader(System.in));
// create new TelnetExample instance
TelnetTest example = new TelnetTest();
} catch (Exception e) {
e.printStackTrace(System.out);
}
}
}
If you are reading/writing Strings then you should always use Reader and Writer. BufferedReader allows you to do line operations. So a BufferedReader wrapped around an Reader (around a InputStreamReader) will allow you to do a readLine() call to get the line of input from the connection:
BufferedReader reader =
new BufferedReader(new InputStreamReader(telnet.getInputStream()));
To write to the connection you would use a Writer around a OutputStreamWriter:
Writer writer = new OutputStreamWriter(telnet.getOutputStream()));
I'm not sure if that works with the stream from Telnet but it works with a raw Socket. You then could do something like the following pseudo code:
while (true) {
read a line from the server
some sort of if/then/else to test for the output
write your username/password or whatever is appropriate for the connection
repeat until some logout or IOException...
}
The Apache Telnet class has a number of interesting listeners and other handlers which you could use if you wanted to but the learning curve may be more. Here's a good sample application using TelnetClient:
http://www.java2s.com/Code/Java/Network-Protocol/ExampleofuseofTelnetClient.htm

Java multiplayer server blocking io

I am currently developing a prototype for a game and I need a simple server to run it.
At this stage, I don't want to invest the time learning about all the different full-featured multiplayer game servers already there (smartfox, etc...)
I know how to develop a basic Server with Threads listening on Sockets but I have ran into a roadblock. Here's the run() function of the Thread
public void run() {
try {
out = new PrintWriter(mSocket1.getOutputStream(), true);
in = new BufferedReader( new InputStreamReader( mSocket1.getInputStream() ) );
String inputLine1 = null, outputLine;
out.println("hello");
out.flush();
while( (inputLine1 = in.readLine()) != null) {
outputLine = mGameControl.processInput(mPlayerNum, inputLine1);
out.println(outputLine);
out.flush();
if(outputLine.contentEquals("bye"))
break;
}
Terminate();
}
catch(IOException e) { e.printStackTrace(); }
}
Now my problem is that the thread is blocked waiting for input. I do have other similar Threads connected to other client which may result in information being dispatched to all clients...
How can I modify it so that a different Thread can interact with it and push info to the client?
Just write a synchronised public method which writes to your PrintWriter, and allow other threads to use it to send messages to your client. Call the same method from your read loop to avoid two threads writing at the same time.
Here's a tested example:
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
public class SocketTest {
public static class Client implements Runnable {
private final BufferedReader in;
private final PrintWriter out;
public Client(Socket clientSocket) throws IOException {
out = new PrintWriter(clientSocket.getOutputStream(), true);
in = new BufferedReader( new InputStreamReader( clientSocket.getInputStream() ) );
}
public void run() {
send("Hello");
String inputLine1 = null, outputLine;
try {
while( (inputLine1 = in.readLine()) != null) {
outputLine = inputLine1.toLowerCase();
System.out.println(inputLine1);
send(outputLine);
if(outputLine.contentEquals("bye"))
break;
}
} catch (IOException e) {
e.printStackTrace();
}
}
public synchronized void send(String message) {
out.println(message);
out.flush();
}
}
public static void main(String[] args) throws IOException, InterruptedException {
ServerSocket s = new ServerSocket(5050);
Socket clientSocket = s.accept();
Client client = new Client(clientSocket);
Thread clientThread = new Thread(client);
clientThread.start();
int i = 1;
while (true) {
Thread.sleep(1000);
client.send("Tick " + (i++));
}
}
}
Use Netty to handle your connections and query treatments. Since I discovered that project, I never touched sockets directly anymore (except when writing C programs, etc.)
There are actually some examples to look at and the documentation is quite extensive. The project is very well alive since a couple of years already, and is not soon to die! There's a fairly large user base behind it.
If you only need to perform a non-blocking read on the socket, one of the simplests ways is to use available(). Call available and if there is data to read (bytes pending > 0), perform the read, any way, wait using Thread.sleep() and try to read again. This allow the thread to do while waiting for input data and cat react to external signals.
The use of selectors is encouraged when you need to perform high-performance non-blocking read using one thread and several sockets (java.nio.channels.Selector).

Categories