标题可能并不十分清楚,我不确定我应该如何用问题,但简而言之,我试图在Java中创建一个"聊天"程序。我使用2个不同的文件聊天服务器和聊天夹。
程序的工作原理是您首先打开ChatServer,然后打开ChatClient,以连接到ChatServer。要连接您首先需要在第一个Textfield中编写用户名,然后按Enter。然后该程序试图将您连接到服务器,这是问题开始的地方。
每当我尝试连接到服务器时,我都会得到NullPoInterException,我不明白为什么。我已经尝试了很长时间来理解,但是我看不到错误。
该程序的重点是mutliple chatclients可以连接到聊天服务器(最多5个人(并互相写入。我知道这个问题非常具体,我希望是否有人能将我指向正确的方向,我会很高兴!
chatserver
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 java.util.Date;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.scene.Scene;
import javafx.scene.control.ScrollPane;
import javafx.scene.control.TextArea;
import javafx.stage.Stage;
public class ChatServer extends Application
{
private ArrayList<ObjectOutputStream> outputToUsers;
private TextArea taServer = new TextArea();
private static final int MAX_USERS = 5;
private boolean acceptingNewUsers = true;
private int connectedUsers = 0;
private static final int port = 4999;
public static void main(String[] args)
{
launch(args);
}
@Override
public void start(Stage primaryStage)
{
initializePrmaryStage(primaryStage);
new Thread( () ->
{
try
{
// Create a server socket
@SuppressWarnings("resource")
ServerSocket serverSocket = new ServerSocket(port);
Platform.runLater(() -> taServer.appendText(new Date() + ": Server started at socket: " + port + 'n'));
acceptUsers();
while (true)
{
if (acceptingNewUsers)
{
// Listen for a new connection request
Socket socket = serverSocket.accept();
connectedUsers++;
if (connectedUsers == MAX_USERS)
refuseNewUsers();
// Create and start a new thread for the connection
new Thread(new AddUserToChat(socket)).start();
}
}
}
catch(IOException ex)
{
System.err.println(ex);
}
}).start();
}
private void initializePrmaryStage(Stage stage)
{
taServer.setMinHeight(450);
// Create a scene and place it in the stage
Scene scene = new Scene(new ScrollPane(taServer), 450, 400);
stage.setTitle("ChatServer"); // Set the stage title
stage.setScene(scene); // Place the scene in the stage
stage.show(); // Display the stage
}
private void acceptUsers()
{
acceptingNewUsers = true;
Platform.runLater(() -> taServer.appendText(new Date() + ": " + "Accepting users" + 'n'));
}
private void refuseNewUsers()
{
acceptingNewUsers = false;
Platform.runLater(() -> taServer.appendText("Maximum user capacity reached." + 'n'));
}
private void writeToAll(String s)
{
try
{
for (int x = 0; x < outputToUsers.size(); x++)
{
outputToUsers.get(x).writeObject(s);
outputToUsers.get(x).flush();
}
} catch (IOException ex)
{
ex.printStackTrace();
}
}
private void writeToLog(String s)
{
Platform.runLater(() ->
{
taServer.appendText(s);
});
}
private class AddUserToChat implements Runnable
{
private ObjectInputStream fromUser;
private ObjectOutputStream toUser;
private String username;
private Socket userSocket;
@SuppressWarnings("unused")
public AddUserToChat(Socket userSocket)
{
this.userSocket = userSocket;
connectedUsers++;
}
@Override
public void run()
{
try
{
establishUserConnection();
readMessagesFromUser();
}
catch (Exception e)
{
System.err.println(e.getMessage());
removeUser();
}
}
/*
* Connects user to server
* @throws IOException if {@link ObjectInputStream#readUTF()} encounters an error
*/
private void establishUserConnection() throws IOException
{
// Get input and output streams from socket
toUser = new ObjectOutputStream(userSocket.getOutputStream());
fromUser = new ObjectInputStream(userSocket.getInputStream());
// Read and save username and save OOS to user in outputToUsers in ChatServer class
username = fromUser.readUTF();
outputToUsers.add(toUser);
writeToLog(username + " joined the chat.");
writeToAll(username + " joined the chat.");
}
/**
* Removes user from server
*/
private void removeUser()
{
// Decrease user counter and remove OOS to user
connectedUsers--;
outputToUsers.remove(toUser);
writeToLog(username + " left the chat.");
writeToAll(username + " left the chat.");
// If server doesn't accept new users, start accepting them once again
if (!acceptingNewUsers) acceptUsers();
}
/**
* Continually read messages from user
*
* @throws IOException if {@link ObjectInputStream#readUTF()} encounters an error
*/
private void readMessagesFromUser() throws IOException
{
while (true)
writeToAll(String.format("%s wrote: %s", username, fromUser.readUTF()));
}
}
}
chatclient
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket;
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.ScrollPane;
import javafx.scene.control.TextArea;
import javafx.scene.control.TextField;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.GridPane;
import javafx.stage.Stage;
public class ChatClient extends Application
{
private ObjectOutputStream toServer = null;
private ObjectInputStream fromServer = null;
private GridPane gridpane = new GridPane();
private BorderPane mainPane = new BorderPane();
private TextField tfUsername = new TextField();
private TextField tfUserInput = new TextField();
private TextArea ta = new TextArea();
private String username = "";
private String userinput = "";
private Socket socket;
public static void main(String[] args)
{
launch(args);
}
@Override
public void start(Stage primaryStage) throws Exception
{
// Panel p to hold the label and text field
BorderPane paneForTextField = new BorderPane();
paneForTextField.setPadding(new Insets(5, 5, 5, 5));
paneForTextField.setStyle("-fx-border-color: green");
gridpane.add(tfUsername, 0, 0);
gridpane.add(tfUserInput, 0, 1);
initializeUsernameTextField();
initializeUserInputTextField();
tfUserInput.setPrefWidth(400.0);
gridpane.setPrefWidth(450.0);
gridpane.setVgap(5.0);
gridpane.setHgap(25.0);
gridpane.setAlignment(Pos.CENTER);
paneForTextField.setBottom(gridpane);
ta.setPrefHeight(450);
mainPane.setCenter(new ScrollPane(ta));
mainPane.setBottom(paneForTextField);
initializePrimaryStage(primaryStage);
tfUsername.setOnAction(e ->
{
if (!tfUsername.getText().equals(""))
{
username = tfUsername.getText().trim();
connecToServer();
}
});
if (!username.equals(""))
{
tfUserInput.setOnAction(e ->
{
// Get user input
String UserInput = tfUserInput.getText();
// Send string
sendToServer(UserInput);
// Get string
receiveDataFromServer();
writeToLog(userinput);
});
}
}
private void initializeUsernameTextField()
{
tfUsername.setPrefWidth(400.0);
tfUsername.promptTextProperty().set("Enter username here...");
}
private void initializeUserInputTextField()
{
tfUserInput.setPrefWidth(400.0);
tfUserInput.promptTextProperty().set("Enter message here...");
}
private void initializePrimaryStage(Stage stage)
{
// Create a scene and place it in the stage
Scene scene = new Scene(mainPane, 450, 400);
stage.setTitle("Chat Client"); // Set the stage title
stage.setScene(scene); // Place the scene in the stage
stage.show(); // Display the stage
}
private BorderPane getBorderPane() {return mainPane;}
private void connecToServer()
{
establishServerConnection();
}
private void establishServerConnection()
{
try
{
socket = new Socket("localhost", 4999);
fromServer = new ObjectInputStream(socket.getInputStream());
toServer = new ObjectOutputStream(socket.getOutputStream());
sendToServer(username);
}
catch (IOException ex)
{
ta.appendText(ex.toString() + 'n');
}
}
private void receiveDataFromServer()
{
try
{
userinput = fromServer.readUTF();
} catch (IOException ex)
{
System.err.println(ex);
}
}
private void sendToServer(String s)
{
try
{
toServer.writeObject(s);
toServer.flush();
}
catch (IOException ex)
{
System.err.println(ex);
}
}
private void writeToLog(String s)
{
ta.appendText(tfUsername + "wrote: " + s + "n");
}
}
tldr;
尝试连接到服务器时获取NullPoInterException,我找不到问题。
错误消息:
Exception in thread "Thread-5" java.lang.NullPointerException
at chapter19.ChatServer$AddUserToChat.removeUser(ChatServer.java:171)
at chapter19.ChatServer$AddUserToChat.run(ChatServer.java:143)
at java.lang.Thread.run(Unknown Source)
属性 outputToUsers
不是不化的,因此等于 null
。
这会导致拨打establishUserConnection
的语句outputToUsers.add(toUser)
时,会在AddUserToChat.run()
中抛出一个例外。
捕获此例外,因此称为removeUser
。
在此方法中,语句outputToUsers.remove(toUser)
导致新的异常。