我使用单例和观察者模式编写了一个带有 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/