Problem in Client and Server Architecture. Exception in thread appears - java

I wrote this code in java based on sockets and thread pool. The server code runs on its own but when I run the client code after running the server, an exception on the server appears. What could be causing this?
Below is the server class which conatains threads for each new client
package java_40;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
//Server Implementation
/**
*
* #author Abdullah
*/
public class Server implements Runnable {
private ArrayList<ConnectionHandler> connections;
private ServerSocket server;
private boolean done;
private ExecutorService pool;
public Server (){
connections = new ArrayList<>();
done = false;
}
#Override
public void run() {
try {
while(!done){
server = new ServerSocket(9999);
pool = Executors.newCachedThreadPool();
Socket client = server.accept();
ConnectionHandler handler = new ConnectionHandler(client);
connections.add(handler);
pool.execute(handler);
}
} catch (IOException ex) {
shutdown();
}
}
public void broadcast(String message){
for(ConnectionHandler ch :connections){
if(ch !=null){
ch.sendMessage(message);
}
}
}
public void shutdown(){
try{
done=true;
if(!server.isClosed()){
server.close();
}
for(ConnectionHandler ch : connections){
ch.shutdown();
}
}catch(IOException e){
// ignore
}
}
class ConnectionHandler implements Runnable{ //handles individual client connection
private final Socket client;
private BufferedReader in;
private PrintWriter out;
private String nickname;
public ConnectionHandler(Socket client){
this.client = client;
}
#Override
public void run() {
if(client != null){
try{
out = new PrintWriter(client.getOutputStream(), true);
in = new BufferedReader(new InputStreamReader(client.getInputStream()));
out.println("Enter nickname");
nickname =in.readLine();
System.out.println(nickname+" connected");
broadcast(nickname + " has joined the chat");
String message;
while((message = in.readLine()) !=null){
if (message.startsWith("/nick"))
{
// TODO: handle nickname
String[] messageSplit = message.split(" ", 2);
if(messageSplit.length == 2){
broadcast(nickname + " renamed themselves to " + messageSplit[1]);
System.out.println(nickname + " renamed themselves to " + messageSplit[1]);
nickname = messageSplit[1];
out.println("Successfully changed nickname to " + nickname);
}else{
out.println("No nickname provided");
}
}
else if(message.startsWith("/quit")){
broadcast(nickname + " has left the chat");
// shutdown();
}
else{
broadcast(nickname + ": " + message);
}
}
} catch(IOException e){
if(this.in != null && this.out != null){
shutdown();
}
}
}
}
public void sendMessage(String message){
out.println(message);
}
public void shutdown(){
try{
in.close();
out.close();
if(!client.isClosed()){
client.close();
}
}catch(IOException e){
// ignore
}
}
}
public static void main(String[] args) {
// TODO code application logic here
Server server =new Server();
server.run();
}
}
below is the client class in which client has been implemented. A great number of clients can connect to the server through cmd.
/*
* Click nbfs://nbhost/SystemFileSystem/Templates/Licenses/license-default.txt to change this license
* Click nbfs://nbhost/SystemFileSystem/Templates/Classes/Class.java to edit this template
*/
package java_40;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
public class client implements Runnable{
private Socket client;
private BufferedReader in;
private PrintWriter out;
private boolean done;
#Override
public void run() {
try{
Socket client = new Socket("127.0.0.1",9999); // if you want to connect to someone elses server replace the ip with their ip as of now this our own ip
out = new PrintWriter(client.getOutputStream(),true);
in = new BufferedReader(new InputStreamReader(client.getInputStream()));
InputHandler inHandler = new InputHandler();
Thread t = new Thread(inHandler);
t.start();
String inMessage;
while((inMessage = in.readLine()) != null )
{
System.out.println(inMessage);
}
}catch(IOException e){
// shutdown();
}
}
private void shutdown(){
done =true;
try{
in.close();
out.close();
if(!client.isClosed()){
client.close();
}
}catch(IOException e){
// ignore
}
}
class InputHandler implements Runnable{
#Override
public void run() {
try{
BufferedReader inReader = new BufferedReader(new InputStreamReader(System.in));
while(!done){
String message = inReader.readLine();
if(message.equals("/quits")){
inReader.close();
shutdown();
}else{
out.println(message);
}
}
}catch(IOException e){
// shutdown();
}
}
}
public static void main(String[] args) {
client c = new client();
c.run();
}
}

Related

How to handle multiple streams from a lobby server to clients

