I'm currently having some issues running this small battleship program I am working on. I have two GUI, one for the server and one for the client. When I click the "Start Server" jButton, the program will freeze until it receives something from the client side.
Image of the GUI if it helps:
I have no issues unfreezing it by starting the client program, my issue is, how can I make it so that it just doesn't freeze while waiting? thanks a lot.
package battleship;
import javax.swing.JOptionPane;
import java.io.IOException;
import java.util.Scanner;
import java.net.ServerSocket;
import java.net.Socket;
import java.io.PrintStream;
import java.net.InetAddress;
import java.net.SocketAddress;
import java.net.UnknownHostException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JFrame;
/**
*
* #author ftonye
*/
public class BackGroundCom implements Runnable {
private int port;
private ServerSocket ss;
private Socket cs;
private Scanner reader;
private PrintStream writer;
public int missileIncomming;
public int missileOutgoing;
public Boolean dataToSend;
InetAddress sa = null;
public BackGroundCom(int port) {
this.port = port;
dataToSend = true;
missileOutgoing = 100;
startServer();
}
private void startServer() {
try {
sa = InetAddress.getLocalHost();
System.out.println(sa);
} catch (UnknownHostException ex) {
Logger.getLogger(BackGroundCom.class.getName()).log(Level.SEVERE, null, ex);
}
System.out.println("Server started");
try {
ss = new ServerSocket(this.port);
cs = ss.accept();
JOptionPane.showMessageDialog(null, "Server accept connection from" + cs.getInetAddress().getHostAddress());
} catch (IOException ex) {
JOptionPane.showMessageDialog(null, ex.getMessage());
}
System.out.println("Server accept connection");
}
Part of my BattleSea.java, which is where I click the button before it freezes:
btnStartServer = new JButton("Start Server");
btnStartServer.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent ae) {
com = new BackGroundCom(Integer.parseInt(txtPortNumber.getText().toString()));
t = new Thread(com);
t.start();
}
});
I am trying to understand how to get it to never freeze. Thanks
I expect that it is the call cs = ss.accept(); that is blocking. This is going to block until the client connects to the server. You run this in response to pushing the button, because in your button action code, you construct a BackGroundCom, and that object's constructor calls startServer, which executes this accept() line directly.
It seems like you are trying to set up the BackGroundCom object so that what it does occurs in a background thread, but what I describe above all happens before you create and run the thread. Maybe what you want to do is move the startServer call into the run() method of the BackGroundCom object. I don't see a run() method in BackGroundCom, although it implements Runnable. I assume it's further down in the code. Without it, this code wouldn't even compile.
Steve is right. ServerSocket.accept(); method will block until a connection is made. You have to put it inside another Thread so it won't block the EDT(Event Dispatching Thread). EDT is where your GUI runs.
public BackGroundCom(int port) {
this.port = port;
dataToSend = true;
missileOutgoing = 100;
new Thread(() -> (startServer()).start(); // run in new thread
}
Related
Hi I'm small a little issue trying to append some text to a JTextArea from another class within the same package. Below is the main class that pertains to the JFrame:
public class Client extends JFrame{
//Static variables
private static final String host = "localhost";
private static final int portNumber = 4444;
//Variables
private String userName;
//JFrame Variables
private JPanel contentPanel;
private JTabbedPane panel_Social;
private JPanel jpanel_Social;
private JPanel jpanel_Chat;
private JTextArea textArea_Receive;
private JTextField textField_Send;
private JTextArea textArea_ClientList;
private JButton btn_Enter;
public JTextArea getTextArea_Receive(){
return this.textArea_Receive;
}
//Constructor
private Client(String userName, String host, int portNumber){
this.userName = userName;
this.serverHost = host;
this.serverPort = portNumber;
}
public void main(String args[]){
//Requests user to enter name
String readName = null;
Scanner scan = new Scanner(System.in);
System.out.println("Please enter username");
readName = scan.nextLine();
//Start client
Client client = new Client(readName, host, portNumber);
client.startClient(scan);
}
private void startClient(Scanner scan){
try{
//Create new socket and wait for network communication
Socket socket = new Socket(serverHost, serverPort);
Thread.sleep(1000);
//Create thread and start it
serverThread = new ServerThread(socket, userName);
Thread serverAccessThread = new Thread(serverThread);
serverAccessThread.start();
}
}
}
Below is the serverThread class
public class ServerThread implements Runnable{
private Socket socket;
private String userName;
private boolean isAlived;
private final LinkedList<String> messagesToSend;
private boolean hasMessages = false;
//Constructor
ServerThread(Socket socket, String userName){
this.socket = socket;
this.userName = userName;
messagesToSend = new LinkedList<String>();
}
public void run(){
try{
Client test1 = new Client();
JTextArea test2 = test1.getTextArea_Receive();
String test3 = "Hello World";
test2.append(test3);
} catch (IOException e){
}
}
I included test variables just to easily recreate the issue but whenever the append function is run nothing appears in the text area of the jFrame. In my scenario I'm having the client receive text from a server then append it to the text box.
BTW I'm using the IntelliJ GUI designer for the JFrame. I've only included code needed to recreate the problem. I'm still trying to create MCVE questions so feel free to let me know mistakes I made.
You should pass Client into ServerThread via the constructor. The Client you are instantiating within run() is not the same reference to the Client you created in main(). So your ServerThread class would be something like
ServerThread(Client client, Socket socket, String userName) {
this.client = client;
this.socket = socket;
this.userName = userName;
messagesToSend = new LinkedList<String>();
}
public void run() {
try
{
JTextArea test2 = this.client.getTextArea_Receive();
String test3 = "Hello World";
test2.append(test3);
}
catch (IOException e)
{}
}
Your startClient() method would be updated to something like this
private void startClient(Client client, Scanner scan)
{
try
{
//Create new socket and wait for network communication
Socket socket = new Socket(serverHost, serverPort);
Thread.sleep(1000);
//Create thread and start it
ServerThread serverThread = new ServerThread(client, socket, userName);
serverAccessThread.run();
}
}
All that being said,
I would recommend moving your main() out of Client and into a class that isn't so coupled to the Swing UI code. Something like this:
public class MySwingApplication {
private static final String host = "localhost";
private static final int portNumber = 4444;
public static void main(String[] args) {
// Requests user to enter name
// Start client
}
}
Your Client is then built more like an instance object
public class Client extends JFrame {
public JTextArea getTextArea_Receive(){
// Return the text area
}
// Constructor -- public to allow instantiation from main()
public Client(String userName, String host, int portNumber) {
// Do stuff
}
private void startClient(Scanner scan) {
// Show the JFrame on screen
// Spawn Server
}
}
the append function is run nothing appears in the text area of the jFrame
There's not enough information available in you question to ascertain why this might be happening, how ever, there are a number of important pieces of information you need to take into account.
Swing is single threaded and not thread safe
You want to, as much as possible, decouple of the code
Basically, the first point means that you shouldn't be running any long running or blocking processes within the Event Dispatching Thread AND you should not be modifying the UI (or any state the UI relies on) from outside the context of the Event Dispatching Thread
Start by taking a look at Concurrency in Swing for more details.
The second point means that, for even given part of your code, you want to be asking, "how hard would it be to replace it with some other implementation?" - If the amount of work scares you, then your code is probably to tightly coupled.
To that end, I started with...
public interface Client {
public void append(String message);
}
This is really basic, but it means that some component can send a message to some other component and neither should care about each other beyond this capability.
Next, I looked at ServerThread. Basically this class becomes responsible for the management of the Socket and delivery of the messages to the Client. Because of the requirements of Swing, I've used a SwingWorker. This allows me to run the Socket code on a background thread, but ensure that the messages are delivered to the Client within the context of the Event Dispatching Thread
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.Socket;
import java.util.List;
import javax.swing.SwingWorker;
public class ServerThread extends SwingWorker<Void, String> {
private String host;
private int port;
private Client client;
//Constructor
ServerThread(String host, int port, Client client) {
this.host = host;
this.port = port;
this.client = client;
}
#Override
protected void process(List<String> chunks) {
for (String message : chunks) {
client.append(message);
}
}
#Override
protected Void doInBackground() throws Exception {
try (Socket socket = new Socket(host, port)) {
try (BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()))) {
String text = null;
while ((text = reader.readLine()) != null) {
System.out.println(text);
if (text.equals("<stop>")) {
break;
}
publish(text);
}
}
} catch (IOException exp) {
exp.printStackTrace();
publish("Failed to establish connection to " + host + ":" + port + " - " + exp.getMessage());
}
return null;
}
}
And then the actual UI client itself...
import java.awt.BorderLayout;
import java.awt.EventQueue;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
public class Main {
public static void main(String[] args) {
new Main();
}
//Constructor
public Main() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
ClientUI clientUI = new ClientUI();
ServerThread thread = new ServerThread("localhost", 4321, clientUI);
thread.execute();
JFrame frame = new JFrame("Client");
frame.add(clientUI);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class ClientUI extends JPanel implements Client {
private JTextArea ta;
public ClientUI() {
setLayout(new BorderLayout());
ta = new JTextArea(10, 20);
add(new JScrollPane(ta));
}
#Override
public void append(String message) {
ta.append(message + "\n");
}
}
}
Not much going on here
Finally, I wrote a simple Server test the code, which simply sends the current date/time as a String to the connected client.
When I say simple, I mean simple. This is intended, by design, to tell with a single client connection and is meant only for testing the above code
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.text.DateFormat;
import java.util.Date;
public class Server {
public static void main(String[] args) {
DateFormat format = DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.LONG);
try (ServerSocket server = new ServerSocket(4321)) {
Socket socket = server.accept();
System.out.println("Connected");
try (BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()))) {
while (true) {
System.out.println("Write >> ");
writer.write(format.format(new Date()));
writer.newLine();
writer.flush();
Thread.sleep(1000);
}
}
} catch (IOException | InterruptedException exp) {
exp.printStackTrace();
}
}
}
To test it all, start the Server and then run the Main class
I've a client class which tries to connect to a server. But as you know you can't
execute network operations on the Main UI thread. So I've to create different threads for each operation.
Current code:
package com.example.justus.rocchat;
import android.os.AsyncTask;
import android.util.JsonWriter;
import java.io.BufferedWriter;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.Socket;
import java.net.URL;
import java.net.UnknownHostException;
/**
* Created by justus on 13-1-2015.
*/
public class Client
{
private String name;
private int port;
private String hostAddress;
private Socket socketClient;
private MainActivity mainActivity;
public boolean isConnected;
public Client(MainActivity mainActivity, String hostAdress, int port)
{
this.hostAddress = hostAdress;
this.port = port;
this.mainActivity = mainActivity;
}
public void send(final byte[] data)
{
Thread sendThread = new Thread(new Runnable() {
public void run()
{
try
{
DataOutputStream out = new DataOutputStream(socketClient.getOutputStream());
out.write(data);
System.out.println("writed data");
} catch (IOException ex) {
}
}
});
sendThread.start();
}
public void connect()
{
Thread connectThread = new Thread(new Runnable() {
public void run() {
try
{
System.out.println("trying to connect");
socketClient = new Socket(hostAddress, port);
isConnected = true;
}
catch(UnknownHostException ex)
{
System.out.println("ex:" + ex.getMessage());
}
catch (IOException ex)
{
System.out.println("ex:" + ex.getMessage());
}
}
});
connectThread.start();
}
}
Isn't this a little to much? Are there any better ways to handle this operations?
Already thanks for your time.
AsyncTask is the accepted way of handling asynchronous operations. It is a wrapper around the Thread class and is part of the Android SDK. They should only be used for operations that last under a few seconds, for longer operations you should use a Service.
developer.android.com/reference/android/os/AsyncTask.html
There are 2 options that you have
Use AsyncTask - much easier, object oriented that using threads but only for shortlived tasks (under 30 secs)
Use RoboSpice - https://github.com/stephanenicolas/robospice
Of the two, I prefer RoboSpice
I am trying to implement a simple program which allows to join a Multicast group on a virtual IP address and listen packets that are sent to this IP (that is why I created the class ThreadGroup).
My code is :
import java.io.ByteArrayInputStream;
import java.io.DataInputStream;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.InetAddress;
import java.net.MulticastSocket;
import java.util.HashSet;
import java.util.Random;
import java.util.Set;
public class MulticastTest {
private Set<Thread> threads;
public MulticastTest(){
threads = new HashSet<Thread>();
}
/**
* #param args
*/
public static void main(String[] args) {
MulticastTest test = new MulticastTest();
test.joinGroup("dogg",test.createAdr(),32445);
}
private void joinGroup(String name, String adr, int port){
try {
MulticastSocket multi = new MulticastSocket(port);
multi.joinGroup(InetAddress.getByName(adr));
ThreadGroup newThread = new ThreadGroup(multi);
threads.add(newThread);
newThread.start();
System.out.println("Congrats you joined the group "+name+".");
}catch (NumberFormatException | IOException e) {
e.printStackTrace();
}
}
private String createAdr(){
Random r = new Random();
return r.nextInt(16)+224 + "." + r.nextInt(256) + "." + (r.nextInt(255)+1) + "." + r.nextInt(56);
}
/**
* Thread to receive datagram that are sent to the group
*/
class ThreadGroup extends Thread {
MulticastSocket multiSocket;
public ThreadGroup(MulticastSocket m) throws IOException{
multiSocket = m;
start();
}
public void run() {
DatagramPacket message;
byte[] contMessage;
String texte;
while(true) {
contMessage = new byte[1024];
message = new DatagramPacket(contMessage, contMessage.length);
try {
multiSocket.receive(message);
texte = (new DataInputStream(new ByteArrayInputStream(contMessage))).readUTF();
System.out.println(texte);
}catch(Exception e){
e.printStackTrace();
}
}
}
}
}
So in my main, I try to connect to a random Multicast InetAddress and wait packets. But when I run my program, I get :
Exception in thread "main" java.lang.IllegalThreadStateException
at java.lang.Thread.start(Thread.java:705)
at MulticastTest.joinGroup(MulticastTest.java:35)
at MulticastTest.main(MulticastTest.java:25)
Could somebody explain to me what I am doing wrong ?
You are calling start() on your thread twice, once in the constructor and once in joinGroup(). Remove start() from the constructor that should get you past the IllegalThreadSTateException.
you have start thread two times.
1) ThreadGroup constructor
2) newThread.start();
use either of one ,your problem may be solved.
I am currently working on a multi-threaded socket based Java program that should allow multiple threads to send request to this program. This should be handled with the event activation but I am having hard time understanding events and their implementation. Below is the code that should allow more than 1 thread to communicate with the program but I only have 1 thread there. Can someone please shed more light on this? Much appreciated.
//this is a a threads class
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.Socket;
import java.util.logging.Level;
import java.util.logging.Logger;
public class Niti implements Runnable {
public String line;
public Socket soc;
public boolean active=true;
public Niti(Socket soc)
{
this.soc=soc;
this.line=line;
this.active=active;
}
public synchronized void run() {
try {
BufferedReader br = new BufferedReader(new InputStreamReader(soc.getInputStream()));
line=br.readLine();
while(line!=null && !line.equals("")){
if(!this.active)
break;
System.out.println(line);
line=br.readLine();
}
BufferedOutputStream bos = new BufferedOutputStream(soc.getOutputStream());
bos.write("Poruka iz Programa".getBytes());
}
catch (IOException ex) {
Logger.getLogger(Niti.class.getName()).log(Level.SEVERE, null, ex);
}
try {
soc.close();
} catch (IOException ex) {
Logger.getLogger(Niti.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;
//and this is the main class
public class Server{
public static synchronized void main(String[] args) throws IOException, InterruptedException{
ServerSocket ss = new ServerSocket(1000);
while(true){
Socket sokit = ss.accept();
Niti n = new Niti(sokit);
while(true){
Thread t = new Thread(n);
t.start();
//Thread.sleep(4000);
//n.active=false;
System.out.println("nit broj:" + Thread.currentThread().getId());
}
}
}
}
Well, without look to Niti class (that is the client handler class as I suppose) you have a logic error here:
while(true){
Socket sokit = ss.accept();
Niti n = new Niti(sokit);
while(true){ // LOGIC ERROR!!!
Thread t = new Thread(n);
t.start();
//Thread.sleep(4000);
//n.active=false;
System.out.println("nit broj:" + Thread.currentThread().getId());
}
}
With the above code you are creating infinit Threads after pass the first time through accept method. What you have to do is to remove the second while(true), like this:
while(true){
Socket sokit = ss.accept();
Niti n = new Niti(sokit);
Thread t = new Thread(n);
t.start();
//Thread.sleep(4000);
//n.active=false;
System.out.println("nit broj:" + Thread.currentThread().getId());
}
I would really appreciate help with my program. It is some sort of chat server with multiple clients.
Here's the server code:
package com.server;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
public class Server {
public static int PORT;
private ServerSocket server;
private Socket socket;
public Server(int port) throws IOException {
PORT = port;
server = new ServerSocket(PORT);
System.out.println("server started");
try {
while (true) {
socket = server.accept();
try {
new ServeClient(socket);
} catch (IOException e) {
socket.close();
}
}
} finally {
server.close();
}
}
public static void main(String[] args) throws IOException {
int port = Integer.parseInt(args[0]);
Server server = new Server(port);
}
}
I start the server and then create a Client. The server receives connection socket from socket
and creates a ServeClient Thread.
Here's ServeClient code:
package com.server;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.Enumeration;
import java.util.Vector;
import com.gui.WindowManager;
public class ServeClient extends Thread {
private final Socket socket;
private BufferedReader in;
private PrintWriter out;
private String msg;
public static final String ENDSTRING = "END";
public static Vector clients = new Vector();
public ServeClient(final Socket socket) throws IOException {
this.socket = socket;
System.out.println("socket " + socket);
in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(
socket.getOutputStream())), true);
start();
}
public void run() {
try {
clients.add(this);
while (true) {
msg = in.readLine();
if (msg == ENDSTRING)
break;
broadcast(msg);
}
System.out.println("closing...");
} catch (IOException e) {
System.err.println("IO EXCEPTION");
} finally {
try {
socket.close();
} catch (IOException e) {
System.err.println("SOCKET NOT CLOSED");
}
}
}
#SuppressWarnings("deprecation")
public void broadcast(String msg) {
synchronized (clients) {
Enumeration<ServeClient> e = clients.elements();
while (e.hasMoreElements()) {
ServeClient serveClient = e.nextElement();
try {
synchronized (serveClient.out) {
serveClient.out.println(msg);
}
} catch (Exception eee) {
serveClient.stop();
}
}
}
}
}
What i get is a NullPointerException when ServeClient invokes run() method
server started
socket Socket[addr=/127.0.0.1,port=51438,localport=8888]
Exception in thread "Thread-0" java.lang.NullPointerException
at com.server.ServeClient.run(ServeClient.java:33)
line 33 is the line with first "try" statement in ServeClient run() method
com.server.ServeClient.run(ServeClient.java:33)
I don't believe that it's happening at the try.
Open up an IDE, turn on debugging, and step through until you can see what's happening. That's the fastest way to figure out what you've missed.
There's an object that you're assuming is fine that is not. Find it.
Here's an example of how to do this properly:
http://www.kodejava.org/examples/216.html
Your problem is with the order in which static instance variables are initialised. Try doing something like:
...
private static Vector clients = null;
...
if (clients==null) {
clients = new Vector(); // consider putting this in a synchronized block
}
before you add the client to the vector.
Sorry for necroing such an old issue but it seemed like this problem wasn't resolved, so I'll give a bit of input from my end.
I've had a similar problem and the compiler also kept telling me that the problem was at the start() method. However, when I commented out the thread part and just ran the code on the same thread as the UI, the compiler directed me to the real source of the problem: the code inside the thread.
After making sure the code didn't give an error, I enclosed the code with the original thread code, and it stopped giving me the NullPointerException error.
Hope this helps someone along the way.
Remove the duplicate class declaration in JPanel.
I was trying to run a timer thread that updated a clock in the main application window.
I had created the JFrame with Eclipse/WindowBuilder and had followed a tutorial on how to make a timer. I had copied the declaration of the textfield into the class declaration to make it available for the entire class, but forgot to remove the Class Id in front of the widget definition. So it still initialized the local instance and not the global one. Thus when I accessed the global one it was still null.