我目前正在将聊天服务器作为项目工作,需要一些帮助。我有一个多线程聊天服务器,我希望客户端能够关闭他们的聊天窗口而不会崩溃服务器,因为它失去了连接。到现在为止还挺好。我有一个工作gui,当我有一个客户端登录到服务器时,它可以正常关闭窗口,因此关闭clientIOProcessor而不会导致服务器崩溃。 但是,当我有多个客户端登录到服务器并想要关闭其中一个客户端时,就会崩溃
这是ClientIOProcessor:
public class ClientIOProcessor extends Object implements Runnable, InputListener {
private ObjectInput socketInput;
private ObjectOutput socketOutput;
private boolean keepRunning;
private ClientModelDistributor assignedModelDistributor;
private MessageImplementation currentMessage;
private UserHub userHub;
public ClientIOProcessor(ClientModelDistributor _assignedModelDistributor, ObjectInput _socketInput,
ObjectOutput _socketOutput) {
this.assignedModelDistributor = _assignedModelDistributor;
this.socketInput = _socketInput;
this.socketOutput = _socketOutput;
assignedModelDistributor.getInputHandler().registerInputListener(this);
keepRunning = true;
}
public ClientIOProcessor(ClientLoginProcessor clientLoginProcessor, UserHub userHub) {
assignedModelDistributor = clientLoginProcessor.assignedModelDistributor;
socketInput = clientLoginProcessor.socketInput;
socketOutput = clientLoginProcessor.socketOutput;
assignedModelDistributor.getInputHandler().registerInputListener(this);
assignedModelDistributor.getIncomingMessageHandler().addIncomingMessageListener(userHub);
keepRunning = true;
this.userHub = userHub;
}
public void run() {
while (keepRunning) {
currentMessage = null;
currentMessage = buildMessageFromInput();
if(currentMessage == null) return;
switch (currentMessage.getMessageType()) {
case CHATMSG:
assignedModelDistributor.getIncomingMessageHandler().enterMessage(currentMessage);
break;
case USRJOINED:
assignedModelDistributor.getIncomingMessageHandler().enterMessage(currentMessage);
break;
case USRLEFT:
assignedModelDistributor.getIncomingMessageHandler().enterMessage(currentMessage);
break;
case TERMINATE:
this.terminateConnection();
break;
default:
System.out.println("An error occurred.");
}
}
}
public void terminateConnection() {
keepRunning = false;
assignedModelDistributor.getIncomingMessageHandler().removeIncomingMessageListener(this.userHub);
assignedModelDistributor.getInputHandler().removeInputListener(this);
try {
socketInput.close();
socketOutput.close();
} catch (IOException e) {
e.printStackTrace();
}
// System.exit(0);
}
private MessageImplementation buildMessageFromInput() {
MessageImplementation messageSentByServer = null;
try {
messageSentByServer = (MessageImplementation) socketInput.readObject();
} catch (ClassNotFoundException | IOException e) {
e.printStackTrace();
}
return messageSentByServer;
}
/**
* This method can be used to send <code>messages</code> to the server.
*
* @param message
* @throws IOException
*/
public void send(Message message) throws IOException {
try {
socketOutput.writeObject(message);
socketOutput.flush();
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* This method is called whenever the user enters input to this
* <code>InputListener</code>'s corresponding <code>InputHandler</code>.
*/
@Override
public void inputEntered(String input) {
try {
send(assignedModelDistributor.buildUserMessage(input));
} catch (IOException e) {
e.printStackTrace();
}
}
}
clientIO处理器从服务器获取TERMINATE消息,并调用terminateConnection()方法结束Streams。
这是ServerIOProcessor:
public class ServerIOProcessor implements Runnable, IncomingMessageListener {
private ServerModelDistributor assignedModelDistributor;
private User assignedUser;
private boolean keepRunning;
private ObjectInputStream socketInput;
private ObjectOutputStream socketOutput;
private MessageImplementation currentMessage;
private final String SYSTEM = "System";
public ServerIOProcessor(User _assignedUser, ObjectOutputStream _socketOutput, ObjectInputStream _socketInput,
ServerModelDistributor _assignedModelDistributor) {
assignedUser = _assignedUser;
socketInput = _socketInput;
socketOutput = _socketOutput;
assignedModelDistributor = _assignedModelDistributor;
keepRunning = true;
assignedModelDistributor.getIncomingMessageHandler().addIncomingMessageListener(this);
}
public void run() {
while (keepRunning) {
currentMessage = null;
currentMessage = buildMessageFromInput();
switch (currentMessage.getMessageType()) {
case CHATMSG:
assignedModelDistributor.getIncomingMessageHandler().enterMessage(currentMessage);
break;
case NEWTASK:
createNewTask();
break;
case TASKASSGNMNT:
assignTaskToUser();
break;
case NEWLEADER:
determineLeaderOfTeam();
break;
case ADDTEAM:
addTeam();
break;
case RMVTEAM:
removeTeam();
break;
case ADDTEAMMEMBER:
addUserToTeam();
break;
case RMVTEAMMEMBER:
removeUserFromTeam();
break;
case JOINTEAM:
joinTeam();
break;
case LEAVETEAM:
leaveTeam();
break;
case EDITDETAILS:
editUserDetails();
break;
case TERMINATE:
assignedModelDistributor.getIncomingMessageHandler().enterMessage(currentMessage);
letClientLeaveServer();
break;
default:
send(newSystemMessage(MessageType.ERROR));
System.out.println("An error occured in one of the server's active IO processes.");
break;
}
}
}
private void createNewTask() {
}
private void assignTaskToUser() {
// TODO Auto-generated method stub
}
private void determineLeaderOfTeam() {
// TODO Auto-generated method stub
}
private void addTeam() {
// TODO Auto-generated method stub
}
private void removeTeam() {
// TODO Auto-generated method stub
}
private void addUserToTeam() {
// TODO Auto-generated method stub
}
private void removeUserFromTeam() {
// TODO Auto-generated method stub
}
private void joinTeam() {
// TODO Auto-generated method stub
}
private void leaveTeam() {
// TODO Auto-generated method stub
}
private void editUserDetails() {
// TODO Auto-generated method stub
}
private void letClientLeaveServer() {
// TODO Auto-generated method stub
assignedModelDistributor.getIncomingMessageHandler().removeIncomingMessageListener(this);
try {
socketInput.close();
socketOutput.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
keepRunning = false;
}
private MessageImplementation buildMessageFromInput() {
MessageImplementation messageSentByUser = null;
try {
messageSentByUser = (MessageImplementation) socketInput.readObject();
} catch (ClassNotFoundException | IOException e) {
e.printStackTrace();
}
return messageSentByUser;
}
private MessageImplementation newSystemMessage(MessageType msgtype) {
return new MessageImplementation(SYSTEM, msgtype);
}
//TODO Check expected purpose of this method.
/**
* This method is called whenever a <code>Message</code> is entered to the
* corresponding <code>IncomingMessageHandler</code>.
*/
@Override
public void messageEntered(Message message) {
// assignedModelDistributor.getIncomingMessageHandler().enterMessage(message);
try {
socketOutput.writeObject(message);
socketOutput.flush();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
/**
* Writes the given <code>Message</code> to the Socket's
* <code>ObjectInputStream</code>.
*
* @throws IOException
*/
void send(Message message) {
try {
socketOutput.writeObject(message);
socketOutput.flush();
} catch (Exception r) {
System.out.println("Error!");
r.printStackTrace();
}
}
}
这里的重要部分是letClientLeaveServer()方法,也可以在这里结束流。但是,我认为这是错误的方式,因为我认为这会结束所有登录客户端的输入/输出流。
它自己的ServerSocket在另一个类ConnectionProcessor中初始化,该类在给定端口上启动Socket并接受它。然后connectionProcessor启动serverLogInProcessor,为serverIOProcessor启动一个新线程。
这里是ConnectionProcessor:
public class ConnectionProcessor implements Runnable {
private ServerModelDistributor assignedModelDistributor;
private boolean keepRunning;
private ServerSocket serverSocket;
private int assignedPort;
public ConnectionProcessor(int _assignedPort, ServerModelDistributor _assignedModelDistributor) {
assignedModelDistributor = _assignedModelDistributor;
assignedPort = _assignedPort;
keepRunning = true;
try {
serverSocket = new ServerSocket(assignedPort);
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void run() {
while (keepRunning) {
if(serverSocket.isClosed()) {
try {
serverSocket = new ServerSocket(assignedPort);
} catch (IOException e) {
e.printStackTrace();
}
}
try {
Socket socketCreatedForLoginProcess = serverSocket.accept();
serverSocket.close();
Thread serverLoginProcessor = new Thread(
new ServerLoginProcessor(this, socketCreatedForLoginProcess, assignedModelDistributor),
"loginProcess");
serverLoginProcessor.start();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}