So, I'm having an issue with a project of mine. I'm writing a multiplayer lobby system which will enable multiple users to join a lobby, readying themselves by pressing a key. The issue that I'm facing is when two players is readying themselves, the lobby is only printing out a message for the last player who readied themselves. The system is built up in the following way.
Main Server
package master;
import java.net.*;
import java.io.*;
import java.util.*;
import java.io.*;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Date;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import main.Lobby;
public class MainServer {
public static final int PORT = 4444;
public static final String HOST = "localhost";
public ArrayList<Lobby> serverList = new ArrayList<>();
public static void main(String[] args) throws IOException, ClassNotFoundException {
new MainServer().runServer();
}
public void runServer() throws IOException, ClassNotFoundException {
// Creating the server
ServerSocket serverSocket = new ServerSocket(PORT);
System.out.println("Main Server initiated.");
while (true) {
Socket socket = serverSocket.accept();
try {
// Establishing the connection to the Lobby server and then adding it to its list
ObjectInputStream objectInputStream = new ObjectInputStream(socket.getInputStream());
ObjectOutputStream objectOutputStream = new ObjectOutputStream(socket.getOutputStream());
objectOutputStream.writeObject("Server created successfully.");
Lobby s = (Lobby) objectInputStream.readObject();
this.serverList.add(s);
System.out.println("Server \"" + s.name + "\" added to game list.");
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
}
The lobby
package main;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.concurrent.Semaphore;
import master.MainServer;
/**
* The Class Server.
*/
public class Lobby implements Serializable {
private static final long serialVersionUID = -21654L;
public static final int PORT = 4445;
public static final int MAX_USERS = 5000;
public static final String HOST = "localhost";
public String name = "Lobby Server";
public int clientNumber;
public int playerNumberReady = 0;
public boolean allPlayersReady = false;
public boolean OddurIsNice = false;
public static void main(String[] args) throws IOException, ClassNotFoundException {
Lobby s = new Lobby();
s.runServer();
}
public void runServer() throws IOException, ClassNotFoundException {
registerServer();
new Thread( () -> {
try {
ServerSocket serverSocket = new ServerSocket(PORT);
System.out.println("Server waiting for connections...");
while (true) {
Socket socket = serverSocket.accept();
System.out.println("User 1 is now connected");
clientNumber++;
new ObjectOutputStream(socket.getOutputStream()).writeObject("You are connected man");
Socket socket2 = serverSocket.accept();
System.out.println("User 2 is now connected");
clientNumber++;
// ObjectOutputStream objectOutputStream2 = new ObjectOutputStream(socket2.getOutputStream());
// objectOutputStream2.writeObject("You are player number " + clientNumber + ". Waiting for other players to join");
new ServerThread(socket, socket2).start();
}
} catch (IOException ex) {
ex.printStackTrace();
}
}).start();
}
private void registerServer() throws UnknownHostException, IOException, ClassNotFoundException {
// Method for establishing a connection to the MainServer
Socket socket = new Socket(MainServer.HOST, MainServer.PORT);
ObjectOutputStream objectOutputStream = new ObjectOutputStream(socket.getOutputStream());
ObjectInputStream objectInputStream = new ObjectInputStream(socket.getInputStream());
objectOutputStream.writeObject(this);
System.out.println((String) objectInputStream.readObject());
}
public class ServerThread extends Thread {
public Socket socket = null;
public Socket socket2 = null;
ServerThread(Socket socket, Socket socket2) {
this.socket = socket;
this.socket2 = socket2;
}
public void run() {
try {
// This method is for when the client want's to connect to the lobby
ObjectInputStream objectInputStream = new ObjectInputStream(socket.getInputStream());
ObjectOutputStream objectOutputStream = new ObjectOutputStream(socket.getOutputStream());
System.out.println("User 1 is now connected");
ObjectInputStream objectInputStream2 = new ObjectInputStream(socket2.getInputStream());
ObjectOutputStream objectOutputStream2 = new ObjectOutputStream(socket2.getOutputStream());
System.out.println("User 2 is now connected");
BoardGameClient joined = (BoardGameClient) objectInputStream.readObject();
System.out.println(joined.name + " is now connected.");
while(true) {
objectOutputStream.writeObject("You joined the server.");
objectOutputStream.writeObject("You are player Number " + 1);
objectOutputStream.writeObject("Press '1' if you are ready");
objectOutputStream2.writeObject("You joined the server.");
objectOutputStream2.writeObject("You are player Number " + 2);
objectOutputStream2.writeObject("Press '1' if you are ready");
if(objectInputStream.readObject().equals(1)) {
playerNumberReady++;
}
if(objectInputStream2.readObject().equals(1)) {
playerNumberReady++;
}
if(playerNumberReady != 2) {
allPlayersReady = false;
} else {
allPlayersReady = true;
}
if (allPlayersReady == false) {
objectOutputStream.writeObject("Waiting...");
objectOutputStream2.writeObject("Waiting...");
}
if (allPlayersReady == true) {
objectOutputStream.writeObject("Lets GO");
objectOutputStream2.writeObject("Lets GO");
}
while (true) {
System.out.println(objectInputStream.readObject());
}
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
And the client
package main;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.Scanner;
import java.util.concurrent.Semaphore;
import master.MainServer;
public class BoardGameClient implements Serializable {
private int playerName;
private static final long serialVersionUID = -6224L;
public String name = "User";
private transient Socket socket;
public transient Scanner input = new Scanner(System.in);
public static void main(String[] args) {
BoardGameClient c = new BoardGameClient();
if (args.length > 0) {
c.name = args[0];
}
try {
c.joinServer();
} catch (ClassNotFoundException | IOException e) {
System.out.println("Failed to join server.");
e.printStackTrace();
}
}
public void joinServer() throws UnknownHostException, IOException, ClassNotFoundException {
socket = new Socket(Lobby.HOST, Lobby.PORT);
ObjectOutputStream objectOutputStream = new ObjectOutputStream(socket.getOutputStream());
ObjectInputStream objectInputStream = new ObjectInputStream(socket.getInputStream());
while(true) {
objectOutputStream.writeObject(this);
BufferedReader inputReader = new BufferedReader(new InputStreamReader(System.in));
System.out.println(objectInputStream.readObject());
System.out.println(objectInputStream.readObject());
System.out.println(objectInputStream.readObject());
int ready = input.nextInt();
objectOutputStream.writeObject(ready);
System.out.println(objectInputStream.readObject());
objectOutputStream.writeObject(name + ": " + inputReader.readLine());
}
}
}
I sincerely hope, that someone will be able to help me out <3
Firstly, there's a few things that bug me about this code. Not to sound condescending but you need to avoid rewriting code as much as possible. What happens if you want 3 or more players in the future? Currently you'd have to manually create a whole socket eg socket3, and then rewrite all the code you've already written. This is bad. You've manually spent the time creating 2 sockets and then created 2 streams for both of these sockets etc etc.
This can be automated don't you think?
Secondly, you have a lot of public variables. Unless they are static and final, for the most part you should keep variables as private.
I've tinkered with your lobby class as seen below, which is more scalable. It's not perfect by any means, but I feel illustrates the direction of improvement you should be heading for. Look up SOLID OOP principles, they'll help you guaranteed.
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.LinkedHashSet;
import java.util.Set;
/**
* The Class Server.
*/
public class Lobby implements Serializable {
private static final long serialVersionUID = -21654L;
public static final int PORT = 4445;
public static final int MAX_USERS = 5000;
public static final String HOST = "localhost";
private static final int MIN_USERS = 2;
private String name = "Lobby Server";
private int clientNumber;
private boolean gameRunning = false;
// set of client connections
private final Set<ServerThread> clientConnectionThreads = new LinkedHashSet<>();
public static void main(String[] args) throws IOException, ClassNotFoundException {
Lobby s = new Lobby();
s.createLobby();
}
public void createLobby() throws IOException, ClassNotFoundException {
// waits for all players to ready up in a different thread
new Thread(this::waitReady).start();
registerServer();
// Listens for clients
runServer();
}
public void runServer() {
// closes serverSocket automatically in this way
try (ServerSocket serverSocket = new ServerSocket(PORT)) {
System.out.println("Server waiting for connections...");
long ids = 0;
while (!gameRunning) {
// accepts a new client connection
Socket socket = serverSocket.accept();
if (clientConnectionThreads.size() >= MAX_USERS) {
// tell user server is full and dont add the connection
} else {
// calculates the new id of the incoming player and adds them to the lobby
ids++;
this.clientConnectionThreads.add(new ServerThread(ids, socket));
System.out.println("User " + ids + " is now connected");
}
}
} catch (IOException ex) {
ex.printStackTrace();
}
}
/*
* loops until every player is ready and there is enough players and then starts
* the game.
*/
public void waitReady() {
while (true) {
try {
if (areAllReady() && this.clientConnectionThreads.size() >= MIN_USERS) {
startGame();
return;
}
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
// returns true if all users are ready
public boolean areAllReady() {
return clientConnectionThreads.stream().allMatch(ServerThread::isReady);
}
public void startGame() {
System.out.println("Starting game...");
this.gameRunning = true;
clientConnectionThreads.forEach(ServerThread::startGame);
// do game stuff
}
// i havent touched this function
private void registerServer() throws UnknownHostException, IOException, ClassNotFoundException {
// Method for establishing a connection to the MainServer
Socket socket = new Socket(MainServer.HOST, MainServer.PORT);
ObjectOutputStream objectOutputStream = new ObjectOutputStream(socket.getOutputStream());
ObjectInputStream objectInputStream = new ObjectInputStream(socket.getInputStream());
objectOutputStream.writeObject(this);
System.out.println((String) objectInputStream.readObject());
}
public class ServerThread extends Thread {
private final Socket socket;
private final ObjectInputStream in;
private final ObjectOutputStream out;
private final long id;
boolean ready = false;
private ServerThread(long id, Socket socket) throws IOException {
// does some basic initialization
this.socket = socket;
this.id = id;
in = new ObjectInputStream(socket.getInputStream());
out = new ObjectOutputStream(socket.getOutputStream());
// starts this connection thread
this.start();
}
public boolean isReady() {
return ready;
}
public void run() {
try {
// sets up the client and waits for their input
BoardGameClient joined = (BoardGameClient) in.readObject();
System.out.println(joined.name + " is now connected.");
out.writeObject("You joined the server.");
out.writeObject("You are player Number " + id);
out.writeObject("Press '1' if you are ready");
out.flush();
// waits for user to return ready
while (!ready) {
try {
int input = in.readInt();
System.out.println("input: " + input);
ready = input == 1;
} catch (ClassCastException e) {
e.printStackTrace();
}
}
out.writeObject("Waiting for players...");
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
public void startGame() {
// send client message etc etc
}
}
public String getName() {
return name;
}
}
I basically didn't change any of the other classes, except a few lines within the client class to make this work. (I've changed the ready input type from writeObject() to writeInt())
I haven't tested this for problems, but I know it works at least on a basic level.
I also suggest using writeUTS()/readUTS() instead of writeObject()/readObject() for sending and receiving Strings across streams as this will add extra complexity to the code.

Java Socket programming with more than one client

I have code which works with one client connection. What I need is ability for the server to handle multiple client requests using multithreaded approach.
I found some solutions, but it's not meet my requirements, like this, or this
Server.java
import java.io.IOException;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
public class Server extends User {
private Socket clientSocket;
private ServerSocket serverSocket;
public Server() {
super();
}
private void createConnection() {
try {
InetAddress locIP = InetAddress.getByName("127.0.0.1");
serverSocket = new ServerSocket(9999, 0, locIP);
// serverSocket = new ServerSocket(4444, 4444, InetAddress.getByName("192.168.0.101"));
} catch (IOException e) {
System.err.println("Could not listen on port: 9999 ." + e);
System.exit(1);
}
}
private void closeConnection() {
try {
serverSocket.close();
} catch (IOException e) {
System.err.println(e);
}
}
#Override
public void connect() {
createConnection();
//Socket clientSocket=null;
try {
clientSocket = serverSocket.accept();
System.out.println("Client connected! "
+ "IP: "
+ clientSocket.getInetAddress()
+ ", port: "
+ clientSocket.getPort());
} catch (IOException e) {
System.err.println("Accept failed. " + e);
System.exit(1);
}
}
#Override
public void disconnect() {
try {
clientSocket.close();
} catch (IOException e) {
System.err.println(e);
}
closeConnection();
}
#Override
public Socket getSocket() {
return clientSocket;
}
#Override
public String toString() {
return new String("Server");
}
}
Client.java
import java.io.IOException;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.UnknownHostException;
public class Client extends User {
private Socket socket;
public Client() {
super();
}
#Override
public Socket getSocket() {
return socket;
}
#Override
public void connect() {
try {
InetAddress locIP = InetAddress.getByName("127.0.0.1");
// socket = new Socket(9999, 0, locIP);
// socket = new Socket("localhost", 9999); oryginalny
socket = new Socket(locIP, 9999);
} catch (UnknownHostException e) {
System.err.println("The host not found! " + e);
System.exit(1);
} catch (IOException e) {
System.err.println("Can't find connection! " + e);
System.exit(1);
}
}
#Override
public void disconnect() {
try {
socket.close();
} catch (IOException e) {
System.err.println(e);
}
}
#Override
public String toString() {
return new String("Client");
}
}
SendButton.java
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.BufferedOutputStream;
import java.io.IOException;
import java.io.PrintStream;
import javax.swing.JButton;
import javax.swing.JTextPane;
#SuppressWarnings("serial")
public class SendButton extends JButton {
private JTextPane incomingMessages;
private JTextPane messageToSend;
private User user;
public SendButton(User user, JTextPane incomingMessages, JTextPane messageToSend) {
super("Send!");
this.user = user;
this.incomingMessages = incomingMessages;
this.messageToSend = messageToSend;
this.addActionListener(new SendListener());
}
public class Write {
private PrintStream out;
public Write() {
try {
out = new PrintStream(new BufferedOutputStream(
user.getSocket().getOutputStream(), 1024), false);
} catch (IOException e) {
System.err.println(e);
}
}
public void send(String message) {
if (message != null) {
out.println(message);
out.flush();
incomingMessages.setText(new String(incomingMessages.getText() + "\nMe: " + message));
}
}
}
public class SendListener implements ActionListener {
private Write write = new Write();
private String toSend;
#Override
public void actionPerformed(ActionEvent event) {
toSend = messageToSend.getText();
if (toSend != null || event.getActionCommand() == "\n") {
write.send(toSend);
}
messageToSend.setText(new String(""));
}
}
}
You need to create a new Runnable class, whose data members consist of a Socket and its input and output streams. This class is used on the server side. Its run() method is responsible for all I/O to that client. Then your accept() loop just looks like this:
while (true)
{
new Thread(new ConnectionHandler(serverSocket.accept())).start();
}
where ConnectionHandler implements Runnable as above.
simply what you need to do is after accepting the request from the client (Using main thread), then the request pass to a new thread with the client socket and process the request inside the new thread. So the main thread is free to accept new requests.

Java NIO - SocketChannel.write() on server many times but Client only receive result once

I'm doing an exercise requires making a server - client chat program using Java Non-Blocking IO. At the moment, the way the program works is simple: when a client send a message to the server, the server (which already keep track of all the clients) echo the message back to all the clients.
This is my some parts of my server-side code:
public static ByteBuffer str_to_bb(String msg) {
try {
return encoder.encode(CharBuffer.wrap(msg));
} catch(Exception e) {
e.printStackTrace();
}
return null;
}
private static void broadcastMessage(String nickname, String message) {
System.out.println(">clientSocketChannels size " + clientSocketChannels.size());
Iterator clientSocketChannelsIterator = clientSocketChannels.iterator();
while (clientSocketChannelsIterator.hasNext()) {
SocketChannel sc = (SocketChannel) clientSocketChannelsIterator.next();
try {
ByteBuffer bb = str_to_bb(message);
System.out.println("bufferRemaining: " + bb.remaining()); // returns 2048
int writeResult = sc.write(bb);
System.out.println("writeResult: " + writeResult); // returns 2048
} catch (IOException e) {
e.printStackTrace();
}
}
}
The following is my client-side code:
import javax.swing.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.Scanner;
/**
* Created by ThaiSon on 7/6/2015.
*/
public class ChatRoomClientGUI {
private JTextArea textAreaMessages;
private JTextField textFieldMessage;
private JButton buttonSendMsg;
private JPanel jPanel1;
private JLabel txtFieldInfo;
private static InetAddress inetAddress;
private static final int PORT = 1234;
private static Socket socket = null;
private static Scanner input = null;
private static PrintWriter output = null;
private static ChatRoomClientGUI singleton;
public ChatRoomClientGUI() {
singleton = this;
buttonSendMsg.addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) {
super.mouseClicked(e);
if (e.getButton() == MouseEvent.BUTTON1) {
String message = textFieldMessage.getText();
output.println(message);
textFieldMessage.setText("");
}
}
});
}
public static void main(String[] args) {
JFrame promptFrame = new JFrame();
Object nickname = JOptionPane.showInputDialog(promptFrame, "Enter your nickname:");
promptFrame.dispose();
JFrame frame = new JFrame("ChatRoomClientGUI");
frame.setContentPane(new ChatRoomClientGUI().jPanel1);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setVisible(true);
System.out.println("> Client with nickname " + nickname);
try {
inetAddress = InetAddress.getLocalHost();
} catch (UnknownHostException e) {
e.printStackTrace();
}
accessServer(nickname.toString());
}
private static void accessServer(String nickname) {
try {
socket = new Socket(inetAddress, PORT);
input = new Scanner(socket.getInputStream());
output = new PrintWriter(socket.getOutputStream(), true);
output.println(nickname); // Register nickname with the server
//TODO update the txtFieldInfo content
// Create a new thread to listen to InputStream event
InputStreamEvent inputStreamEvent = new InputStreamEvent(socket);
inputStreamEvent.start();
} catch (IOException e) {
e.printStackTrace();
}
}
private static void handleInputStream(){
String response = input.nextLine();
System.out.println("TODO " + response);
singleton.textAreaMessages.append(response + "\n");
}
static class InputStreamEvent extends Thread{
Socket socket;
public InputStreamEvent(Socket socket){
this.socket = socket;
}
public void run(){
try {
InputStream inputStream = socket.getInputStream();
byte[] buffer = new byte[2048];
int read;
while (true){
if(inputStream.available() > 0){
handleInputStream();
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
The problem I'm facing with now is that when I use a client (which works well with the old multithreaded server) to send message to the server, the client only get return the first message it sends. All the next responses from the server is empty (the server does send back, but only an empty message).
So my attempts to debug includes:
check if the messages from the client has reach the server or not. They does.
log the buffer.remaining() and socketChannel.write(buffer) result as shown above, all the log result seems to be normal to me.
Hope you guys can help me with this.
This:
if(inputStream.available() > 0){
Get rid of this test. With it, your client is smoking the CPU. Without it, it will block in readLine() as God intended.
Are you sure your server is still sending lines? with line terminators? If it isn't, readLine() will block forever looking for one, until end of stream or an exception occurs.
I referred the code explain by EJP on this link Java NIO Server/Client Chat App - sending data only by closing the socket
it solves my problem. use this code
import java.nio.channels.SocketChannel;
import java.nio.channels.Selector;
import java.nio.ByteBuffer;
import java.io.IOException;
import java.util.Scanner;
import java.nio.channels.SelectionKey;
import java.net.InetSocketAddress;
public class Client {
public static void main(String args[]) {
try {
ByteBuffer buf = ByteBuffer.allocate(200);
Scanner scanner = new Scanner(System.in);
Selector selector = Selector.open();
SocketChannel socketChannel = SocketChannel.open();
socketChannel.configureBlocking(false);
socketChannel.register(selector, SelectionKey.OP_CONNECT|SelectionKey.OP_READ|SelectionKey.OP_WRITE);
boolean isConnected = socketChannel.connect(new InetSocketAddress("localhost", 5000));
if(isConnected) {
System.out.println("Connected, de-registering OP_CONNECT");
}
new Thread(new Runnable(){
private SocketChannel socketChannel;
private Selector selector;
public Runnable init(SocketChannel socketChannel, Selector selector) {
this.socketChannel = socketChannel;
this.selector = selector;
return this;
}
public void run() {
try {
ByteBuffer buf = ByteBuffer.allocate(200);
while(!Thread.interrupted()) {
int keys = selector.select();
if(keys > 0) {
for(SelectionKey key : selector.selectedKeys()) {
if(key.isConnectable()) {
boolean finishConnectResult = socketChannel.finishConnect();
socketChannel.register(this.selector, SelectionKey.OP_WRITE|SelectionKey.OP_READ);
System.out.println("Finished Connect : " + finishConnectResult);
}
if(key.isReadable()) {
int bytesRead = 0;
while((bytesRead = socketChannel.read(buf)) > 0) {
buf.flip();
while(buf.hasRemaining()) {
System.out.print((char)buf.get());
}
buf.clear();
}
if(bytesRead == -1) {
key.channel().close();
}
}
}
}
Thread.sleep(10);
}
} catch(IOException e) {
e.printStackTrace();
} catch(InterruptedException e) {
e.printStackTrace();
}
}
}.init(socketChannel, selector)).start();
while(true) {
while(scanner.hasNextLine()) {
buf.clear();
buf.put(scanner.nextLine().getBytes());
buf.flip();
socketChannel.write(buf);
buf.flip();
}
}
} catch(IOException e) {
e.printStackTrace();
}
}
}
I have done the mistake setting this flag
key.interestOps(SelectionKey.OP_READ);
)
instead of below.
use this
socketChannel.register(this.selector, SelectionKey.OP_WRITE|SelectionKey.OP_READ);

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.

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