Я написал простую серверно-клиентскую программу с интерфейсом Swing, используя шаблон синглтона и наблюдателя. Каждый клиент подключается к серверу и может отправлять сообщения. Сервер пересылает полученные сообщения остальным клиентам. Клиенты используют графический интерфейс, который позволяет им подключаться и отключаться от сервера в любое время.
Программа работает довольно хорошо, потому что я пробовал ловить везде, где обрабатываются все исключения, которые могут возникнуть. Но если вы поиграете с ним, вы увидите в консоли миллион исключений. Это, вероятно, из-за плохого дизайна, но я старался изо всех сил, чтобы сделать чистый код.
так что вопрос:
Кто-нибудь может дать мне подсказку и совет о том, как сделать код более корректным и чистым (особенно когда речь идет об отключении клиента от сервера)?
В классе CustomServer
есть основной метод для запуска сервера и
Основной метод в контроллере для запуска клиента (с графическим интерфейсом).
Спасибо за вашу помощь!
Вот код:
Класс CustomServer
package model;
import java.io.*;
import java.net.*;
import controller.Controller;
public class CustomServer extends Thread{
private ServerSocket ss = null;;
public CustomServer() throws Exception {
ss = new ServerSocket(4444);
this.start();
}
@Override
public void run(){
while (true){
try {
Socket connSoc = ss.accept();
new Connect(connSoc);
} catch (IOException e) {
e.printStackTrace();
try{
ss.close();
}catch (Exception e1) {
e1.printStackTrace();
}
}
}
}
private class Connect extends Thread{
ObjectOutputStream out;
ObjectInputStream in;
public Connect(Socket connSoc) {
final IOController server = IOController.getInstance();
try {
out = new ObjectOutputStream(connSoc.getOutputStream());
in = new ObjectInputStream(connSoc.getInputStream());
server.add(in, out);
}catch (Exception e) {
e.printStackTrace();
try{
connSoc.close();
}catch (Exception ex) {
e.printStackTrace();
}
}
this.start();
}
}
public static void main(String[] args) throws Exception{
new CustomServer();
}
}
Класс CustomClient
package model;
import java.io.*;
import java.net.*;
import java.util.*;
public class CustomClient extends Observable{
private Socket connSocket;
private ObjectOutputStream out;
private ObjectInputStream in;
private boolean isOn = true;
private Thread receiver;
public CustomClient() throws Exception{
System.out.println("inside CClient");
Socket soc = new Socket();
soc.connect(new InetSocketAddress(InetAddress.getLocalHost(), 4444));
out = new ObjectOutputStream(soc.getOutputStream());
in = new ObjectInputStream(soc.getInputStream());
}
public void transmit(Object obj){
System.out.println("CClient - transitmin - start");
try {
out.writeObject(obj);
out.flush();
} catch (IOException e) {
e.printStackTrace();
}
System.out.println("CClient - transitmin - end");
}
public void reveive(){
System.out.println("CClient - recieve - start");
receiver = new Thread(new Runnable() {
@Override
public void run() {
while (isOn){
Object obj = null;
try {
obj = in.readObject();
setChanged();
} catch (Exception ex){
ex.printStackTrace();
}
if (hasChanged()){
notifyObservers(obj);
}
}
}
});
receiver.start();
System.out.println("CClient - recieve - end");
}
public void closeConnection(){
try{
in.close();
} catch (Exception e) {
System.err.println("CAUGHT");
e.printStackTrace();
}
try{
out.close();
} catch (Exception e) {
System.err.println("CAUGHT");
e.printStackTrace();
}
finally {
try {
isOn = false;
in = null;
out = null;
connSocket.close();
} catch (Exception e) {
e.printStackTrace();
}
connSocket = null;
}
}
public Socket getSocket(){
return connSocket;
}
}
IOController class
package model;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.ArrayList;
public class IOController{
ArrayList<ObjectInputStream> ins = new ArrayList<ObjectInputStream>();
ArrayList<ObjectOutputStream> outs = new ArrayList<ObjectOutputStream>();
private static IOController instance = new IOController();
private IOController() {
}
public static IOController getInstance(){
return instance;
}
public void add(final ObjectInputStream in, final ObjectOutputStream out){
ins.add(in);
outs.add(out);
new Connect(in);
}
private class Connect extends Thread{
ObjectInputStream in;
public Connect(ObjectInputStream in) {
this.in = in;
this.start();
}
@Override
public void run() {
boolean isOn = true;
ArrayList<ObjectOutputStream> toBeRemoved = new ArrayList<ObjectOutputStream>();
while(isOn){
try {
Object obj = in.readObject();
for (ObjectOutputStream out : outs){
try {
out.writeObject(obj);
out.flush();
}catch (Exception ex){
toBeRemoved.add(out);
ex.printStackTrace();
}
}
for (ObjectOutputStream oos : toBeRemoved){
outs.remove(oos);
}
}catch (Exception ex){
ins.remove(in);
isOn = false;
in = null;
ex.printStackTrace();
}
}
}
}
}
Контроллер класса
package controller;
import java.awt.*;
import view.GUI;
import model.CustomClient;
public class Controller {
private GUI gui;
private CustomClient client;
public Controller() {
gui = new GUI();
gui.addConnectButtonActionListener(new ConnectButtonActionListener());
gui.addTextFieldKeyListner(new TextFieldKeyListener());
gui.addDisconnectButtonActionListener(new DisconnectButtonActionListener());
gui.addCustomWindowListener(new GuiWindowListener());
}
private class DisconnectButtonActionListener implements ActionListener{
@Override
public void actionPerformed(ActionEvent e) {
client.closeConnection();
client = null;
gui.getDisconnectButton().setEnabled(false);
gui.getConnectButton().setEnabled(true);
}
}
private class ConnectButtonActionListener implements ActionListener{
@Override
public void actionPerformed(ActionEvent e) {
try {
client = new CustomClient();
client.addObserver(gui);
client.reveive();
gui.getConnectButton().setEnabled(false);
gui.getDisconnectButton().setEnabled(true);
}catch (Exception ex) {
ex.printStackTrace();
}
}
}
private class TextFieldKeyListener extends KeyAdapter{
@Override
public void keyReleased(KeyEvent e) {
if (e.getKeyCode()==KeyEvent.VK_ENTER){
String msg = gui.getTextField().getText();
gui.getTextField().setText("");
if (client != null){
client.transmit(msg);
}
}
}
}
private class GuiWindowListener extends WindowAdapter{
@Override
public void windowClosing(WindowEvent e) {
try{
if (client != null){
client.closeConnection();
client = null;
}
}catch (Exception e2) {
}
System.out.println("closed");
}
}
public static void main(String[] args) {
new Controller();
}
}
и GUI класс
package view;
import java.awt.*;
import java.awt.event.*;
import java.util.*;
import javax.swing.*;
public class GUI extends JFrame implements Observer{
private JTextField textField;
private JTextArea displayArea;
private JButton connectButton;
private JButton disconnectButton;
public GUI() {
init();
setDefaultCloseOperation(EXIT_ON_CLOSE);
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
pack();
setVisible(true);
textField.requestFocusInWindow();
}
});
}
public void addConnectButtonActionListener(ActionListener al){
connectButton.addActionListener(al);
}
public void addDisconnectButtonActionListener(ActionListener al){
disconnectButton.addActionListener(al);
}
public void addTextFieldKeyListner(KeyListener kl){
textField.addKeyListener(kl);
}
public void addCustomWindowListener(WindowListener guiWindowListener) {
addWindowListener(guiWindowListener);
}
public void appendText(String text){
displayArea.append("\n"+ text);
}
private void init() {
JPanel panel = new JPanel();
JPanel southPanel = new JPanel();
JPanel northPanel = new JPanel();
connectButton = new JButton("connect");
connectButton.setFocusable(false);
disconnectButton = new JButton("disconnect");
disconnectButton.setFocusable(false);
textField = new JTextField(20);
displayArea = new JTextArea();
displayArea.setEditable(false);
displayArea.setPreferredSize(new Dimension(300,250));
panel.setLayout(new BorderLayout());
southPanel.setLayout(new FlowLayout());
northPanel.setLayout(new FlowLayout());
northPanel.add(displayArea);
southPanel.add(connectButton);
southPanel.add(disconnectButton);
panel.add(textField,BorderLayout.CENTER);
panel.add(southPanel, BorderLayout.SOUTH);
panel.add(northPanel, BorderLayout.NORTH);
this.getContentPane().add(panel);
disconnectButton.setEnabled(false);
System.out.println(textField.hasFocus());
}
public JTextField getTextField() {
return textField;
}
public void setTextField(JTextField textField) {
this.textField = textField;
}
public JTextArea getDisplayArea() {
return displayArea;
}
public void setDisplayArea(JTextArea displayArea) {
this.displayArea = displayArea;
}
public JButton getConnectButton() {
return connectButton;
}
public void setConnectButton(JButton connectButton) {
this.connectButton = connectButton;
}
public JButton getDisconnectButton() {
return disconnectButton;
}
public void setDisconnectButton(JButton disconnectButton) {
this.disconnectButton = disconnectButton;
}
@Override
public void update(Observable observable, Object object) {
displayArea.append("\n"+(String)object);
}
}