Рефакторинг - хорошая практика в Sockets - простое приложение Swing сервер-клиент - PullRequest
2 голосов
/ 10 января 2011

Я написал простую серверно-клиентскую программу с интерфейсом 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);
}
}

1 Ответ

2 голосов
/ 16 ноября 2011

Без тщательного изучения вашего кода я не вижу проблем с этим подходом к реализации шаблона наблюдателя . MVCGame является связанным примером. Синхронизация остается потенциальной ловушкой: слава за запуск в потоке диспетчеризации событий , но помните, что append() больше не является поточно-ориентированным в Java 7. В этом примере показана альтернатива, которая также использует invokeLater() .

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...