java - 重构——Sockets 中的良好实践——简单的服务器-客户端 Swing 应用程序

标签 java multithreading sockets refactoring

我使用单例和观察者模式编写了一个带有 Swing 接口(interface)的简单服务器-客户端程序。每个客户端都连接到服务器并可以发送消息。服务器将收到的消息转发给其余的客户端。客户端使用 GUI,允许他们随时连接和断开与服务器的连接。 该程序运行良好,因为我有 try - catch 处理可能发生的每个异常的所有地方。但是如果你玩它,你会在控制台中看到一百万个异常。这可能是因为设计不佳,但我已尽力编写干净的代码。 所以问题是:

任何人都可以就如何重构代码以使其更加正确和干净(特别是在断开客户端与服务器的连接时)给我提示和建议吗?

CustomServer 类中有一个 main 方法来启动服务器和一个 Controller 中的 main 方法启动客户端(带 GUI)。

感谢您的热心帮助!

代码如下:

自定义服务器类

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();
}

}

自定义客户端类

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;
}

}

IO Controller 类

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();
            } 
        } 
    }
}
}

Controller 类

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);
}
}

最佳答案

在没有彻底检查您的代码的情况下,我认为这种实现 observer pattern 的方法没有问题。 . MVCGame是一个相关的例子。同步仍然是一个潜在的陷阱:从 event dispatch thread 开始的荣誉,但要注意 append() 在 Java 7 中不再是线程安全的。这个例子展示了一个也使用 invokeLater() 的替代方案。 .

关于java - 重构——Sockets 中的良好实践——简单的服务器-客户端 Swing 应用程序,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4649498/

相关文章:

java - Android 线程实例化

python - UDP 和 TCP 始终为一个客户端使用相同的 IP?

c - 使用套接字传输数据时避免结构填充的最佳方法是什么?

java - 如何使用密码来解码字符串?

java - 如何使用 MemoryPoolMXBean

java - 解析已在 Spring boot 测试中使用的端口 DEFINED PORT

c++ - GCC 8.1.0/MinGW64 编译的 OpenMP 程序崩溃寻找 cygwin.s?

java - 以编程方式设置多个 ImageView 的 setImageResource() 会导致崩溃

python - 你可以在 Python 中对线程或进程进行排队吗?

java - Java 客户端和 Python 服务器之间的 Socket 消息传递