I try to build server that could talk with several clients at the same time.
But there are some problems with the thread, and the code doesn't work well.
Could you tell me what is wrong with the thread part? Thank you very much.
public class ServerMulti extends JFrame implements ActionListener{
private static final int PORT = 60534;
private JTextField textField = new JTextField();
private static JTextArea textArea = new JTextArea();
private JButton button = new JButton();
public static ServerSocket server;
public static Socket socket;
public static BufferedReader in;
public static PrintWriter out;
public ServerMulti(){
/*
* build up GUI
*/
setTitle("Server Multi");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setSize(300,400);
setBackground(Color.white);
setLayout(new BorderLayout());
button.setText("Send");
button.setSize(300, 50);
textField.setText("Type here");
textArea.setSize(300, 50);
add(textField, BorderLayout.NORTH);
add(new JScrollPane(textArea), BorderLayout.CENTER);
add(button, BorderLayout.SOUTH);
setLocation(300, 100);
button.addActionListener(this);
setVisible(true);
}
/*
* print out the information that need to sent to clients
*
*/
public void actionPerformed(ActionEvent e) {
textArea.append(textField.getText()+"\n");
out.println(textField.getText());
textField.setText("");
}
public static void main(String[] args) throws IOException{
new ServerMulti();
//build up the socket and server
try{
server = new ServerSocket(PORT);
socket = server.accept();
in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
out = new PrintWriter(socket.getOutputStream());
textArea.append("Connected. "+" Port: " + PORT + "\n");
while(true){
worker w = new worker();
Thread t = new Thread(w);
t.start();
}
}catch (IOException e) {
System.out.println("Accept failed:" +PORT);
System.exit(-1);
}
}
//to the same server, different sockets are created connecting to different client
public static class worker implements Runnable{
public void run() {
String msg;
/*
* the "in.readLine()" give the data only once
* 1. so we save the information to a String from the socket
* 2. Then sent out a feedback to client, through socket
* 3. print out the String we just collected
*/
while(true){
try {
msg = in.readLine();
out.println("Server received : " + msg);
textArea.append(msg+"\n");
} catch (IOException e) {
System.out.println("Fail");
e.printStackTrace();
}
}
}
}
}
There are a couple of problems with this, but in terms of multithreading the server: ServerSocket.accept() blocks until a new client attempts to connect. In your code this only happens once; so only one client will be accepted. The layout should instead be something like this:
ServerSocket ss = new ServerSocket(PORT);
while (LISTENING) {
Socket sock = ss.accept();
Handler handler = new Handler(sock);
new Thread(handler).start();
//With the LISTENING variable dealt with as well
}
Here the class Handler should be a Runnable class that deals with the socket on another thread. The while loop can then go back and accept a new connection.
Related
I'm trying to create a client/server application using sockets, I have 2 JFrames (2 seperate classes) , a user will initially open up the one frame and there's a button to go to the other JFrame, when clicked it disposes the previous frame and opens the new frame.
I'd like to know how I could switch back and forth between these 2 JFrames without my program crashing and needing to forcefully close, I am establishing the connection in the constructor of each JFrame.
try {
server = new Socket("localhost", PORT);
// creates & instantiates objectInput and output streams
getStreams();
} catch (IOException ex) {
System.out.println("error creating socket: " + ex.getMessage());
}
I have these in the constructor of both JFrames
EDIT
Server
public class Testserver {
private ServerSocket serverSocket;
private Socket client;
private final int PORT = 5432;
private ObjectOutputStream objectOutputStream;
private ObjectInputStream objectInputStream;
public Testserver() {
try {
serverSocket = new ServerSocket(PORT);
} catch (IOException ex) {
JOptionPane.showMessageDialog(null, "Error: " + ex.getMessage());
System.out.println("error creating server socket: " + ex.getMessage());
}
}
private void listenForClient() {
try {
System.out.println("Server is running and is waiting/listening for a connection to be established.");
////JOptionPane.showMessageDialog(null, "Server is running and is waiting/listening for a connection to be established." );
client = serverSocket.accept();
System.out.println("A client has connected");
objectOutputStream = new ObjectOutputStream(client.getOutputStream());
objectInputStream = new ObjectInputStream(client.getInputStream());
processClient();
} catch (IOException ex) {
JOptionPane.showMessageDialog(null, "Error: " + ex.getMessage());
}
}
private void processClient() {
do {
try {
String messageFromClient = (String) objectInputStream.readObject();
// check for clients requests and handle them (database etc)
System.out.println("[CLIENT] " + messageFromClient);
} catch (IOException ex) {
Logger.getLogger(Testserver.class.getName()).log(Level.SEVERE, null, ex);
break;
} catch (ClassNotFoundException ex) {
Logger.getLogger(Testserver.class.getName()).log(Level.SEVERE, null, ex);
break;
}
} while (true);
closeConnections();
}
private void closeConnections() {
try {
objectInputStream.close();
objectInputStream.close();
client.close();
} catch (IOException ex) {
JOptionPane.showMessageDialog(null, "Error: " + ex.getMessage());
}
}
public static void main(String[] args) {
new Testserver().listenForClient();
}
}
CustomerGUI
public class CustomerGUI extends JFrame implements ActionListener{
private Socket server;
private ObjectOutputStream objectOutputStream;
private ObjectInputStream objectInputStream;
private JPanel panel1;
private JButton btnAdminGUI;
private final int PORT = 5432;
public CustomerGUI() {
btnAdminGUI = new JButton("Go to Admin GUI");
try {
server = new Socket("localhost", PORT);
System.out.println("Connected to server");
objectOutputStream = new ObjectOutputStream(server.getOutputStream());
objectInputStream = new ObjectInputStream(server.getInputStream());
objectOutputStream.writeObject("from Customer");
objectOutputStream.flush();
} catch (IOException ex) {
JOptionPane.showMessageDialog(null, "Error: " + ex.getMessage()););
}
btnAdminGUI.addActionListener(this);
}
public void setGUI() {
add(btnAdminGUI);
setSize(300, 400);
setVisible(true);
}
public static void main(String[] args) {
new CustomerGUI().setGUI();
}
#Override
public void actionPerformed(ActionEvent e) {
if(e.getSource() == btnAdminGUI) {
new AdminGUI().setGUI();
dispose();
}
}
}
AdminGUI
public class AdminGUI extends JFrame implements ActionListener{
private Socket server;
private ObjectOutputStream objectOutputStream;
private ObjectInputStream objectInputStream;
private JPanel panel1;
private JButton btnCustomerGUI;
private final int PORT = 5432;
public AdminGUI() {
btnCustomerGUI = new JButton("Go to Customer GUI");
try {
server = new Socket("localhost", PORT);
System.out.println("Connected to server");
objectOutputStream = new ObjectOutputStream(server.getOutputStream());
objectInputStream = new ObjectInputStream(server.getInputStream());
objectOutputStream.writeObject("from Admin");
objectOutputStream.flush();
} catch (IOException ex) {
JOptionPane.showMessageDialog(null, "Error: " + ex.getMessage());
}
btnCustomerGUI.addActionListener(this);
}
public void setGUI() {
add(btnCustomerGUI);
setSize(300, 400);
setVisible(true);
}
#Override
public void actionPerformed(ActionEvent e) {
if(e.getSource() == btnCustomerGUI) {
new CustomerGUI().setGUI();
dispose();
}
}
}
I'll post a solution this because I have done something similar recently, but I don't have a UI for it yet.
The idea is MVC, with two models, a Server and Client and there are two Views, JPanels, which can be displayed in a window/jframe/dialog, whichever is appropriate. The Controller is essentially an api for interacting with the model, for this simple example the controllers will be mixed with the models. I think this example has a lot of flaws, but I think it gives a good idea of what needs to be done.
A button starts a course of action,
serverControls.addActionListener( evt ->{ } );
The button does one of two actions, either it starts the server or it stops the server.
serverLoop.submit( ()->{
server.listen();
//should notify a listener that the server has stopped.
});
serverLoop is another thread of execution, server.listen() is a long running task. It shouldn't return until we want the server to stop listening.
The other button is on the client. It has a similar structure..
clientControls.addActionListener( evt->{
clientLoop.submit( () -> client.connect( server ) );
clientLoop.submit(
() -> SwingUtilities.invokeLater(
() -> response.setText( client.communicate() )
)
);
});
First the client is going to connect, then it communicates.
import javax.swing.*;
import java.util.concurrent.*;
public class ClientServerApp{
static class Server{
volatile boolean available = false;
public void listen(){
try{
available = true;
synchronized( this ){
wait(5000);
}
} catch(Exception e){
e.printStackTrace();
//if the server terminates unexpectedly.
}finally{
available = false;
}
}
public void stopListening(){
}
}
static class Client{
Server connected;
public void connect(Server host){
connected = host;
}
public String communicate(){
if(connected != null){
if(connected.available){
return "connected";
} else{
return "cannot connect";
}
}
return "no host";
}
}
public static void main(String[] args){
Server server = new Server();
Client client = new Client();
ExecutorService serverLoop = Executors.newSingleThreadExecutor();
JPanel serverView = new JPanel();
JButton serverControls = new JButton("start");
serverView.add( serverControls );
serverControls.addActionListener( evt ->{
if(serverControls.getText().equals("start") ){
serverControls.setText("stop");
serverLoop.submit( ()->{
server.listen();
//should notify a listener that the server has stopped.
});
} else{
server.stopListening();
serverLoop.submit( ()->{
//will be run after the listen loop has completed.
SwingUtilities.invokeLater( () - >
serverControls.setText("start")
);
});
}
} );
JPanel clientView = new JPanel();
JButton clientControls = new JButton("connect");
JTextField response = new JTextField(40);
clientView.add( clientControls );
clientView.add(response);
ExecutorService clientLoop = Executors.newSingleThreadExecutor();
System.out.println("creating client action listener");
clientControls.addActionListener( evt->{
clientLoop.submit( () -> client.connect( server ) );
clientLoop.submit( () ->
SwingUtilities.invokeLater(
() -> response.setText( client.communicate() )
)
);
});
JFrame mainWindow = new JFrame();
mainWindow.setContentPane( serverView);
mainWindow.pack();
mainWindow.setVisible(true);
JDialog clientWindow = new JDialog( mainWindow, "Client Window");
clientWindow.setContentPane(clientView);
clientWindow.pack();
clientWindow.setVisible(true);
}
}
In a more complete example, you would probably have a Listener interface, so that your swing gui can response to changes in state of the server or the client, and a controller that manages the threads.
I'm creating a server based chat program that has multiple worker threads each handling a client on a socket. It has a single server socket that passes off the client to the worker thread.
public static void main(String[] args) {
ExecutorService executor = Executors.newFixedThreadPool(5);
ServerSocket serverSocket = null;
try {
serverSocket = new ServerSocket(4001);
System.out.println("Listening server");
while (true) {
Socket socket = serverSocket.accept();
System.out.println("Connected");
Random rand= new Random();
int port=4001+rand.nextInt(5);
Worker worker = new Worker(port);
executor.execute(worker);
System.out.println("Thread started");
new PrintWriter(socket.getOutputStream(), true).println(port);
socket.close();
System.out.println("Closed");
// break;
}
} catch (IOException e) {
e.printStackTrace();
}
}
public class Worker implements Runnable {
private int port;
public Worker(int i) {
port=i;
}
#Override
public void run() {
worker();
}
private static Socket socket;
private static PrintWriter out;
private static BufferedReader in;
private void worker() {
try {
ServerSocket serverSocket = new ServerSocket(port);
socket = serverSocket.accept();
out = new PrintWriter(socket.getOutputStream(), true);
in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
String line;
while ((line = in.readLine()) != null) {
System.out.println("Received: " + line);
switch (line) {
case ("Button Press"):
System.out.println("Handled button");
out.println("Button acknowledged");
break;
case ("Give me some data"):
System.out.println("Handled data");
out.println("Have some data");
break;
}
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
out.close();
in.close();
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
This works fine, however the issue is when I have an automated request by the client to check for messages and the user provides some input at the same type. This causes conflict as the actual methods take a couple of seconds to run and if more input is received then the request won't be handled because its in the middle of the method. For example:
private static BufferedReader in;
private static PrintWriter out;
public static void main(String[] args) {
Main main=new Main();
main.createWindow();
try {
Socket init = new Socket(InetAddress.getLocalHost(), 4001);
int port
=Integer.parseInt
(new BufferedReader
(new InputStreamReader(init.getInputStream())).readLine());
init.close();
Socket socket=new Socket(InetAddress.getLocalHost(), port);
out = new PrintWriter(socket.getOutputStream(), true);
in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
while(true){
try {
Thread.sleep(5000);
out.println("Give me some data");
if(in.readLine().equals("Have some data")){
System.out.println("Data recieved");
}else{
System.out.println("Data not recieved");
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
private void createWindow(){
JFrame frame =new JFrame("This is a button");
Container pane=getContentPane();
JButton button=new JButton("This is a button");
button.addActionListener(this);
pane.add(button);
frame.setTitle("Messaging");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setContentPane(pane);
frame.setVisible(true);
frame.setSize(400, 350);
}
#Override
public void actionPerformed(ActionEvent e) {
System.out.println("Button press");
try {
out.println("Button Press");
if(in.readLine().equals("Button acknowledged")){
System.out.println("Button complete");
}else{
System.out.println("Button fail");
}
} catch (IOException e1) {
e1.printStackTrace();
}
}
If the button is pressed whilst the server is fetching data it conflicts and the wrong response is sent. So how would I deal with this? If I create a separate socket to just handle automated checks that's double the amount of threads the server has to cope with. Is there a better solution?
And please can you try to explain as this is a new area for me.
Your problem is that you have two threads interacting with the socket, your main thread and the Swing event handling thread. This means that the two threads can queue two different things, and the responses may be picked up by the wrong thread. The simplest way to get where you want to be is to put all the socket interaction in one thread.
There are two ways to do this. One would be for the main thread to queue the periodic automated checks to the Swing event handling thread. However, that's a bit complicated and also possibly buggy as the actual Swing threading model is different from what's documented.
The other way to do it would be for the Swing event handling thread to queue button presses to the main thread. In this case, you would queue the button press to the main thread using proper synchronization. The main loop of the main thread would check for button presses and send them through the socket and wait for the proper response.
I am trying to set up a server class, and i'm running into a issue in which no error is being thrown.
import java.io.*;
import java.net.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class Server extends JFrame implements Runnable{
private static final long serialVersionUID = 1L;
private JTextField userText;
private JTextArea chatWindow;
private ObjectOutputStream output;
private ObjectInputStream input;
private ServerSocket server;
private Socket connection;
//constructor
public Server(){
super("Server");
userText = new JTextField();
userText.setEditable(false);
userText.addActionListener(
new ActionListener(){
public void actionPerformed(ActionEvent event){
sendMessage(event.getActionCommand());
userText.setText("");
}
}
);
add(userText, BorderLayout.NORTH);
chatWindow = new JTextArea();
add(new JScrollPane(chatWindow));
setSize(300, 150); //Sets the window size
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setVisible(true);
}
public void run(){
try{
server = new ServerSocket(6789, 100); //6789 is a dummy port for testing, this can be changed. The 100 is the maximum people waiting to connect.
while(true){
try{
//Trying to connect and have conversation
waitForConnection();
setupStreams();
whileChatting();
}catch(EOFException eofException){
showMessage("\n Server ended the connection! ");
} finally{
closeConnection(); //Changed the name to something more appropriate
}
}
} catch (IOException ioException){
ioException.printStackTrace();
}
}
//wait for connection, then display connection information
private void waitForConnection() throws IOException{
showMessage(" Waiting for someone to connect... \n");
connection = server.accept();
showMessage(" Now connected to " + connection.getInetAddress().getHostName());
}
//get stream to send and receive data
private void setupStreams() throws IOException{
output = new ObjectOutputStream(connection.getOutputStream());
output.flush();
input = new ObjectInputStream(connection.getInputStream());
showMessage("\n Streams are now setup \n");
}
//during the chat conversation
private void whileChatting() throws IOException{
String message = " You are now connected! ";
sendMessage(message);
ableToType(true);
do{
try{
message = (String) input.readObject();
showMessage("\n" + message);
}catch(ClassNotFoundException classNotFoundException){
showMessage("The user has sent an unknown object!");
}
}while(!message.equals("CLIENT - END"));
}
public void closeConnection(){
showMessage("\n Closing Connections... \n");
ableToType(false);
try{
output.close(); //Closes the output path to the client
input.close(); //Closes the input path to the server, from the client.
connection.close(); //Closes the connection between you can the client
}catch(IOException ioException){
ioException.printStackTrace();
}
}
//Send a mesage to the client
private void sendMessage(String message){
try{
output.writeObject("SERVER - " + message);
output.flush();
showMessage("\nSERVER -" + message);
}catch(IOException ioException){
chatWindow.append("\n ERROR: CANNOT SEND MESSAGE, PLEASE RETRY");
}
}
//update chatWindow
private void showMessage(final String text){
SwingUtilities.invokeLater(
new Runnable(){
public void run(){
chatWindow.append(text);
}
}
);
}
private void ableToType(final boolean tof){
SwingUtilities.invokeLater(
new Runnable(){
public void run(){
userText.setEditable(tof);
}
}
);
}
}
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
public class Menu extends JFrame implements ActionListener{
private static final long serialVersionUID = 1L;
private JButton server;
private JButton client;
private static String Host;
public Menu(){
this.getContentPane().setPreferredSize(new Dimension(300, 300));
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setLayout(null);
this.pack();
this.setLocationRelativeTo(null);
server = new JButton();
server.setText("server");
server.setBounds(0, 0, 300, 150);
server.addActionListener(this);
client = new JButton();
client.setText("client");
client.setBounds(0, 150, 300, 150);
client.addActionListener(this);
this.add(server);
this.add(client);
this.setVisible(true);
}
public void actionPerformed(ActionEvent e) {
if(e.getSource() == server){
Server s = new Server();
s.run();
}
if(e.getSource() == client){
Host = JOptionPane.showInputDialog("Enter Server I.P.");
Client c = new Client(Host);
c.run();
}
}
public static void main(String[] args){
new Menu();
}
}
The JFrame is created, but can only be exited by the termination button in eclipse, not the default_exit_on_close operation, and is see through (not opaque as it should be). The client class acts the same way, leading me to believe that the issue is the:
Server s = new Server();
s.run();
since if i have the main method call that, everything works fine.
Your constructor can never exit.
This waitForConnection()/setUpStreams() logic is not appropriate. You need an accept loop that just accepts sockets, constructs a Runnable to handle the connection, and starts a thread. That loop should be in a separate run() method and be executed in a separate thread. Not in a constructor.
NB The Socket that is returned by accept() must be a local variable in that loop, otherwise you are liable to run into concurrency problems.
i'm trying to create a server that allows several clients, I can connect with one client fine, but I cant with two. The was I try to connect two clients to the server is by creating two client objects and one server in a main method. Here is the code for the Server:
public class DraughtsSever extends JFrame{
JPanel panel;
JTextArea gamesList;
ServerSocket draughtsSS;
JScrollPane scroll;
Socket s;
int i = 0;
DraughtsSever(){
panel = new JPanel();
panel.setPreferredSize(new Dimension(350,350));
gamesList = new JTextArea();
//scroll.setPreferredSize(new Dimension(350,350));
gamesList.setPreferredSize(new Dimension(300,300));
//scroll.add(GamesList);
//scroll = new JScrollPane(GamesList, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
//panel.add(scroll);
this.add(panel);
panel.add(gamesList);
this.setSize(400,400);
this.setVisible(true);
this.setTitle("Draughts Server");
panel.add(gamesList);
allowConnections();
}
public void allowConnections(){
gamesList.append("Server listening on port 50000...");
try{
while(true){
draughtsSS = new ServerSocket(50000);
//s = draughtsSS.accept();
new Thread(new TestT(draughtsSS.accept())).start();
//t.start();
}
}
catch(IOException e){
System.out.println("Error :"+e.getMessage());
}
}
class TestT implements Runnable{
Socket s;
TestT(Socket s){
this.s = s;
}
public void run(){
try{
PrintWriter out = new PrintWriter(s.getOutputStream());
Scanner in = new Scanner(s.getInputStream());
gamesList.append("\n"+s.getInetAddress().toString() +" has connected.");
JOptionPane.showMessageDialog(new JFrame(), "Successfully connected");
out.println("hello");
System.out.println(s.getInetAddress().toString() +" has connected.");
}
catch(IOException e){
}
}
}
}
Here is the method in the client that I use to connect to the server
private void setupConnection(String serverIP, String port){
int portInt = Integer.parseInt(port);
try{
InetAddress IP = InetAddress.getByName(serverIP);
int intPort = Integer.parseInt(port);
Socket clientSocket = new Socket(IP,intPort);
JOptionPane.showMessageDialog(new JFrame(), "Successfully connected to Server");
PrintWriter out = new PrintWriter(clientSocket.getOutputStream());
Scanner in = new Scanner(clientSocket.getInputStream());
//in.
}
catch(Exception e){
System.out.println("ErrorC :" +e.getMessage());
}
}
}
}
I think you should only create the server socket once, so move this outside of the while loop:
draughtsSS = new ServerSocket(50000);
You don't need to keep re-creating this because when clients connect they automatically get moved to a different socket when you call
draughtsSS.accept()
I am using Java Socket programming. When I click button the first time, the Socket is working perfectly. But button and other functions are not working anymore. All are disabled or do not react when I click them.
This is Main Class
JButton btnRemote = new JButton("Remote ");
btnRemote.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
ServerInitiator ser=new ServerInitiator();
ser.initialize(4444);
}
});
Socket Class
public static void main(String args[]){
String port = JOptionPane.showInputDialog("Please enter listening port");
new ServerInitiator().initialize(5555);
}
public void initialize(int port){
try {
ServerSocket sc = new ServerSocket(port);
//Show Server GUI
drawGUI();
// drawGUI();
//Listen to server port and accept clients connections
while(true){
Socket client = sc.accept();
System.out.println("New client Connected to the server");
//Per each client create a ClientHandler
new ClientHandler(client,desktop);
}
} catch (IOException ex) {
ex.printStackTrace();
}
}
You should separate your GUI from your server logic. Anyway,
you can make the ClientHandler a Runnable, then spawn a new Thread for each new client:
// server is listening
while (true){
...
new Thread(new ClientHandler(client, desktop)).start();
...
}