制作聊天应用程序,我最近将它从Swing移植到JavaFX。我遇到了使用ObjectOutputStream引用的问题。
在Client.java文件中,底部的toServer引用返回null。我认为使toServer static允许我访问代码中的任何位置的指针。只是无法弄清楚我做错了什么。
我收到了java.lang.NullPointerException 在Client.sendMessageToServer(Client.java:130) 我很感激任何意见,谢谢!
Main.java
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;
import java.io.IOException;
/**
* @author Sergey
* Date: 11/22/13 Time: 9:10 PM
*/
public class Main extends Application
{
private Stage primaryStage;
private BorderPane rootLayout;
/**
* this code creates a borderpane with a menubar at the top, the purpose is of
* this is to nest the chat client into the borderpane's centre location
*/
@Override
public void start( Stage primaryStage )
{
this.primaryStage = primaryStage;
this.primaryStage.setTitle( "chatninjas client" );
try
{
// load the menubar layout from the fxml file
FXMLLoader loader = new FXMLLoader( Client.class.getResource( "ninjamenu.fxml" ) );
rootLayout = ( BorderPane ) loader.load();
Scene scene = new Scene( rootLayout );
primaryStage.setScene( scene );
primaryStage.show();
}
catch( Exception ex )
{
// Exception gets thrown if the fxml file could not be loaded
ex.printStackTrace();
}
showChatRoom();
}
/**
* Shows the chat room area that will be nested into the centre of the borderpane above
*/
public void showChatRoom()
{
try
{
// Load the fxml file and set into the center of the main layout
FXMLLoader loader = new FXMLLoader( Client.class.getResource( "ninja.fxml" ) );
AnchorPane overviewPage = ( AnchorPane ) loader.load();
rootLayout.setCenter( overviewPage );
}
catch( IOException e )
{
// Exception gets thrown if the fxml file could not be loaded
e.printStackTrace();
}
}
/**
* Returns the main stage.
*
* @return
*/
public Stage getPrimaryStage()
{
return primaryStage;
}
public static void main( String[] args )
{
launch( args );
}
}
NinjaController.java
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Button;
import javafx.scene.control.TextArea;
import javafx.scene.control.TextField;
import java.net.URL;
import java.util.ResourceBundle;
public class NinjaController implements Initializable
{
@FXML // fx:id="userOutput"
static protected TextArea userOutput = null;
@FXML // fx:id="userInput"
static protected TextField userInput = null;
@FXML // fx:id="sendButton"
static protected Button sendButton = null;
@FXML
ResourceBundle resources = null;
@FXML
URL location = null;
@Override
public void initialize( URL fxmlFileLocation, ResourceBundle resources )
{
this.resources = resources;
this.location = fxmlFileLocation;
System.out.println( "about to show the resources var: " + resources );
System.out.println( "about to show the fxmlFileLocation var: " + fxmlFileLocation );
userInput.setOnAction( new EventHandler<ActionEvent>()
{
@Override
public void handle( javafx.event.ActionEvent actionEvent )
{
Client.sendMessageToServer( constructMessageFromFXMLuserInput() );
userInput.setText( "" );
System.out.println( "send via enter key" );
}
});
sendButton.setOnAction( new EventHandler<javafx.event.ActionEvent>()
{
@Override
public void handle( javafx.event.ActionEvent actionEvent )
{
Client.sendMessageToServer( constructMessageFromFXMLuserInput() );
userInput.setText( "" );
System.out.println( "sent via send button" );
}
} );
}
/* dont think i need this anymore
public void onEnter()
{
System.out.println( "user hit enter while mouse was focused in the textfield with this message: " + userInput.getText() );
}
*/
// returns a String trimmed of spaces
public static String retrieveTextFromFXMLuserInput()
{
return userInput.getText().trim();
}
// appends the incoming text area (userOut) with an inbound message
public static void sendMessageToFXMLuserOutput( Message incomingMessage )
{
userOutput.appendText( incomingMessage.getMsgBody() + "\n" );
}
public static Message constructMessageFromFXMLuserInput()
{
// if the userInput field is empty, don't even bother sending the message
if( retrieveTextFromFXMLuserInput().equals( "" ))
{
System.out.println( "message is empty, didnt send anything" );
return null;
}
return new Message( retrieveTextFromFXMLuserInput(),
null, // font entry
null, // font entry
"bob" /*getUserName()*/ );
}
}
Client.java:
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket;
import java.util.Date;
import static java.text.DateFormat.MEDIUM;
import static java.text.DateFormat.getTimeInstance;
public class Client
{
/*
These are the user set preferences, the userName will be set by the user
when the Client loads.
*/
private static String userName; // this field will be changed when user is prompted for a name
private static int fontSize = 12;
// Object IO streams
private static ObjectOutputStream toServer;
private ObjectInputStream fromServer;
/*
Plan to update the port selection to use a showInputDialog that will request a user
specified port and url of the server to connect to.
Like the other showInputDialogs, this will have a server address and port preset.
*/
private static final int port = 4000;
String date;
boolean connected;
// static Font myFont = new Font( fontType, Font.BOLD, fontSize ); // testing the font functionality // works!
Message wantToSend;
public Client() throws IOException
{
String hostname = "localhost";
Socket socket = new Socket( hostname, port );
// prompt the user for an alias using JOptionPane.showInputDialog
userName = "bob"; //getName(); // getName needs to be rewritten, it used the old JFrames approach, needs to use the new css approach
toServer = new ObjectOutputStream( socket.getOutputStream() );
fromServer = new ObjectInputStream( socket.getInputStream() );
// send the username to the server for "online list"
// ** feature is beta right now **
wantToSend = new Message( userName );
toServer.writeObject( wantToSend );
Message receiveMessage;
connected = true;
while( connected )
{
try
{
// make the date look pretty: 8:54:17 PM
date = getTimeInstance( MEDIUM ).format( new Date() );
receiveMessage = ( Message ) fromServer.readObject();
if( receiveMessage != null )
{
sendMessageToController( receiveMessage );
}
else
{
sendMessageToController( new Message( 1, ">>> Message from server == null <<< BUGCODE1001" ) );
}
}
catch( ClassNotFoundException e1 )
{
e1.printStackTrace();
}
}
}
/* _ _ _ _
| | | | ___ | | | |
__ _ ___ | |_ | |_ ___ _ __ ___ ( _ ) ___ ___ | |_ | |_ ___ _ __ ___
/ _` | / _ \ | __| | __| / _ \ | '__| / __| / _ \/\ / __| / _ \ | __| | __| / _ \ | '__| / __|
| (_| | | __/ | |_ | |_ | __/ | | \__ \ | (_> < \__ \ | __/ | |_ | |_ | __/ | | \__ \
\__, | \___| \__| \__| \___| |_| |___/ \___/\/ |___/ \___| \__| \__| \___| |_| |___/
__/ |
|___/
*/
/**
* Prompt for and return the desired screen name.
*/
// need to update this code with nicole's login screen functionality
/*
public String getName()
{
// the random generator is for testing purposes, i didn't want to keep having
// to type in usernames to test features
return ( String ) JOptionPane.showInputDialog(
null,
"Choose a name:",
"Screen name selection",
JOptionPane.PLAIN_MESSAGE,
null,
null,
"moose" + ( int ) ( Math.random() * ( 9999 - 1111 ) ) );
}
*/
public void sendMessageToController( Message sendThisToController )
{
System.out.println("--sending message to controller");
NinjaController.sendMessageToFXMLuserOutput( sendThisToController );
System.out.println("--message sent to controller");
}
static void sendMessageToServer( Message messageToSend )
{
try
{
if( ! ( messageToSend.getMsgBody().isEmpty() ) )
{
// sends wantToSend object to the server via ObjectOutputStream->OutputStream->Socket
toServer.writeObject( messageToSend ); // exception: java.lang.NullPointerException
//at Client.sendMessageToServer(Client.java:130)
print( "message sent to server" );
toServer.flush();
}
else if( messageToSend == null ) // this shouldn't ever happen BUGCODE1000
{
print( " >>> wantToSend is null .. this message is a bug BUGCODE1000 <<< " );
}
else;
}
catch( IOException ex )
{
System.err.println( ex );
}
}
// basic stdout print because System.out.println( "" )
public static void print( String printThisMessageToSystemOutPrintln )
{
System.out.println( printThisMessageToSystemOutPrintln );
}
}
编辑: 好的,我发现了我的问题。
我的客户端没有与我的服务器进行通信,我遇到了运行Controller或Client类的情况,并且无法弄清楚程序为什么会冻结。
所以我在Client上实现了Runnable,并让Controller创建了一个新线程: 客户端任务= new Client(); 新线程(任务).start();
现在一切都很时髦。