chat in java not synchronized (sockets, threads) - java

I'm trying to figure out a way to get an instance of a server to negotiate between two clients by creating a chat thread between them.
I created this project, and it "almost" works... but it seems like there is a buffer of synch problem.
when writing a line in one side (i.e Client#1), it doesn't pass to the other side (i.e Client#2), but only after Client#2 trys to pass a line too.
I know there might be better ways to implement this, but I'd like to understand what's wrong with my code.
your help would be great!
the code:
server
import java.io.*;
import java.net.*;
public class Server
{
public static void main(String[] args)
{
int id = 1;
System.out.println();
System.out.println("Server");
try
{
ServerSocket serverSocket = new ServerSocket(4321);
while (true)
{
Socket client1Socket = serverSocket.accept();
Socket client2Socket = serverSocket.accept();
System.out.println("clients connected from ports: \n"
+ client1Socket.getPort() + ", " + client2Socket.getPort());
Thread client1Thread = new ServerThread(client1Socket, client2Socket, id);
client1Thread.start();
id++;
Thread client2Thread = new ServerThread(client2Socket, client1Socket, id);
client2Thread.start();
id++;
}
}
catch (IOException ioe)
{
ioe.printStackTrace();
}
}
}
server thread
import java.io.*;
import java.net.*;
import java.util.*;
public class ServerThread extends Thread
{
Socket sourceSocket;
Socket destSocket;
int id;
public ServerThread(Socket src, Socket dst, int n)
{
sourceSocket = src;
destSocket = dst;
id = n;
}
public void run()
{
try
{
Scanner clientInput = new Scanner(sourceSocket.getInputStream());
PrintStream destOutput = new PrintStream(destSocket.getOutputStream());
destOutput.println("You are chatting with Client " + id);
boolean more = true;
while (more)
{
String input = clientInput.nextLine();
destOutput.println(input);
if (input.equals("Q"))
{
more = false;
}
}
sourceSocket.close();
destSocket.close();
}
catch (IOException ex)
{
ex.printStackTrace();
}
}
}
client
import java.io.*;
import java.net.*;
import java.util.*;
public class Client
{
public static void main(String[] args)
{
System.out.println();
System.out.println("Client");
try
{
Socket clientSocket = new Socket("localhost", 4321);
System.out.println("Connection Established");
Scanner input = new Scanner(clientSocket.getInputStream());
PrintStream output = new PrintStream(clientSocket.getOutputStream());
Scanner in = new Scanner(System.in);
System.out.println(input.nextLine());
boolean more = true;
while (more)
{
String text = in.nextLine();
output.println(text);
String nextInput = input.nextLine();
if (nextInput == null)
{
more = false;
}
else
{
System.out.println(nextInput);
}
}
}
catch (IOException ex)
{
ex.printStackTrace();
}
}
}

In your client code, the line String text = in.nextLine(); will block your thread. That means if you never type in anything in your client, you can not receive anything. So the solution is to put your message receiving code in another thread. such as:
Thread thread = new Thread(new Runnable() {
#Override
public void run() {
// TODO Auto-generated method stub
while (!Thread.interrupted()) {
System.out.println(input.nextLine());
}
}
});
thread.start();
while (true)
{
String text = in.nextLine();
output.println(text);
// String nextInput = input.nextLine();
......................
}

Related

Why can't the client obtain input again and again; it just goes blank after one input and one response from the server?

Client Side
import java.io.IOException;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.Scanner;
public class ThreadClient {
public static void main(String[] args) throws IOException {
final String HOST = "127.0.0.1";
final int PORT = 4040;
System.out.println("Client started.");
Socket socket = new Socket("127.0.0.1", 4040);
PrintWriter out = new PrintWriter(socket.getOutputStream(), true);
Scanner in = new Scanner(socket.getInputStream());
Scanner s = new Scanner(System.in);
while (true) {
System.out.print("Input: ");
String input = s.nextLine();
System.out.println("Sent: " + input);
out.println(input);
while(!"End".equals(input)){
System.out.println("Echoed from server: " + in.nextLine());
}
}
}
}
Server Side
import java.io.IOException;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;
public class ThreadServer {
public static void main(String[] args) throws IOException {
final int PORT = 4040;
ServerSocket serverSocket = new ServerSocket(PORT);
System.out.println("Server started...");
System.out.println("Wating for clients...");
while (true) {
Socket clientSocket = serverSocket.accept();
Thread t = new Thread() {
public void run() {
try {
PrintWriter out = new PrintWriter(clientSocket.getOutputStream(), true);
Scanner in = new Scanner(clientSocket.getInputStream());
while (in.hasNextLine()) {
String input = in.nextLine();
if (input.equalsIgnoreCase("exit")) {
break;
} else if(input.equals("Hi")){
out.println("How are you?");
} else if(input.equals("Bye")){
out.println("Thankyou! Have a good day!");
} else if (input != null) {
try {
String numbers;
numbers = input.replaceAll("[^0-9]", "");
int number = Integer.parseInt(numbers);
out.println("The line is being printed");
for (int i = 0; i < number; i++) {
out.println(input.replaceAll("[^a-z,^A-Z]", ""));
}
}
catch (Exception e) {
e.printStackTrace();
}
}else {
out.println("Sorry!");
}
}
} catch (IOException e) { }
}
};
t.start();
try {
Thread.sleep(2000L);
}
catch (InterruptedException xInterrupted) {
// Ignore.
}
}
}
}
I'm adding one input on the client-side, and it's getting a response from the server, but when I attempt to insert another, it goes blank, showing that it's not accepting any further input from the client. So, can someone point me in the right direction as to where I went wrong with this code?
You're probably stuck in the nested while loop. The input never changes inside the nested while loop, so you'll never escape.
while (true) {
System.out.print("Input: ");
String input = s.nextLine();
System.out.println("Sent: " + input);
out.println(input);
while(!"End".equals(input)){ // <-- Why the while loop?
System.out.println("Echoed from server: " + in.nextLine());
}
}
One option to fix it is to replace the while(true) loop with this:
System.out.print("Input: ");
String input = s.nextLine();
while (!"End".equals(input)) {
System.out.println("Sent: " + input);
out.println(input);
System.out.println("Echoed from server: " + in.nextLine());
System.out.print("Input: ");
String input = s.nextLine();
}

send a message to specific client threads

I have this Server class,
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
public class Server {
public static ArrayList<String> waiting = new ArrayList<String>();
public static ArrayList<String> playing = new ArrayList<String>();
public static ArrayList<Integer> score = new ArrayList<Integer>();
public static void main(String[] args) {
try {
ServerSocket server = new ServerSocket(4321);
while (true) {
try {
Socket socket = server.accept();
new EchoThread(socket).start();
} catch (Exception exc) {
exc.printStackTrace();
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
public void addClient(String name) {
waiting.add(name);
}
public int getNumClients() {
return waiting.size();
}
public String getClientName(int i) {
return waiting.get(i);
}
public void play() {
int scr = 0;
for (int i = 0; i < 4; i++) {
playing.add(waiting.get(0));
score.add(scr);
waiting.remove(0);
}
}
public boolean checkIfPlaying(String name) {
if (playing.indexOf(name) >= 0) {
return true;
} else {
return false;
}
}
}
and the Thread Class,
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;
public class EchoThread extends Thread {
protected Socket socket;
public EchoThread(Socket clientSocket) {
this.socket = clientSocket;
}
public void run() {
Server s = new Server();
DataInputStream in = null;
DataOutputStream out = null;
String line;
try {
in = new DataInputStream(socket.getInputStream());
out = new DataOutputStream(socket.getOutputStream());
} catch (IOException e) {
return;
}
while (true) {
try {
line = in.readLine();
String[] prot = line.split(":");
if (prot[0].equals("/login")) {
s.addClient(prot[1]);
} else if (prot[0].equals("/waiting")) {
if (s.checkIfPlaying(prot[1])) {
out.writeBytes("Playing" + "\r\n");
} else {
if (s.getNumClients() >= 4) {
s.play();
out.writeBytes("Playing" + "\r\n");
} else {
out.writeBytes(s.getNumClients() + "\r\n");
}
}
}
} catch (IOException e) {
e.printStackTrace();
return;
}
}
}
}
If the client connect to the server, the name of the client is stored in Server Class Array, waiting.
If the waiting clients is equals to 4, it will remove from the waiting array and put it in playing array.
I would like to make the server send message to the first 4 clients in playing array.
How can I do it?
For your Server Class, I would change your ArrayList< String > for waiting and playing to ArrayList< EchoThread >. This way your Server class is tracking each client object themselves instead of just their names. When you instantiate your EchoThread objects, I would pass the local server object to each EchoThread that way each object knows about the server that instantiated them.
Server Class
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
public class Server {
public ArrayList<EchoThread> waiting = new ArrayList<EchoThread>();
public ArrayList<EchoThread> playing = new ArrayList<EchoThread>();
public ArrayList<Integer> score = new ArrayList<Integer>();
public static void main(String[] args) {
try {
// Instantiate a single server object that you can pass into your connected clients
Server myServer = new Server();
ServerSocket server = new ServerSocket(4321);
while (true) {
try {
Socket socket = server.accept();
// Pass myServer into Echo Thread
new EchoThread(myServer, socket).start();
} catch (Exception exc) {
exc.printStackTrace();
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
// Have to synchronize this since multiple clients could be adding to this list at the same time
public synchronized void addClient(EchoThread client) {
waiting.add(client);
}
public int getNumClients() {
return waiting.size();
}
public String getClientName(int i) {
return waiting.get(i).getCName();
}
public void play() {
int scr = 0;
for (int i = 0; i < 4; i++) {
EchoThread clientBeingMovedToPlaying = waiting.get(0);
playing.add(clientBeingMovedToPlaying);
score.add(scr);
waiting.remove(0);
// This will be a new method in your EchoThread class
clientBeingMovedToPlaying.SendServerPlayingMessage();
}
}
public boolean checkIfPlaying(String name) {
boolean isPlaying = false;
for(EchoThread client : playing) {
if (client.getName().contentEquals(name)) {
isPlaying = true;
break;
}
}
return isPlaying;
}
}
For your Echo Thread class, I would make your variables in your run method class variables so they can be used throughout the class
EchoThread Class
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;
public class EchoThread extends Thread {
protected Socket socket;
protected Server s;
protected DataInputStream in;
protected DataOutputStream out;
protected String line;
protected String clientName;
// This way, each EchoThread object knows about the server
public EchoThread(Server theServer, Socket clientSocket) {
this.s = theServer;
this.socket = clientSocket;
}
public void run() {
try {
in = new DataInputStream(socket.getInputStream());
out = new DataOutputStream(socket.getOutputStream());
} catch (IOException e) {
return;
}
while (true) {
try {
line = in.readLine();
String[] prot = line.split(":");
if (prot[0].equals("/login")) {
// Original code
//s.addClient(prot[1]);
// New code
clientName = prot[1];
s.addClient(this);
} else if (prot[0].equals("/waiting")) {
if (s.checkIfPlaying(prot[1])) {
out.writeBytes("Playing" + "\r\n");
} else {
// You don't want multiple clients firing the play method, so you need to synchronize your server object
synchronized (s) {
if (s.getNumClients() >= 4) {
s.play();
out.writeBytes("Playing" + "\r\n");
} else {
out.writeBytes(s.getNumClients() + "\r\n");
}
}
}
}
} catch (IOException e) {
e.printStackTrace();
return;
}
}
}
public String getCName() {
return clientName;
}
public void SendServerPlayingMessage() {
if (out != null) {
// Send whatever message you want
}
}
}
I think this'll get you what your wanting... Forgive any syntax or logical errors, I don't have an IDE right in front of me at the moment.

multithreaded server, loop is working only once

I have problem with my multhreaded server for bridge auction. The topic of it is less important, all I need to do so far is to make the loop inside the run method work for more than only one "lap". I mean my loop is working for each client only once and then It stopped, but I can't solve this problem. It should work all the time and after sequence of players N-> E-> S-> W-> it start another lap from player N, but now it just stand still...
Check my code:
package serwer;
import java.io.*;
import java.net.*;
import java.util.*;
import java.util.logging.*;
public class Serwer {
public static void main(String[] args) throws Exception {
System.out.println("Started server to the bridge auction");
int conCount = 0;
ServerSocket serwer = new ServerSocket(9898);
ArrayList<connection> connections = new ArrayList<connection>(){};
try {
//only 4 players are allowed to play bridge in one table
while (connections.size() < 4) {
connection p = new connection(serwer.accept(), conCount++);
connections.add(p);
connections.get(conCount-1).start();
}
} finally {
serwer.close();
}
}
/**
* static class responsible for the connection in multithreaded server
*/
static class connection extends Thread {
private Socket socket;
private int conCount;
private static int counter = 0;
private final String[] Players;
private String stringOnServer = "0";
private int stake = 1;
public connection(Socket socket, int conCount) {
this.Players = new String[]{"N", "E", "S", "W"};
this.socket = socket;
this.conCount = conCount;
System.out.println("New connection id: " + Players[conCount]);
}
public void run() {
try {
BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
PrintWriter out = new PrintWriter(socket.getOutputStream(), true);
//here is info what player are you
out.println("You are player on position " + Players[conCount]);
while (true) {
synchronized (this) {
if (counter % 4 == conCount) {
while (true) {
out.println("Your turn " + Players[conCount] + ", please input the text: ");
String input = in.readLine();
System.out.println("\t" + Players[conCount] + " : " + input);
counter += 1;
//for now only the stringOnServer is simply echo
stringOnServer = input;
System.out.println("\tCurrent string on server = " + stringOnServer);
try {
this.wait();
} catch (InterruptedException ex) {
Logger.getLogger(Serwer.class.getName()).log(Level.SEVERE, null, ex);
}
break;
}
} else {
this.notify();
}
}
}
} catch (IOException e) {
System.out.println("error with player: " + Players[conCount] + ": " + e);
} finally {
try {
socket.close();
} catch (IOException e) {
System.out.println("can't closed");
}
System.out.println("connection with player" + Players[conCount] + " terminated");
}
}
}
}
Client's code is really simple, but if someone will have time and patience to test it I add it to:
package klient;
import java.net.*;
import java.io.*;
public class Klient {
public static void main(String[] args) throws IOException {
try {
Socket s = new Socket("127.0.0.1", 9898);
String answer;
System.out.println("Welcome on the server of auction.");
//Here is displayed info from server what player are you
BufferedReader fromServer = new BufferedReader(new InputStreamReader(s.getInputStream()));
System.out.println(fromServer.readLine());
while (true) {
System.out.println(fromServer.readLine());
PrintWriter out = new PrintWriter(s.getOutputStream(), true);
BufferedReader input = new BufferedReader(new InputStreamReader(System.in));
answer = input.readLine();
out.println(answer);
}
} catch (ConnectException ex) {
System.out.println("There are 4 players on server or server is closed, try again later");
}
}
}
The problem is in this loop in connection class with counter.
Thanks in advance for your help :)
You have to call notify() from another thread. Your current thread is waiting and cannot notify itself.

Socket Client/Server stop method in java

This is the simple client/server socket app for my faculty project. First, the Server class should be run, and then if Client class runs - it prints out the IP address of the local machine and the port that's been used.
I can't figure out one thing:
How and WHERE to create a method in class that will close(stop) the Server? And
how to make this like an event or something, for example if client
sends "stop" it should somehow stop the server...
SERVER.JAVA
import java.io.*;
import java.net.*;
public class Server {
public static void main(String[] args) {
System.out.println("The server has been summoned.\n");
System.out.println("The server is waiting for client to come...");
try {
ServerSocket servertest = new ServerSocket(2014);
while (true) {
try {
Socket ser = servertest.accept();
new ThreadSer(ser).start();
} catch (IOException e) {}
}
} catch (IOException e) {System.err.println(e);}
}
public static class ThreadSer extends Thread {
private Socket s;
public ThreadSer(Socket s) {
this.s = s;
}
#Override
public void run() {
try {
String response = "This is the IP: " + s.getLocalAddress() + " that has come via port: "
+ s.getLocalPort() + "\r\n";
OutputStream out = s.getOutputStream();
out.write(response.getBytes());
} catch (IOException e) { System.err.println(e); }
}}}
CLIENT.JAVA
import java.io.*;
import java.net.*;
public class Client {
public static void main(String[] args) throws UnknownHostException, IOException {
Socket socket = new Socket("localhost", 2014);
new OutputThread(socket.getInputStream()).start();
}
public static class OutputThread extends Thread {
private InputStream inputstream;
public OutputThread(InputStream inputstream) {
this.inputstream = inputstream;
}
#Override
public void run() {
BufferedReader input = new BufferedReader(new InputStreamReader(inputstream));
while (true) {
try {
String line = input.readLine();
System.out.println(line);
} catch (IOException exception) {
exception.printStackTrace();
break;
}
}
}}}
You should constantly ask for the inputstream of the client.. put it in the loop that always accept for the client input..
example:
public static class ThreadSer extends Thread {
private Socket s;
public ThreadSer(Socket s) {
this.s = s;
}
#Override
public void run() {
try {
String response = "This is the IP: " + s.getLocalAddress() + " that has come via port: "
+ s.getLocalPort() + "\r\n";
ObjectInputStream input = new ObjectInputStream(s.getInputStream());
while(true)
{
Object object = input.readObject();
if(object instanceof String)
{
String command = ((String) object).trim();
if(command.equals("stop"))
break;
}
}
s.close();
} catch (IOException e) { System.err.println(e); }
}}}

Program won't continue after initializing input/output streams?

Before people suspect that I have no idea what I'm doing at all (and end up voting this down for no reason at all), please read this:
It connects to my server just fine! I'm getting no errors (from the client OR server), and my server is recognizing the connection. It works with my friend's client that he made, but I wanted to make my own client, and apparently I'm doing something wrong. PLEASE STAY ON TOPIC! Thanks :)
Title basically says it all. I've tested with println messages above and below the setupStream() in my Client.java run(), but only the message above the setupStream() prints. I'm not sure how I'm supposed to initialize my stream without making my program come to a halt.
Client.java
import java.io.IOException;
public class Client extends Stream implements Runnable {
public boolean running = false;
private Thread clientThread;
Frame frame;
public Client() {
super("localhost", 43594);
frame = new ClientFrame(500, 500);
start();
}
public synchronized void start() {
if(running) return;
running = true;
clientThread = new Thread(this);
clientThread.start();
}
public synchronized void stop() {
if(!running) return;
running = false;
clientThread.interrupt();
try {
clientThread.join();
} catch (InterruptedException e) {e.printStackTrace();}
}
public void run() {
try{
setupStream();
while(running) {
System.out.println("running");
}
}catch(IOException e) {
e.printStackTrace();
}finally {
try{
out.close();
in.close();
socket.close();
clientThread.join();
}catch(IOException | InterruptedException e) { e.printStackTrace(); }
}
}
public static void main(String[] args) {
new Client();
}
}
Stream.java
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket;
public class Stream {
Socket socket;
ObjectOutputStream out;
ObjectInputStream in;
String data;
public Stream(String host, int port) {
try {
socket = new Socket(host, port);
} catch (IOException e) {
e.printStackTrace();
}
}
protected void setupStream() throws IOException {
out = new ObjectOutputStream(socket.getOutputStream());
out.flush();
in = new ObjectInputStream(socket.getInputStream());
}
}
My Server Thread:
package Server;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
public class User extends Thread {
public static int users = 0;
public int ID;
public String username;
boolean online = false;
public static ArrayList<String> usernames = new ArrayList<String>();
Socket socket;
DataOutputStream out;
DataInputStream in;
String input;
public User(Socket socket) {
this.socket = socket;
}
public String decode(String input) {
String[] split = input.split(" ");
if(input.startsWith("::")) {
try {
switch(split[0].substring(2, split[0].length()).toLowerCase()) {
case "setname":
case "changename":
case "newname":
if(usernames.contains(split[1].toLowerCase())) {
out.writeUTF("This name is already taken! Please choose a different one.");
out.flush();
return null;
}
if(username == null) {
username = split[1].substring(0, 1).toUpperCase() + split[1].substring(1, split[1].length());
Server.users.put(split[1].toLowerCase(), Server.user[ID]);
usernames.add(split[1].toLowerCase());
} else {
usernames.remove(username.toLowerCase());
username = split[1].substring(0, 1).toUpperCase() + split[1].substring(1, split[1].length());
usernames.add(split[1].toLowerCase());
}
return null;
case "rank+":
return null;
case "[sm]=":
return null;
}
}catch(IOException e) { }
}
return input;
}
String timeStamp;
public void run() {
try {
out = new DataOutputStream(socket.getOutputStream());
out.flush();
in = new DataInputStream(socket.getInputStream());
while((input = in.readUTF()) != null) {
input = decode(input);
if(input != null) {
if(username != null) {
timeStamp = new SimpleDateFormat("[h:mm:ss] ").format(Calendar.getInstance().getTime());
Server.sendGlobalMessage(timeStamp + username +": "+input);
} else {
timeStamp = new SimpleDateFormat("[h:mm:ss] ").format(Calendar.getInstance().getTime());
Server.sendGlobalMessage(timeStamp + "Guest "+ID+": "+input);
}
}
}
}catch(IOException e) { e.printStackTrace(); } finally {
try{
out.close();
in.close();
socket.close();
} catch(IOException e) { e.printStackTrace(); }
}
}
}
I haven't touched the code of my Server Thread for a while, since it has always worked up until I made my new client.
I suspect that your server does not create an ObjectOutputStream, so when the client constructs its ObjectInputStream, it blocks waiting for the object stream header, which never arrives.

Categories

Resources