Dobry wieczór. Ponownie zwracam się do Was z prośbą o pomoc. Jestem w trakcie tworzenia prostego komunikatora (jednocześnie może rozmawiać kilka osób, każdy klient ma do dyspozycji jedno okno) z użyciem klas Socket
oraz ServerSocket
. Niestety, okienko klienta od razu uruchamia się zamrożone, bez tytułu i jakichkolwiek pól. Wrzucenie w komentarz uruchamiania wątku przez klasę Main
skutkuje normalnym uruchomieniem okienka. Stąd wnioskuję, że wątek ClientReader
w jakiś sposób blokuje silnik JavyFX. Czy jest to spowodowane przez moją próbę dostępu do pól klasy Controller
? Dziękuję za wszelką pomoc.
package communicate;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class Main extends Application {
private Controller controller;
private ClientReader clientReader;
@Override
public void start(Stage primaryStage) throws Exception{
FXMLLoader loader=new FXMLLoader(getClass().getResource("communicate.fxml"));
Parent root = loader.load();
controller=loader.getController();
//controller.initialize();
primaryStage.setTitle("Projekt");
primaryStage.setScene(new Scene(root));
primaryStage.show();
clientReader=new ClientReader(controller);
Thread cr=new Thread(clientReader);
cr.setDaemon(true);
cr.start();
}
public static void main(String[] args) {
launch(args);
}
}
package communicate;
import javafx.application.Platform;
import javafx.scene.control.TextArea;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket;
import java.util.ArrayList;
import static java.lang.Thread.sleep;
public class ClientReader implements Runnable {
private Socket socket;
private ObjectInputStream objectInputStream;
private ObjectOutputStream objectOutputStream;
private ArrayList<Message> messagesIncoming;
private ArrayList<Message> messagesOutcoming;
private Controller ctrl;
private TextArea conversationArea;
ClientReader(Controller ctrl) {
this.ctrl=ctrl;
conversationArea=this.ctrl.getConversationArea();
socket=ctrl.getSocket();
messagesIncoming=new ArrayList<>();
messagesOutcoming=new ArrayList<>();
try {
objectInputStream = new ObjectInputStream(socket.getInputStream());
objectOutputStream = new ObjectOutputStream(socket.getOutputStream());
}
catch (IOException e) {
e.printStackTrace();
}
}
public void run() {
while(true) {
try {
sleep(100);
readMsgFromServer();
printMessageToClient();
sleep(100);
getMessageFromClient();
sendMsgToServer();
}
catch (Exception e) {
e.printStackTrace();
}
}
}
private synchronized void readMsgFromServer() {
try {
Message msg=(Message)objectInputStream.readObject();
messagesIncoming.add(msg);
}
catch (Exception e) {
e.printStackTrace();
}
}
private synchronized void sendMsgToServer() {
if(!messagesOutcoming.isEmpty()) {
try {
objectOutputStream.writeObject(messagesOutcoming.get(0));
messagesOutcoming.remove(0);
} catch (Exception e) {
e.printStackTrace();
}
}
}
synchronized void getMessageFromClient() {
if(!ctrl.getMessagesOutcoming().isEmpty()) {
Message msg=ctrl.getMessagesOutcoming().remove(0);
messagesOutcoming.add(msg);
}
}
synchronized void printMessageToClient() {
if(!messagesIncoming.isEmpty()) {
Platform.runLater(() -> {
Message msg = messagesIncoming.get(0);
messagesIncoming.remove(0);
conversationArea.appendText(msg.getSender() + ": " + msg.getText() + "\n");
}
);
}
}
}
package communicate;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.fxml.FXML;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.TextArea;
import javafx.scene.control.TextField;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Priority;
import javafx.scene.layout.VBox;
import java.net.InetAddress;
import java.net.Socket;
import java.util.ArrayList;
public class Controller {
private final int port=4000;
@FXML
VBox pane;
@FXML
HBox hboxForButton;
private ArrayList<Message> messagesOutcoming;
private Label nameTextLabel;
private TextField myName;
private String myNick;
private String addresseeNick;
private String msgText;
private Label addresseeNameLabel;
private TextField addressee;
private Label conversationAreaLabel;
private TextArea conversationArea;
private Label yourAnswerLabel;
private TextField yourAnswer;
private Button sendMsgButton;
private Socket socket;
//HashMap<String, TextArea> windowsTextAreas;
public void initialize() throws Exception {
//windowsTextAreas=new HashMap<>();
socket=new Socket(InetAddress.getLocalHost(), port);
nameTextLabel=new Label("Twój myNick:");
myName=new TextField();
addresseeNameLabel=new Label("Nick Twojego rozmówcy:");
addressee=new TextField();
conversationAreaLabel=new Label("Rozmowa:");
conversationArea=new TextArea();
yourAnswerLabel=new Label("Twoja odpowiedź:");
yourAnswer=new TextField();
//dodaję elementy, ustawiam odległości pomiędzy elementami oraz akapity
pane.getChildren().addAll(nameTextLabel, myName, addresseeNameLabel, addressee,
conversationAreaLabel, conversationArea, yourAnswerLabel, yourAnswer);
pane.setSpacing(10); //tylko dla HBox lub VBox!
pane.setPadding(new Insets(10));
sendMsgButton =new Button("Wyślij");
sendMsgButton.setAlignment(Pos.CENTER_RIGHT);
HBox.setHgrow(sendMsgButton, Priority.ALWAYS);
hboxForButton.getChildren().add(sendMsgButton);
hboxForButton.setPadding(new Insets(10));
sendMsgButton.setOnAction(new EventHandler<ActionEvent>() {
//tu muszę odczytać dane z pól tekstowych
@Override
public void handle(ActionEvent actionEvent) {
send(actionEvent);
}
});
}
private synchronized void send(ActionEvent event) {
myNick=myName.getText();
addresseeNick=addressee.getText();
msgText=yourAnswer.getText();
conversationArea.appendText("Ty: "+msgText+"\n");
messagesOutcoming.add(new Message(myNick, addresseeNick, msgText));
}
/*HashMap<String, TextArea> getWindowsTextAreas() {
return windowsTextAreas;
}*/
TextArea getConversationArea() {
return conversationArea;
}
ArrayList<Message> getMessagesOutcoming() {
return messagesOutcoming;
}
Socket getSocket() {
return socket;
}
}
package communicate;
import java.io.Serializable;
public class Message implements Serializable {
private final String sender;
private final String addressee;
private final String text;
/**
*
* @param sender nazwa (ID) nadawcy
* @param addressee nazwa (ID) adresata
*/
public Message(String sender, String addressee, String text) {
this.sender=sender;
this.addressee=addressee;
this.text=text;
}
String getSender() {
return sender;
}
String getAddressee() {
return addressee;
}
String getText() {
return text;
}
}
package communicate;
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 static java.lang.Thread.sleep;
public class Server implements Runnable {
//private ArrayList<Message> messages;
private Socket sock;
private String ID;
private ObjectInputStream objectInputStream;
private ObjectOutputStream objectOutputStream;
ArrayList<Message> messages;
/*Server(String ID) {
this.ID=ID;
}*/
Server(ArrayList<Message> msg, Socket sock) throws IOException {
messages=msg;
this.sock=sock;
objectInputStream=new ObjectInputStream(sock.getInputStream());
objectOutputStream=new ObjectOutputStream(sock.getOutputStream());
}
@Override
public void run() {
while(true) {
try {
sleep(100);
receiveMsg();
sleep(100);
sendMsg();
}
catch (InterruptedException e) {
e.printStackTrace();
}
}
}
synchronized void sendMsg() { //ten sychronized do przemyślenia!
if(!messages.isEmpty()) {
for (Message x : messages) {
if (x.getAddressee().equals(ID)) {
//wyślij
try {
objectOutputStream.writeObject(x);
//objectOutputStream.flush();
messages.remove(x);
} catch (Exception e) {
e.printStackTrace();
}
break;
}
}
}
/*if(messages.containsKey(ID)) {
//wyślij
}*/
}
synchronized void receiveMsg() {
try {
Message msg=(Message)objectInputStream.readObject();
messages.add(msg);
}
catch(Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) throws IOException {
final int port=4000;
final int maxUsers=50;
ArrayList<Thread> serverThreads=new ArrayList<>();
ArrayList<Message> messages = new ArrayList<>();
//HashMap<String, Message> messages=new HashMap<>();
ServerSocket servSocket;
ArrayList<Socket> users=new ArrayList<>();
int i=0;
System.out.println("Serwer uruchomiony");
//try {
servSocket = new ServerSocket(port, maxUsers);
/*}
catch (IOException e) {
e.printStackTrace();
System.out.println("Nie udało się utworzyć gniazda");
System.exit(1);
}*/
/*class messagesHandler implements Runnable {
private ArrayList<Thread> servers;
private ArrayList<String> messages;
messagesHandler(ArrayList<Thread> serv, ArrayList<String> msg) {
servers=serv;
messages=msg;
}
@Override
public void run() {
while(true) {
//odbieram i wysyłam wiadomości
}
}
}*/
while(true) {
users.add(servSocket.accept());
addUser(serverThreads, messages, users.get(i));
serverThreads.get(i).setDaemon(true);
serverThreads.get(i).start();
++i;
//fajnie byłoby wysłać wszystkim nową listę użytkowników, ale to potem - niech się domyślają xD
}
}
private void addMessage() {
}
private static synchronized void addUser(ArrayList<Thread> servers, ArrayList<Message> msg, Socket sock) {
try {
servers.add(new Thread(new Server(msg, sock)));
}
catch (IOException e) {
e.printStackTrace();
System.exit(1);
}
}
}
<?import javafx.geometry.Insets?>
<?import javafx.scene.layout.GridPane?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.layout.Pane?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.layout.BorderPane?>
<?import javafx.scene.layout.VBox?>
<BorderPane fx:controller="communicate.Controller"
xmlns:fx="http://javafx.com/fxml" prefWidth="512" prefHeight="512">
<top>
</top>
<center>
<VBox fx:id="pane" prefWidth="512" prefHeight="512" />
</center>
<bottom>
<HBox fx:id="hboxForButton" prefWidth="512" prefHeight="100">
</HBox>
</bottom>
</BorderPane>