标题可能不是很清楚,我不确定我应该如何表达我的问题,但简而言之,我正在尝试创建一个"聊天"程序在java中。我使用2个不同的文件ChatServer和ChatClient。
程序的工作原理是首先打开ChatServer,然后打开ChatClient以连接到ChatServer。要连接,首先需要在第一个TextField中写入用户名,然后按Enter键。然后程序尝试将您连接到服务器,这是问题开始的地方。
每当我尝试连接到服务器时,我都会遇到nullpointerexception,我无法理解为什么。我已经尝试了很长时间才能理解,但我无法看到错误。
该程序的重点是多个ChatClients可以连接到ChatServer(最多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)
答案 0 :(得分:2)
属性outputToUsers
未被初始化,因此等于null
这会导致在为AddUserToChat.run()
语句调用establishUserConnection
时在outputToUsers.add(toUser)
内抛出异常。
捕获此异常以便调用removeUser
在此方法中,语句outputToUsers.remove(toUser)
会导致新的异常。