先提问题,再说我实际碰到的情况
问题一:几个关闭system.exit(0)//程序退出windowClosing()//按我的想法,我把这个和windowClosed归结为界面关闭线程正常情况下执行结束就关闭了,但是有时候为了监听在run()里面有给了个while(true){...},这样的循环,好叫程序段循环的执行监听,除非服务器退出
问题一的我的理解对吗?********************************************************************************
问题二:几个退出能关闭对方吗?比如说,程序退出exit(0),肯定会把所有的线程都关掉,界面也关掉对吧?(这里指未重写的情况下)其余的呢?默认的windowsclosed()能关闭线程吗?不重写方法的情况写(如果不能,我自己的程序段就要自己加进程跳出循环的标记了),我想了解清楚,但是自己只能想到这里,没法用小例子去验证自己的想法。*********************************************************************************下面是自己的写作业的实际情况:package com.nyohh.chat.server;import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.Socket;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;import com.nyohh.chat.util.CharacterUtil;public class Server extends JFrame
{
private static final long serialVersionUID = 2289774636261266450L; private JLabel jLabel2; // 服务器状态显示文字
private JButton jButton; // 启动按钮
private JTextArea jTextArea; // 在线用户
private JTextField jTextField; // 服务器默认端口号,用户输入大于1024小于65535 private static Thread thread; // 用户的连接,登录
private static Thread thread2; // 接收消息
private static Thread thread3; // 发送消息 private Map<String, String> map = new HashMap<String, String>(); // 用户及其端口号的映射{{username,port1_port2_address},...} // 在其它线程用到用户列表时候需要这个取得方法
protected Map<String, String> getMap()
{
return map;
} public Server(String name)
{
super(name);
initComponents(); // 初始化界面 addListener(); // 添加各个监听事件 } // 添加各个监听事件
private void addListener()
{
// 启动服务器按钮
jButton.addActionListener(new ActionListener()
{
@Override
public void actionPerformed(ActionEvent e)
{
execute();
}
}); // 点击关闭按钮时,增加"向所有客户端发送服务器关闭消息"
addWindowListener(new WindowAdapter()
{
@Override
public void windowClosing(WindowEvent e)
{
Set<Map.Entry<String, String>> set = map.entrySet();
for (Iterator<Map.Entry<String, String>> iterator = set.iterator(); iterator.hasNext();)
{
try
{
Map.Entry<String, String> entry = iterator.next();
String[] temp = entry.getValue().split("_");// [0]端口1接收消息,[1]端口2接收用户列表的,[2]客户端地址 Socket socket = new Socket(InetAddress.getByName(temp[2]), Integer.valueOf(temp[0]));
OutputStream outputStream = socket.getOutputStream(); outputStream.write("@@@serverClose".getBytes()); outputStream.close();
socket.close();
}
catch (Exception e1)
{
e1.printStackTrace();
}
} //这里有个小问题,当发送完所有的消息后,是否要给出线程跳出循环的标记?
//就是在connectThread里面死循环加个if(flag){break;}当这里结束窗口时候也顺便叫死循环跳出
//不然会不会出现窗口关闭了,但是线程还在一直死循环等待?

//System.exit(0);//该直接这样退出呢,还是给线程个标志,在这里改变下标志呢?

}
});
} // 获取控件,给进程更新控件内容
public JTextArea getjTextArea()
{
return jTextArea;
} // 点击 启动服务器 按钮时候,检测输入是否合法
// 合法启动线程,改变部分界面
private void execute()
{
String hostPort = this.jTextField.getText(); if (CharacterUtil.isEmpty(hostPort))
{
JOptionPane.showMessageDialog(this, "端口号不能为空", "警告", JOptionPane.WARNING_MESSAGE);
this.jTextField.requestFocus();
return;
} if (!CharacterUtil.isNumber(hostPort))
{
JOptionPane.showMessageDialog(this, "端口号必须为数字", "警告", JOptionPane.WARNING_MESSAGE);
this.jTextField.requestFocus();
return;
} if (!CharacterUtil.isPortCorrect(hostPort))
{
JOptionPane.showMessageDialog(this, "端口号必须在1024到65535之间", "警告", JOptionPane.WARNING_MESSAGE);
this.jTextField.requestFocus();
return;
} int port = Integer.valueOf(hostPort); thread = new ConnectThread(this, port);
thread.start(); thread2 = new ExitThread(this);
thread2.start(); thread3 = new ServerUDP(this);
thread3.start(); // 对界面操作
jButton.setText("运行中");
jLabel2.setForeground(Color.RED);
jLabel2.setText("运行中");
jTextField.setEnabled(false);
jButton.setEnabled(false);
} // 初始化界面
private void initComponents()
{
JPanel jPanel1 = new JPanel();
JPanel jPanel2 = new JPanel();
JPanel jPanel3 = new JPanel(); JLabel jLabel1 = new JLabel("服务器状态");
jLabel2 = new JLabel("— — —"); JLabel jLabel3 = new JLabel("端口号");
jLabel3.setForeground(Color.BLUE); jTextField = new JTextField(10); jButton = new JButton("启动服务器"); jTextArea = new JTextArea();
jTextArea.setEditable(false);
jTextArea.setColumns(30);
jTextArea.setForeground(new Color(0, 51, 204));
jTextArea.setRows(20); JScrollPane jScrollPane = new JScrollPane();
jScrollPane.setViewportView(jTextArea); jPanel1.setBorder(BorderFactory.createTitledBorder("服务器信息"));
jPanel3.setBorder(BorderFactory.createTitledBorder("在线用户列表")); jPanel1.add(jLabel1);
jPanel1.add(jLabel2); jPanel2.add(jLabel3);
jPanel2.add(jTextField);
jPanel2.add(jButton); jPanel3.add(jTextArea);
jPanel3.add(jScrollPane); this.getContentPane().add(jPanel1, BorderLayout.NORTH);
this.getContentPane().add(jPanel2, BorderLayout.CENTER);
this.getContentPane().add(jPanel3, BorderLayout.SOUTH); this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setResizable(false);
this.pack();
this.setVisible(true);
} public static void main(String[] args)
{
new Server("服务器");
}
}
package com.nyohh.chat.server;import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;import javax.swing.JFrame;import com.nyohh.chat.util.CharacterUtil;/**
 * 这个线程是监听新登录用户<br>
 * 默认端口是输入的端口号,客户端需要输入一致的端口号<br>
 * <br>
 * 监听这个端口用ServerSocket(port),监听到信息后自动打包成socket交给后续处理<br>
 */
public class ConnectThread extends Thread
{
private JFrame frame; // 持有server的引用
private ServerSocket serverSocket; // 监听用户输入的默认端口 public ConnectThread(JFrame frame, int port)
{
this.setName("线程:ConnectThread");

this.frame = frame; try
{
serverSocket = new ServerSocket(port);
}
catch (IOException e)
{
e.printStackTrace();
} } @Override
public void run()
{
while (true)// 因为要一直等待用户登录,所以循环
{
try
{
Socket socket = serverSocket.accept(); // 客户端登录发送过来的信息
String loginMsg = readLoginMsg(socket); String[] temp = loginMsg.split("@@@"); // 检测重名是否
Map<String, String> map = ((Server) frame).getMap();
boolean flag = CharacterUtil.isUsernameDuplicated(map, temp[0]); // 回复给当前连接客户端消息
returnLoginMsg(socket, flag); // 添加用户进本地列表
map.put(temp[0], temp[1]); // 组成新的用户列表字符串
String userList = nameMapToNameString(map); // 更新服务端列表
((Server) frame).getjTextArea().setText(userList); // 遍历发送所有用户新的用户列表
sendAllUserNamelist(map, userList);
}
catch (Exception e)
{
e.printStackTrace();
}
} } /**
 * 遍历当前所有用户,把新的用户列表字符串发送给用户
 */
private void sendAllUserNamelist(Map<String, String> map, String userList) throws NumberFormatException, UnknownHostException, IOException
{
Set<Map.Entry<String, String>> set = map.entrySet();
for (Iterator<Map.Entry<String, String>> iterator = set.iterator(); iterator.hasNext();)
{
Map.Entry<String, String> entry = iterator.next(); String[] temp = entry.getValue().split("_");// [0]端口1接收消息,[1]端口2接收用户列表的,[2]客户端地址 Socket socket = new Socket(InetAddress.getByName(temp[2]), Integer.valueOf(temp[1]));
OutputStream outputStream = socket.getOutputStream(); outputStream.write(userList.getBytes()); outputStream.close();
socket.close(); }
} /**
 * 把map里面的名字组合成长字符串发送时候用
 */
private String nameMapToNameString(Map<String, String> map)
{
Set<String> set = map.keySet();
StringBuffer sb = new StringBuffer(); for (Iterator<String> iterator = set.iterator(); iterator.hasNext();)
{
sb.append(iterator.next() + "\n");
} return sb.toString(); // 得到用户列表字符串
} /**
 * 回执客户端一个消息,是否用户名重复<br>
 * 重名,回复ERROR;<br>
 * 正确,回复SUCCESS@@@port1_port2<br>
 * port1为PORT1<br>
 * port2为PORT2
 */
private void returnLoginMsg(Socket socket, boolean flag) throws IOException
{
OutputStream outputStream = socket.getOutputStream(); String returnMsg;// 根据用户名情况组成不同的字符串,回复给当前登录用户
if (flag)// 检测重名
{
returnMsg = CharacterUtil.ERROR;
}
else
{
returnMsg = CharacterUtil.SUCCESS + "@@@" + CharacterUtil.PORT1 + "_" + CharacterUtil.PORT2;
} outputStream.write(returnMsg.getBytes()); outputStream.close();
} /**
 * 把输入流接进来,收到客户端发送过来的的字符串<br>
 * 格式:username@@@port1_port2_address<br>
 * port1为工具里面的randomPort1<br>
 * port2为工具里面的randomPort2<br>
 */
private String readLoginMsg(Socket socket) throws IOException
{
InputStream inputStream = socket.getInputStream(); byte[] buffer = new byte[100];
int length = inputStream.read(buffer);
String loginMsg = new String(buffer, 0, length); inputStream.close(); return loginMsg;
}}
关闭多线程咨询界面

解决方案 »

  1.   

    工具package com.nyohh.chat.util;import java.util.Iterator;
    import java.util.Map;public class CharacterUtil
    {
    public static final String ERROR = "ERROR";
    public static final String SUCCESS = "SUCCESS"; public static String SERVER_HOST;// 客户端填入的服务器地址信息
    public static String CLIENT_NAME;// 客户端填入的用户名 // 服务器端口号,从服务端线程t1获得的端口号port1_port2
    // 用CharacterUtil.PORT + "_" + CharacterUtil.PORT2生成的
    public static String SERVER_PORT; public static int randomPort1 = generatePort();// 客户端接收消息端口号
    public static int randomPort2 = generatePort();// 客户端接收用户列表的端口号 public static int PORT1 = generatePort(); // 接收客户端退出信息的端口
    public static int PORT2 = generatePort(); // 向所有客户端发送消息的端口
    public static int PORT3 = generatePort(); // 接收客户端发送到服务器端消息的端口 /**
     * 判断给定的字符串是否为空,为空返回true,否则你返回false
     */
    public static boolean isEmpty(String str)
    {
    if ("".equals(str))
    {
    return true;
    } return false;
    } /**
     * 判断给定的字符串是否是数字,是数字返回true,否则返回false
     */
    public static boolean isNumber(String str)
    {
    for (int i = 0; i < str.length(); i++)
    {
    if (!Character.isDigit(str.charAt(i)))
    {
    return false;
    }
    } return true;
    } /**
     * 判断服务器端口号是否在正确的范围内,在范围内返回true,否则返回false
     */
    public static boolean isPortCorrect(String port)
    {
    int temp = Integer.parseInt(port);
    if (temp < 1024 || temp > 65535)
    {
    return false;
    } return true;
    } /**
     * 产生随机的端口号,该随机端口号>=1025
     */
    public static int generatePort()
    {
    int port = (int) (Math.random() * 50000 + 1025);
    return port;
    } /**
     * 判断服务器上的用户列表是否有重名,重名返回true,没有返回false
     */
    public static boolean isUsernameDuplicated(Map<String, String> map, String username)
    {
    for (Iterator<String> iterator = map.keySet().iterator(); iterator.hasNext();)
    {
    if (username.equals(iterator.next()))
    {
    return true;
    }
    }
    return false;
    } /**
     * 检测用户名不能包含@与/符号
     */
    public static boolean isCorrect(String str)
    {
    for (int i = 0; i < str.length(); i++)
    {
    char ch = str.charAt(i);
    if ('@' == ch || '/' == ch)
    {
    return false;
    }
    }
    return true;
    } /**
     * 拆分服务端端口字符串<br>
     * 服务器端口号,从服务端线程t1获得的端口号port1_port2<br>
     * 用CharacterUtil.PORT + "_" +CharacterUtil.PORT2生成的
     */
    public static int[] string2Array(String string)
    {
    String[] temp = string.split("_"); int[] ports = new int[2];
    for (int i = 0; i < temp.length; i++)
    {
    ports[i] = Integer.valueOf(temp[i]);
    } return ports;
    }}
      

  2.   

    求知道,自己顶上去下,知道的麻烦说下,其它已经写好,就等待这里给点提示,需要给死循环线程写标记的,我就去添加,不需要的就只是这么closing完就结束了