I'm learning distributed systems basics and currently I'm trying to do a simple yet realistic messenger between one server and one client. What I do intend is that on each endpoint socket side (Server and Client) text automatically updates (like a real "messaging app"). In other words, I want that the moment I write and "send" the message, it automatically appears on recipient side. What I have now follows this schema:
I send a message (let's assume from client)
To see that message on Server's side I need to reply first (because Server's BufferedReader / Client's PrintWriter is only read after asking for the answer)
My code:
public class ClientSide {
public static void main(String [] args){
String host_name = args[0];
int port_number = Integer.parseInt(args[1]);
try {
Socket s = new Socket(host_name, port_number);
PrintWriter out =
new PrintWriter(s.getOutputStream(), true);
BufferedReader in =
new BufferedReader(
new InputStreamReader(s.getInputStream()));
BufferedReader stdIn =
new BufferedReader(
new InputStreamReader(System.in));
String answer;
while ((answer = stdIn.readLine()) != null) {
out.println(answer);
System.out.println("\nlocalhost said\n\t" + in.readLine());
}
} catch (IOException ex) {
Logger.getLogger(ClientSide.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
public class ServerSide {
public static void main(String [] args){
int port_number = Integer.parseInt(args[0]);
try {
ServerSocket ss = new ServerSocket(port_number);
Socket tcp = ss.accept();
PrintWriter out =
new PrintWriter(tcp.getOutputStream(), true);
BufferedReader in =
new BufferedReader(
new InputStreamReader(tcp.getInputStream()));
BufferedReader stdIn =
new BufferedReader(
new InputStreamReader(System.in));
String answer;
while ((answer = stdIn.readLine()) != null){
out.println(answer);
System.out.println("\nClient said\n\t" + in.readLine());
}
} catch (IOException ex) {
Logger.getLogger(ServerSide.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
How can I do this? Does it involve advanced knowledge on the matter?
Thanks in advance.
The core problem is that you want to wait for two events concurrently -- either a message from the socket, or input from the user!
You want to wait on both at the same time -- you don't want to be stuck waiting for a message in the socket if the user types a message; nor to be waiting for the user message while you have a new message from the network.
To 'wait' for messages from multiple streams, you have java.nio. I believe it is the most correct way of doing it.
But if you want to keep using the BufferedReader, there is a ready() method that returns true if and only if there is a message waiting to be read.
Your code after the in and stdIn declarations would then look something like (I didn't test it!!):
while(true) {
if(stdIn.ready()) {
System.out.println("I said " + stdIn.readLine());
}
if(in.ready()) (
System.out.println("He said " + in.readLine());
}
}
A few somewhat useful random links:
Java - Reading from a buffered reader (from a socket) is pausing the thread
Is there epoll equivalent in Java?
Related
Here is my code for the server side:
#Override
public void run(){
String message;
String command;
String[] arguments;
try{
BufferedReader inStream = new BufferedReader(
new InputStreamReader(
clientSocket.getInputStream()));
while(online){
message = inStream.readLine();
if(message == null)
continue;
if(message.charAt(0) == '/'){
int endOfCommandIndex = message.indexOf(' ');
command = message.substring(1, endOfCommandIndex);
arguments = message.substring(endOfCommandIndex + 1).split(" ");
if(command.equals("login")){
setUserName(arguments[0]);
setName(arguments[0]);
sendMessage(this, "Connected");
}
//....
}
}
}
As mentioned in the title, the thread gets stuck reading from the InputStream of the Socket (I checked with JDB and it's not a conditional waiting because it appears to be still "running").
I tried to write a line to the socket but it doesn't change its state at all. I'm trying to build a chat-like local application and I'm quite new to socket and streams. Thanks in advance.
For the client side:
String msg;
try{
while(!((msg = stdIn.readLine()).equals("/quit")))
toServer.println(msg);
}
catch(IOException e){
e.printStackTrace();
}
In case someone wants review my entire code, it is here hosted on github
It looks like the message is never flushed after being written into the socket stream.
Try either call:
toServer.flush();
after println, or enable auto flushing when constructing toServer:
toServer = new PrintWriter(socket.getOutputStream(), true);
I've been making a chat room where multiple clients can connect and talk together on the same server. The only problem I'm having is getting each client to send more than one message. I've been trying different ways of looping the method to do so but I'm having some issues.
Any help would be appreciated :) thank you.
HERE'S THE CODE:
public class Client {
public static void main(String[] args){
Scanner clientInput = new Scanner(System.in);
try {
Socket SOCK = new Socket("localhost", 14001);
System.out.println("Client started!");
//Streams
while(true){
OutputStream OUT = SOCK.getOutputStream(); //writing data to a destination
PrintWriter WRITE = new PrintWriter(OUT); // PrintWriter prints formatted representations of objects to a text-output stream
InputStream in = SOCK.getInputStream(); //reads data from a source
BufferedReader READ = new BufferedReader(new InputStreamReader(in));
//---------------------------------
System.out.print("My input: ");
String atServer = clientInput.nextLine();
WRITE.write(atServer + "\n");
WRITE.flush(); //flushes the stream
String stream = null;
while((stream = READ.readLine()) != null){ //if stream is not empty
System.out.println("Client said: " + stream);
}
READ.close();
WRITE.close();
}
} catch (UnknownHostException e){
e.printStackTrace();
}
catch (IOException e) {
e.printStackTrace();
}
}
}
I've tried using a while loop to continuously ask for an input but doesn't seem to be working.
Are you making it out of the READ.readLine() while loop? Perhaps you're never getting an end of input character and thats never terminating. Also, you're closing both your READ and WRITE at the end of the while loop, and then expect them to be open on the next iteration. Move those and the close statements to the same layer as the Socket.
With that, every time you send something, your client is expecting something in response from the server. If you don't want them to be dependent on each other, I recommend moving the receive logic to its own thread in a while(true) loop.
I am attempting stream data over a socket with Java in an attempt to write a Kafka producer. I've written a class to pull the data in but I'm not getting the results I'd expect. I've got it set up so the data is being streamed from a Linux box. The source of the data is a csv file that I'm using the nc utility to stream. The class is running on a Windows 10 machine from Eclipse. When I run the class I see two weird things.
The column headers don't get transmitted.
I can only run the class once. If I want to run it again, I have to stop nc and restart it.
Below is my code. Am I missing anything? At this point I'm just trying to connect to the socket and pull the data over.
I run nc with the following command:
$ nc -kl 9999 < uber_data.csv
Below is my class
import java.net.*;
import java.io.*;
public class Client
{
static String userInput;
public static void main(String [] args)
{
try
{
InetAddress serverAddress = InetAddress.getByName("servername");
Socket socket = new Socket(serverAddress, 9999);
BufferedReader input = new BufferedReader(new InputStreamReader(socket.getInputStream()));
while ((userInput = input.readLine()) != null) {
System.out.println(input.readLine());
}
input.close();
socket.close();
}
catch(UnknownHostException e1)
{
System.out.println("Unknown host exception " + e1.toString());
}
catch(IOException e2)
{
System.out.println("IOException " + e2.toString());
}
catch(IllegalArgumentException e3)
{
System.out.println("Illegal Argument Exception " + e3.toString());
}
catch(Exception e4)
{
System.out.println("Other exceptions " + e4.toString());
}
}
}
You're throwing away every odd-numbered line. It should be:
while ((userInput = input.readLine()) != null) {
System.out.println(userInput);
}
Secondly, you aren't closing the socket. Use a try-with-resources:
try
{
InetAddress serverAddress = InetAddress.getByName("servername");
try (
Socket socket = new Socket(serverAddress, 9999);
BufferedReader input = new BufferedReader(new InputStreamReader(socket.getInputStream()));
) {
while ((userInput = input.readLine()) != null) {
System.out.println(input.readLine());
}
}
}
catch (...)
First, each call readLine() tries to read line from input stream.
In userInput = input.readLine() you read header, but println(input.readLine()) read body and print in console.
while ((userInput = input.readLine()) != null) {
System.out.println(userInput); //instead input.readLine()
}
Second, I didn't use nc, but I think problem will solve if you will close socket (and reader) in finally statement.
I hope it would be helpful.
For the first question: you were trying to print userInput string. But it's printing the result of another readline() call.
For the second: after the file has been transferred, you have to stop and restart nc; no matter what you do from your side. It's from nc side.
See the nc documentation.
My program is basically:
Client sends a String to Server,
Based on this String, Server is creating an ArrayList,
ArrayList is sent back to the Client.
What is failing here is:
After Client sends a String, the Server receives it and doesn't do anything else. In this time Client keeps on working and gets a NullPointer.
Client side:
public static ArrayList<String> sendStringToServer(String report) {
Socket socket;
ArrayList<String> fieldsList = new ArrayList<String>();
try {
socket = new Socket("localhost", 2345);
OutputStream os = socket.getOutputStream();
PrintStream ps = new PrintStream(os, true);
ps.println(report);
ps.flush();
//Here the debugger should stop and wait for server to create a List
//at this point there is no answer, code breaks
ObjectInputStream objectInput = new ObjectInputStream(socket.getInputStream());
Object object = objectInput.readObject();
fieldsList = (ArrayList<String>) object;
socket.close();
return fieldsList;
} catch (IOException e1) {
e1.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
Server side:
public class Server {
private ServerSocket serverSocket;
private Socket clientSocket;
private String telegram;
private StringBuilder telegramSB;
public static void main(String[] args) throws IOException, JRException {
new Server();
}
public Server() {
try {
serverSocket = new ServerSocket(2345);
while (true) {
clientSocket = serverSocket.accept();
InputStream is = clientSocket.getInputStream();
InputStreamReader isr = new InputStreamReader(is);
BufferedReader br = new BufferedReader(isr);
try {
//debugger goes to here and then stops
telegram = br.readLine();
int counter = 0;
boolean startSeq = false;
for (char ch : telegram.toCharArray()) {
if (counter == 0 && ch == '/') {
startSeq = true;
}
if (startSeq == true) {
telegramSB = new StringBuilder();
telegramSB.append(ch);
}
if (ch == '\n') {
if (telegram.length() < 255) {
sendListWithFields();
} else {
new Launcher(telegram).run();
}
}
counter++;
}
} catch (JRException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
} catch (IOException e) {
System.out.println(e);
}
}
My guess here would be that the BufferedReader is waiting to fill its buffer and you haven't sent enough data for it to do that and return so its waiting for more data to come through that never does (because your clients stops writing and starts to read). You could test this theory temporarily by dumping a load more data into the OutputStream on the client and flushing it.
If the above is the case then you probably want to not use BufferedReader but you have other issues here which also mean you probably want to avoid using PrintStream and BufferedReader for communication and serialisation anyway. For example the default character encoding on two different machines and JVMs could be different. When you create your PrintStream and InputStreamReader you don't specify a character encoding so they could end up being mismatched and the string that you write (including the newline character) could end up being understood completely differently by the remote side, this could also be a reason why its blocking (the client side encodes the newline character in one way but the server is expecting it to be encoded a completely different way), though less likely I think .
If you don't have to use PrintStream then I would suggest instead using DataOutputStream / DataInputStream:
//Client
BufferedOutputStream bufout = new BufferedOutputStream(socket.getOutputStream());
DataOutputStream dout = new DataOutputStream(bufout);
dout.writeUTF(report);
dout.flush();
//Server
BufferedInputStream bufin = new BufferedInputStream(socket.getInputStream());
DataInputStream din = new DataInputStream(bufin);
String report = din.readUTF();
You still get buffering from the BufferedIn/OutputStreams so it will be performant but the DataIn/OutputStreams will manage termination of variable length objects for you - they will send a length prefixing the string to tell the other side exactly how many bytes to read, so you don't need to use a special character to terminate the string you wrote, and this also means it doesn't matter what the content of your String is. In your example above even if it was working if your String had a newline character in it the server would read up until that first newline character, not to the end of the string you sent and that would put them out of sync for the next send/receive along that stream.
Using write/readUTF also specifies an encoding (UTF-8) so there is no mismatch there either.
I have developed a client-server game (called "SET") in java.
During the debugging process I ran into a very awkward problem:
If i run both the client and the server on the same machine (client connects to localhost), the game works fantastically (also if i run the server and a lot of clients).
But, if i run the client and the server on 2 separate machines, then both the client and the server hang in thir Inputstream readLine method.
I will mention that i'm using writeBytes method to write the data, but i always finish a data line with \n (as mentioned, the systems works perfectly on 1 machine !)
The architecture of the system is as follows:
public SetServer(){
this.deck = initCardsList();
Collections.shuffle(this.deck);
shownCards = new Card[12];
for(int i = 0; i<12; i++){
Card c = this.deck.removeFirst();
shownCards[i]=c;
}
while(!isSetOnTable()){
Card c = this.deck.removeFirst();
this.deck.addLast(shownCards[0]);
shownCards[0]=c;
}
playersQueue = new LinkedList<String>();
clients = new LinkedList<ServerOperation>();
try{
ServerSocket welcomeSocket = new ServerSocket(6789);
while(true)
{
if(currNumOfPlayers<5){
System.out.println("Waiting for connection...");
Socket connectionSocket = welcomeSocket.accept();
String line = (new BufferedReader(new InputStreamReader(connectionSocket.getInputStream()))).readLine();
currNumOfPlayers++;
playersQueue.addLast(line);
ServerOperation client = new ServerOperation(connectionSocket,this, line);
clients.add(client);
Thread t = new Thread(client);
t.start(); //<--- This thread listens to client's request
notifyPlayersAdded(line,new DataOutputStream(connectionSocket.getOutputStream())); //<-----This method sends 3 lines of data
}
}
}
catch(Exception e){
System.out.println(e.getMessage());
}
}
The ServerOperation's run method:
public void run(){
if(socket==null){
return;
}
else{
try{
// this.out.writeBytes("NewConnection:" + this.parent.STATUS+"\n");
// this.parent.notifyCards();
while(true){
String line = this.br.readLine();
String command = line.split(":")[0];
if(command.equals("SetRequest")){
String[] cards = line.substring(line.indexOf(':')+1).split(" ");
Card c1,c2,c3;
c1 = new Card(
CardShape.values()[(int)(cards[0].charAt(3)-'0')],
CardNumber.values()[(int)(cards[0].charAt(2)-'0')],
CardFill.values()[(int)(cards[0].charAt(1)-'0')],
CardColor.values()[(int)(cards[0].charAt(0)-'0')]);
c2 = new Card(
CardShape.values()[(int)(cards[1].charAt(3)-'0')],
CardNumber.values()[(int)(cards[1].charAt(2)-'0')],
CardFill.values()[(int)(cards[1].charAt(1)-'0')],
CardColor.values()[(int)(cards[1].charAt(0)-'0')]);
c3 = new Card(
CardShape.values()[(int)(cards[2].charAt(3)-'0')],
CardNumber.values()[(int)(cards[2].charAt(2)-'0')],
CardFill.values()[(int)(cards[2].charAt(1)-'0')],
CardColor.values()[(int)(cards[2].charAt(0)-'0')]);
this.parent.checkIfSetAndNotify(c1,c2,c3,this.playerId);
}
}
}
catch(Exception e){
System.out.println(e.getMessage());
}
}
Client's code:
public SetClient()
{
String sentence;
this.myCards = new LinkedList<Card>();
try{
String modifiedSentence;
BufferedReader inFromUser = new BufferedReader( new InputStreamReader(System.in));
clientSocket = new Socket("10.0.0.3", 6789);
DataOutputStream outToServer = new DataOutputStream(clientSocket.getOutputStream());
BufferedReader inFromServer = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
outToServer.flush();
String name = sayHelloAndGetUserName();
outToServer.writeBytes(name);
outToServer.flush();
sentence = inFromServer.readLine();
sayWelcome(sentence);
ClientOperation clnOper = new ClientOperation(clientSocket,this);
Thread t = new Thread(clnOper);
t.start(); //<---this thread listens to the server for messages (suppose
//to catch the first 3 lines. In practice, catches only the first one.
while(!GameOver)
{
try{
BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); //<-----listens to the user's input
String line = br.readLine();
String[] choices = line.split(" ");
int c1 = Integer.parseInt(choices[0]);
int c2 = Integer.parseInt(choices[1]);
int c3 = Integer.parseInt(choices[2]);
sendSetMessage(outToServer,c1,c2,c3);
}
catch(Exception ex){
System.out.println("Error listening to system.in...");
}
}
}
catch(Exception e){
System.out.println(e.getMessage());
}
}
ClientOperation's code:
public void run()
{
if(socket==null){
return;
}
else{
try{
this.br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
while(true){
String line = this.br.readLine();
String command = line.split(":")[0];
String value = line.split(":")[1];
if (command.equalsIgnoreCase("Deck"))
{
ConvertStringToCards(value);
this.parent.PopulateCards(shownCards);
}
else if (command.equalsIgnoreCase("Points"))
{
System.out.println("Points: " + value);
}
else if(command.equalsIgnoreCase("NewPlayer")){
System.out.println(value + " has joined the game !\n");
}
else if(command.equalsIgnoreCase("Hint")){
this.parent.printHint(ConvertStringToHint(value));
}
}
}
catch(Exception e){
System.out.println(e.getMessage());
}
}
}
In general, what could be the reason that a client server project will work perfectly on 1 machine, but will hang in 2 separate machines?
P.S. when I am debugging step by step the client and the server on separate machines, it also works perfectly.
Thanks in advance,
If it's not an issue of localhost, then most likely if check for firewall if it is present disable and check again, secondly run netstat to check whether your server is listening on all interfaces not only on loopback.
On linux you can try:
netstat -nat | grep 6789
it will probably also work in windows power shell. If server is running/listening on your external ip and client still cannot connect and firewall is disable install wireshark and look what is going on on the network.