这个多线程聊天室是用swing的WindowBuilder做的页面,已经测试过了,可能有点小瑕疵,可留言指出
package test9;
/**
* 用户信息
* @author Administrator
*
*/
public class User {
private String name;
private String ip;
public User() {
super();
// TODO Auto-generated constructor stub
}
public User(String name, String ip) {
super();
this.name = name;
this.ip = ip;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getIp() {
return ip;
}
public void setIp(String ip) {
this.ip = ip;
}
}
<pre name="code" class="java">package test9;
import java.awt.BorderLayout;
public class Server extends JFrame {
private JPanel contentPane;
private JTextField textPort;
private JTextField textSend;
private boolean isStart = false;
private List<ClientThread> client = null;
private JTextArea textShow;
private ServerSocket serverSocket;
private ServerThread serverThread;
private JButton btnStop, btnStart;
/**
* Launch the application.
*/
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
Server frame = new Server();
frame.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
/**
* Create the frame.
*/
public Server() {
setTitle("服务器");
setResizable(false);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setBounds(100, 100, 400, 500);
setLocationRelativeTo(null);
contentPane = new JPanel();
contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
contentPane.setLayout(new BorderLayout(0, 0));
setContentPane(contentPane);
JPanel panel = new JPanel();
panel.setBorder(new TitledBorder(null, "\u914D\u7F6E\u4FE1\u606F",
TitledBorder.LEADING, TitledBorder.TOP, null, null));
contentPane.add(panel, BorderLayout.NORTH);
JLabel lblNewLabel = new JLabel("端口号:");
panel.add(lblNewLabel);
textPort = new JTextField();
panel.add(textPort);
textPort.setColumns(10);
btnStart = new JButton("启动");
btnStart.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
start();
}
});
panel.add(btnStart);
btnStop = new JButton("停止");
btnStop.setEnabled(false);
btnStop.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
stop();
}
});
panel.add(btnStop);
JScrollPane scrollPane = new JScrollPane();
contentPane.add(scrollPane, BorderLayout.CENTER);
textShow = new JTextArea();
textShow.setEditable(false);
scrollPane.setViewportView(textShow);
JPanel panel_1 = new JPanel();
contentPane.add(panel_1, BorderLayout.SOUTH);
textSend = new JTextField();
textSend.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
send();
}
});
textSend.setColumns(25);
panel_1.add(textSend);
JButton btnSend = new JButton("发送");
btnSend.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
send();
}
});
panel_1.add(btnSend);
}
/**
* 发送消息
*/
public void send() {
if (!isStart) {
JOptionPane.showMessageDialog(this, "服务器还未启动,请先启动服务器");
return;
}
if (client.size() == 0) {
JOptionPane.showMessageDialog(this, "没有用户在线,不能发送消息!");
return;
}
String message = textSend.getText().trim(); // 去掉字符串头部和尾部的空字符串
if (message == null || message.equals("")) {
JOptionPane.showMessageDialog(this, "消息不能为空!");
return;
}
sendServerMessage(message); // 群发消息
textShow.append("服务器说:" + textSend.getText() + "\r\n");
textSend.setText(null);
}
/**
* 群发消息
*
* @param message
*/
public void sendServerMessage(String message) {
for (int i = client.size() - 1; i >= 0; i--) {
client.get(i).getWrite().println("服务器:" + message);
client.get(i).getWrite().flush();
}
}
/**
* 单击启动按钮事件
*
* @param port
* @return
*/
public void start() {
if (isStart) {
JOptionPane.showMessageDialog(this, "服务器已经启动过啦!");
return;
}
int port = -1;
try {
port = Integer.parseInt(textPort.getText());
if (port < 0) {
JOptionPane.showMessageDialog(this, "端口号为正整数!");
return;
}
serverStart(port);
textShow.append("服务器已启动!端口号:" + port + "\r\n");
JOptionPane.showMessageDialog(this, "服务器启动成功!");
btnStart.setEnabled(false); // 开始按钮锁定
textPort.setEditable(false); // 端口号文本框锁定
btnStop.setEnabled(true); // 停止按钮解除锁定
} catch (Exception e) {
JOptionPane.showMessageDialog(this, "端口号为正整数!");
}
}
/**
* 单击停止按钮事件
*/
public void stop() {
if (!isStart) {
JOptionPane.showMessageDialog(this, "服务器还未启动,无需停止!");
return;
}
try {
closeServer();
btnStart.setEnabled(true);
textPort.setEditable(true);
btnStop.setEnabled(false);
textShow.append("服务器已成功停止!\r\n");
JOptionPane.showMessageDialog(this, "服务器已停止!");
} catch (Exception e) {
// TODO: handle exception
JOptionPane.showMessageDialog(this, "停止服务器发生异常!");
}
}
/**
* 启动服务器
*/
public void serverStart(int port) {
client = new ArrayList<ClientThread>();
try {
serverSocket = new ServerSocket(port);
serverThread = new ServerThread(serverSocket);
serverThread.start();
isStart = true;
} catch (BindException e) {
isStart = false;
JOptionPane.showMessageDialog(this, "端口号被占用!");
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
isStart = false;
JOptionPane.showMessageDialog(this, "服务器启动异常");
}
}
/**
* 关闭服务器
*/
public void closeServer() {
try {
if (serverThread != null) {
serverThread.stop(); // 停止服务线程
}
// 发布给所有在线用户下线的消息
for (int i = client.size() - 1; i >= 0; i--) {
client.get(i).getWrite().println("CLOSE");
client.get(i).getWrite().flush();
// 释放资源
client.get(i).stop(); // 停止此条为客户服务的线程
client.get(i).read.close();
client.get(i).write.close();
client.get(i).socket.close();
client.remove(i);
}
// 关闭服务器连接
if (serverSocket != null) {
serverSocket.close();
}
isStart = false;
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
isStart = true;
}
}
/**
* 服务器线程
*
* @author Administrator
*
*/
class ServerThread extends Thread {
private ServerSocket serverSocket;
public ServerThread(ServerSocket serverSocket) {
this.serverSocket = serverSocket;
}
@Override
public void run() {
// 不停地等待客户连接
while (true) {
try {
Socket socket = serverSocket.accept();
ClientThread clients = new ClientThread(socket);
clients.start(); // 开启客户端服务线程
client.add(clients);
textShow.append(clients.getUser().getName()
+ clients.getUser().getIp() + "上线了!\r\n");
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
/**
* 为每个客户端服务的线程
*
* @author Administrator
*
*/
class ClientThread extends Thread {
private Socket socket;
private BufferedReader read;
private PrintWriter write;
private User user;
public BufferedReader getRead() {
return read;
}
public PrintWriter getWrite() {
return write;
}
public User getUser() {
return user;
}
public ClientThread(Socket socket) {
super();
this.socket = socket;
try {
read = new BufferedReader(new InputStreamReader(
socket.getInputStream()));
write = new PrintWriter(socket.getOutputStream());
// 接受用户信息
String info = read.readLine();
StringTokenizer st = new StringTokenizer(info, "@");
user = new User(st.nextToken(), st.nextToken());
// 反馈连接成功的信息
write.print(user.getName() + user.getIp() + "与服务器连接成功!\r\n");
write.flush();
// 反馈当前所有在线用户信息
if (client.size() > 0) {
String temp = "";
for (int i = client.size() - 1; i >= 0; i--) {
temp += (client.get(i).getUser().getName() + "/" + client
.get(i).getUser().getIp())
+ "@";
}
write.print("USERLIST@" + client.size() + "@" + temp);
write.flush();
}
// 向所有的用户发送该用户上线的消息
for (int i = client.size() - 1; i >= 0; i--) {
client.get(i).getWrite()
.println("ADD@" + user.getName() + user.getIp());
client.get(i).getWrite().flush();
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
/**
* 不断接受客户传来的消息,进行处理
*/
@Override
public void run() {
String message = null;
while (true) {
try {
message = read.readLine(); // 接受消息
if (message.equals("CLOSE")) { // 下线命令
textShow.append(this.getUser().getName()
+ this.getUser().getIp() + "下线!\r\n");
// 断开连接的资源
read.close();
write.close();
socket.close();
// 向所有在线用户发送该用户下线的消息
for (int i = client.size() - 1; i >= 0; i--) {
client.get(i)
.getWrite()
.println(
"DELETE@"
+ client.get(i).getUser()
.getName());
client.get(i).getWrite().flush();
}
// 删除此条客户端的服务线程
for (int i = client.size() - 1; i >= 0; i--) {
if (client.get(i).getUser() == user) {
ClientThread temp = client.get(i);
client.remove(i); // 删除此用户的服务线程
temp.stop(); // 停止该条线程
return;
}
}
} else {
dispatcherMessage(message);
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
/**
* 转发消息
*
* @param message
*/
public void dispatcherMessage(String message) {
StringTokenizer st = new StringTokenizer(message, "@");
String source = st.nextToken();
String owner = st.nextToken();
String contant = st.nextToken();
message = source + "说:" + contant;
textShow.append(message + "\r\n");
if (owner.equals("ALL")) { // 群发
for (int i = client.size() - 1; i >= 0; i--) {
client.get(i).getWrite().println(message);
client.get(i).getWrite().flush();
}
}
}
}
<pre name="code" class="java">package test9;
import java.awt.BorderLayout;
public class Client extends JFrame {
private JPanel contentPane;
private JTextField textIp;
private JTextField textPort;
private JTextField textName;
private JTextField textSend;
private boolean isCon = false;
private BufferedReader read;
private PrintWriter write;
private Socket socket;
private JTextArea textShow;
private JButton btnCon;
private JButton btnCurrent;
private Map<String, User> onLineUser = new HashMap<String, User>(); // 所有在线的用户
private MessageThread messageThread;
/**
* Launch the application.
*/
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
Client frame = new Client();
frame.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
/**
* Create the frame.
*/
public Client() {
setResizable(false);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setBounds(100, 100, 594, 500);
setLocationRelativeTo(null);
contentPane = new JPanel();
contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
setContentPane(contentPane);
contentPane.setLayout(new BorderLayout(0, 0));
JPanel panel = new JPanel();
panel.setBorder(new TitledBorder(null, "\u914D\u7F6E\u4FE1\u606F",
TitledBorder.LEADING, TitledBorder.TOP, null, null));
contentPane.add(panel, BorderLayout.NORTH);
JLabel lblIp = new JLabel("IP地址:");
panel.add(lblIp);
textIp = new JTextField();
panel.add(textIp);
textIp.setColumns(7);
JLabel label = new JLabel("端口号:");
panel.add(label);
textPort = new JTextField();
panel.add(textPort);
textPort.setColumns(5);
JLabel label_1 = new JLabel("姓名:");
panel.add(label_1);
textName = new JTextField();
panel.add(textName);
textName.setColumns(5);
btnCon = new JButton("连接");
btnCon.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
con();
}
});
panel.add(btnCon);
btnCurrent = new JButton("断开");
btnCurrent.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
current();
}
});
btnCurrent.setEnabled(false);
panel.add(btnCurrent);
JScrollPane scrollPane = new JScrollPane();
contentPane.add(scrollPane, BorderLayout.CENTER);
textShow = new JTextArea();
textShow.setEditable(false);
scrollPane.setViewportView(textShow);
JPanel panel_1 = new JPanel();
contentPane.add(panel_1, BorderLayout.SOUTH);
textSend = new JTextField();
textSend.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
send();
}
});
panel_1.add(textSend);
textSend.setColumns(40);
JButton btnSend = new JButton("发送");
btnSend.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
send();
}
});
panel_1.add(btnSend);
}
/**
* 发送消息
*/
public void send(){
if(!isCon){
JOptionPane.showMessageDialog(this, "还没有连接服务器,无法发送消息!");
return;
}
String message = textSend.getText().trim();
if(message == null || message.equals("")){
JOptionPane.showMessageDialog(this, "消息不能为空");
return;
}
sendMessage(this.getTitle() + "@" + "ALL" + "@" + message);
textSend.setText(null);
}
/**
* 点击连接按钮事件处理
*/
public void con() {
int port = -1;
if(isCon){
JOptionPane.showMessageDialog(this, "已经处于连接状态,不能重复连接!");
return;
}
try {
try {
port = Integer.parseInt(textPort.getText().trim());
} catch (Exception e) {
JOptionPane.showMessageDialog(this, "输入的端口号不规范,要求为整数!");
}
String hostIp = textIp.getText().trim();
String name = textName.getText().trim();
if(hostIp.equals("") || name.equals("")){
JOptionPane.showMessageDialog(this, "Ip地址和用户名都不能为空!");
return;
}
boolean flag = connecServer(port, hostIp, name);
if(flag == false){
JOptionPane.showMessageDialog(this, "与服务器连接失败!");
return;
}
this.setTitle(name);
JOptionPane.showMessageDialog(this, "成功连接!");
textPort.setEditable(false);
textIp.setEditable(false);
textName.setEditable(false);
btnCon.setEnabled(false);
btnCurrent.setEnabled(true);
} catch (Exception e) {
JOptionPane.showMessageDialog(this, e.toString());
}
}
/**
* 断开事件处理
*/
public void current(){
if(!isCon){
JOptionPane.showMessageDialog(this, "已经是断开状态了哦!");
return;
}
try {
boolean flag = closeCon(); //断开连接
if(!flag){
JOptionPane.showMessageDialog(this, "断开连接发生异常!");
return;
}
JOptionPane.showConfirmDialog(this,"断开成功!");
textPort.setEditable(true);
textIp.setEditable(true);
textName.setEditable(true);
btnCon.setEnabled(true);
btnCurrent.setEnabled(false);
} catch (Exception e) {
// TODO: handle exception
JOptionPane.showMessageDialog(this, e.toString());
}
}
/**
* 连接服务器
* @param port
* @param hostIp
* @param name
* @return
*/
public boolean connecServer(int port, String hostIp, String name) {
try {
socket = new Socket(hostIp, port); // 根据端口号号和服务器
write = new PrintWriter(socket.getOutputStream());
read = new BufferedReader(new InputStreamReader(
socket.getInputStream()));
//发送客户端的基本信息
sendMessage(name+"@"+socket.getLocalAddress().toString());
//开启接收消息的线程
messageThread = new MessageThread(read, textShow);
messageThread.start();
isCon = true; //状态改为:已连接
return true;
} catch (Exception e) {
// TODO: handle exception
textShow.append("与端口号为:"+port+",Ip地址为:"+hostIp+"的服务器连接失败!\r\n");
isCon = false; //状态为:未连接
return false;
}
}
/**
* 客户端主动关闭连接
*
* @return
*/
public synchronized boolean closeCon() {
try {
sendMessage("CLOSE"); // 发送断开连接命令给服务器
messageThread.stop(); // 停止接受消息的线程
// 释放资源
if (read != null) {
read.close();
}
if (write != null) {
write.close();
}
if (socket != null) {
socket.close();
}
isCon = false;
return true;
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
isCon = true;
return false;
}
}
/**
* 发送消息
*
* @param message
*/
public void sendMessage(String message) {
write.println(message);
write.flush();
}
/**
* 不断接受消息的线程
* @author Administrator
*
*/
class MessageThread extends Thread {
private BufferedReader read;
private JTextArea textShow;
// 接收消息线程的构造方法
public MessageThread(BufferedReader read, JTextArea textShow) {
super();
this.read = read;
this.textShow = textShow;
}
// 被动关闭连接
public synchronized void closeCon() throws Exception {
// 被动关闭连接释放资源
if (read != null) {
read.close();
}
if (write != null) {
write.close();
}
if (socket != null) {
socket.close();
}
isCon = false; // 将状态改为未连接状态
textPort.setEditable(true);
textIp.setEditable(true);
textName.setEditable(true);
btnCon.setEnabled(true);
btnCurrent.setEnabled(false);
}
@Override
public void run() { // 不断接受消息
String message = "";
while (true) {
try {
message = read.readLine();
StringTokenizer st = new StringTokenizer(message, "/@");
String command = st.nextToken(); // 命令
if (command.equals("CLOSE")) { // 关闭命令
textShow.append("服务器已关闭!\r\n");
closeCon(); // 被动关闭连接
return; // 结束线程
} else if (command.equals("ADD")) { // 有用户上线更新列表
String userName = "";
String userIp = "";
if ((userName = st.nextToken()) != null
&& (userIp = st.nextToken()) != null) {
User user = new User(userName, userIp);
onLineUser.put(userName, user);
}
} else if (command.equals("DELETE")) { // 有用户下线更新列表
String userName = st.nextToken();
User user = (User) onLineUser.get(userName);
onLineUser.remove(userName);
} else if (command.equals("USERLIST")) {
int size = Integer.parseInt(st.nextToken());
String userName = null;
String userIp = null;
for (int i = 0; i < size; i++) {
userName = st.nextToken();
userIp = st.nextToken();
User user = new User(userName, userIp);
onLineUser.put(userName, user);
}
} else { // 普通消息
textShow.append(message + "\r\n");
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}
显示效果: