Java聊天室
阿新 • • 發佈:2019-01-01
1.首先是伺服器的介面實現,如下:
2.如何實現呢?主要使用swing和awt,首先整個是一個Frame容器,然後設定它的佈局為BoderLayout,最上邊的是一個Panel,設定它的佈局為GridLayout,中間使用了JSplitPane,把左右兩個面板放在裡面
、
最下邊也是一個面板,裡面套了寫元件
package com.example.li; import java.awt.BorderLayout; import java.awt.Color; import java.awt.GridLayout; import java.awt.Toolkit; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintWriter; import java.net.BindException; import java.net.ServerSocket; import java.net.Socket; import java.util.ArrayList; import java.util.StringTokenizer; import javax.swing.DefaultListModel; import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JList; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JSplitPane; import javax.swing.JTextArea; import javax.swing.JTextField; import javax.swing.border.TitledBorder; public class Server { private JFrame frame; private JTextArea contentArea; private JTextField txt_message; private JTextField txt_max; private JTextField txt_port; private JButton btn_start; private JButton btn_stop; private JButton btn_send; private JPanel northPanel; private JPanel southPanel; private JScrollPane rightPanel; private JScrollPane leftPanel; private JSplitPane centerSplit; private JList userList; private DefaultListModel listModel; private ServerSocket serverSocket; //private ServerThread serverThread; //private ArrayList<ClientThread> clients; private boolean isStart = false; public static void main(String []args) { new Server(); } public Server() { frame = new JFrame("伺服器"); //放在bin下包中檔案 frame.setIconImage(Toolkit.getDefaultToolkit().createImage(Server.class.getResource("qq.jpg"))); contentArea = new JTextArea(); contentArea.setEditable(false); contentArea.setForeground(Color.pink); txt_message = new JTextField(); txt_max = new JTextField("30"); txt_port = new JTextField("6666"); btn_start = new JButton("啟動"); btn_stop = new JButton("停止"); btn_send = new JButton("傳送"); btn_stop.setEnabled(false); //使用了defaultListModel可以是列表的可選項是動態改變的!如果不用,列表中只能出現程式碼中定義的那些可選項 listModel = new DefaultListModel(); southPanel = new JPanel(new BorderLayout()); southPanel.setBorder(new TitledBorder("寫訊息")); southPanel.add(txt_message,"Center"); southPanel.add(btn_send,"East"); leftPanel = new JScrollPane(userList); leftPanel.setBorder(new TitledBorder("線上使用者")); rightPanel = new JScrollPane(contentArea); rightPanel.setBorder(new TitledBorder("訊息顯示區")); centerSplit = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT,leftPanel,rightPanel); northPanel = new JPanel(); northPanel.setLayout(new GridLayout(1,6)); northPanel.add(new JLabel("人數上限")); northPanel.add(txt_max); northPanel.add(new JLabel("埠")); northPanel.add(txt_port); northPanel.add(btn_start); northPanel.add(btn_stop); northPanel.setBorder(new TitledBorder("配置資訊")); frame.setLayout(new BorderLayout()); frame.add(northPanel,"North"); frame.add(centerSplit,"Center"); frame.add(southPanel, "South"); frame.setSize(600, 400); int screen_width = Toolkit.getDefaultToolkit().getScreenSize().width; int screen_height = Toolkit.getDefaultToolkit().getScreenSize().height; frame.setLocation((screen_width-frame.getWidth())/2,(screen_height-frame.getHeight())/2); frame.setVisible(true); } }
介面已經實現,下面改實現伺服器應有的功能,伺服器功能需求分析:
1.接發信息(群發)
2.開啟和關閉伺服器
3.線上使用者顯示(實時修改)
具體步驟分析:
首先需要先開啟伺服器才能傳送資訊,啟動伺服器時,檢查相關配置資訊是否正確,如埠號
服務開啟後,建立一個伺服器執行緒來管理,並監聽相關埠,若伺服器執行緒監聽到客戶端請求(客戶端會發出一些關於自己的使用者資訊,給伺服器處理),則建立一個客戶端執行緒來處理請求,客戶端執行緒則處理來自客戶端的資訊,向所有線上使用者反饋資訊,併發送其上線通知,同時加入到 使用者列表中
伺服器關閉後,需要釋放所有客戶端執行緒資源和緩衝流資源
群發訊息,則通過已有的所有客戶端執行緒列表,向所有線上使用者傳送資訊
package com.example.li;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.GridLayout;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.BindException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.StringTokenizer;
import javax.swing.DefaultListModel;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSplitPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.border.TitledBorder;
public class Server {
private JFrame frame;
private JTextArea contentArea;
private JTextField txt_message;
private JTextField txt_max;
private JTextField txt_port;
private JButton btn_start;
private JButton btn_stop;
private JButton btn_send;
private JPanel northPanel;
private JPanel southPanel;
private JScrollPane rightPanel;
private JScrollPane leftPanel;
private JSplitPane centerSplit;
private JList userList;
private DefaultListModel listModel;
private ServerSocket serverSocket;
private ServerThread serverThread;
private ArrayList<ClientThread> clients;
private boolean isStart = false;
// 主方法,程式執行入口
public static void main(String[] args) {
new Server();
}
// 執行訊息傳送
public void send() {
if (!isStart) {
JOptionPane.showMessageDialog(frame, "伺服器還未啟動,不能傳送訊息!", "錯誤",
JOptionPane.ERROR_MESSAGE);
return;
}
if (clients.size() == 0) {
JOptionPane.showMessageDialog(frame, "沒有使用者線上,不能傳送訊息!", "錯誤",
JOptionPane.ERROR_MESSAGE);
return;
}
String message = txt_message.getText().trim();
if (message == null || message.equals("")) {
JOptionPane.showMessageDialog(frame, "訊息不能為空!", "錯誤",
JOptionPane.ERROR_MESSAGE);
return;
}
sendServerMessage(message);// 群發伺服器訊息
contentArea.append("伺服器說:" + txt_message.getText() + "\r\n");
txt_message.setText(null);
}
// 構造放法
public Server() {
frame = new JFrame("伺服器");
// 更改JFrame的圖示:
//frame.setIconImage(Toolkit.getDefaultToolkit().createImage(Client.class.getResource("qq.png")));
frame.setIconImage(Toolkit.getDefaultToolkit().createImage(Server.class.getResource("qq.jpg")));
contentArea = new JTextArea();
contentArea.setEditable(false);
contentArea.setForeground(Color.blue);
txt_message = new JTextField();
txt_max = new JTextField("30");
txt_port = new JTextField("6666");
btn_start = new JButton("啟動");
btn_stop = new JButton("停止");
btn_send = new JButton("傳送");
btn_stop.setEnabled(false);
listModel = new DefaultListModel();
userList = new JList(listModel);
southPanel = new JPanel(new BorderLayout());
southPanel.setBorder(new TitledBorder("寫訊息"));
southPanel.add(txt_message, "Center");
southPanel.add(btn_send, "East");
leftPanel = new JScrollPane(userList);
leftPanel.setBorder(new TitledBorder("線上使用者"));
rightPanel = new JScrollPane(contentArea);
rightPanel.setBorder(new TitledBorder("訊息顯示區"));
centerSplit = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, leftPanel,
rightPanel);
centerSplit.setDividerLocation(100);
northPanel = new JPanel();
northPanel.setLayout(new GridLayout(1, 6));
northPanel.add(new JLabel("人數上限"));
northPanel.add(txt_max);
northPanel.add(new JLabel("埠"));
northPanel.add(txt_port);
northPanel.add(btn_start);
northPanel.add(btn_stop);
northPanel.setBorder(new TitledBorder("配置資訊"));
frame.setLayout(new BorderLayout());
frame.add(northPanel, "North");
frame.add(centerSplit, "Center");
frame.add(southPanel, "South");
frame.setSize(600, 400);
//frame.setSize(Toolkit.getDefaultToolkit().getScreenSize());//設定全屏
int screen_width = Toolkit.getDefaultToolkit().getScreenSize().width;
int screen_height = Toolkit.getDefaultToolkit().getScreenSize().height;
frame.setLocation((screen_width - frame.getWidth()) / 2,
(screen_height - frame.getHeight()) / 2);
frame.setVisible(true);
// 關閉視窗時事件
frame.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
if (isStart) {
closeServer();// 關閉伺服器
}
System.exit(0);// 退出程式
}
});
// 文字框按回車鍵時事件
txt_message.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
send();
}
});
// 單擊發送按鈕時事件
btn_send.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
send();
}
});
// 單擊啟動伺服器按鈕時事件
btn_start.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
if (isStart) {
JOptionPane.showMessageDialog(frame, "伺服器已處於啟動狀態,不要重複啟動!",
"錯誤", JOptionPane.ERROR_MESSAGE);
return;
}
int max;
int port;
try {
try {
max = Integer.parseInt(txt_max.getText());
} catch (Exception e1) {
throw new Exception("人數上限為正整數!");
}
if (max <= 0) {
throw new Exception("人數上限為正整數!");
}
try {
port = Integer.parseInt(txt_port.getText());
} catch (Exception e1) {
throw new Exception("埠號為正整數!");
}
if (port <= 0) {
throw new Exception("埠號 為正整數!");
}
serverStart(max, port);
contentArea.append("伺服器已成功啟動!人數上限:" + max + ",埠:" + port
+ "\r\n");
JOptionPane.showMessageDialog(frame, "伺服器成功啟動!");
btn_start.setEnabled(false);
txt_max.setEnabled(false);
txt_port.setEnabled(false);
btn_stop.setEnabled(true);
} catch (Exception exc) {
JOptionPane.showMessageDialog(frame, exc.getMessage(),
"錯誤", JOptionPane.ERROR_MESSAGE);
}
}
});
// 單擊停止伺服器按鈕時事件
btn_stop.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
if (!isStart) {
JOptionPane.showMessageDialog(frame, "伺服器還未啟動,無需停止!", "錯誤",
JOptionPane.ERROR_MESSAGE);
return;
}
try {
closeServer();
btn_start.setEnabled(true);
txt_max.setEnabled(true);
txt_port.setEnabled(true);
btn_stop.setEnabled(false);
contentArea.append("伺服器成功停止!\r\n");
JOptionPane.showMessageDialog(frame, "伺服器成功停止!");
} catch (Exception exc) {
JOptionPane.showMessageDialog(frame, "停止伺服器發生異常!", "錯誤",
JOptionPane.ERROR_MESSAGE);
}
}
});
}
// 啟動伺服器
public void serverStart(int max, int port) throws java.net.BindException {
try {
clients = new ArrayList<ClientThread>();
serverSocket = new ServerSocket(port);
serverThread = new ServerThread(serverSocket, max);
serverThread.start();
isStart = true;
} catch (BindException e) {
isStart = false;
throw new BindException("埠號已被佔用,請換一個!");
} catch (Exception e1) {
e1.printStackTrace();
isStart = false;
throw new BindException("啟動伺服器異常!");
}
}
// 關閉伺服器
@SuppressWarnings("deprecation")
public void closeServer() {
try {
if (serverThread != null)
serverThread.stop();// 停止伺服器執行緒
for (int i = clients.size() - 1; i >= 0; i--) {
// 給所有線上使用者傳送關閉命令
clients.get(i).getWriter().println("CLOSE");
clients.get(i).getWriter().flush();
// 釋放資源
clients.get(i).stop();// 停止此條為客戶端服務的執行緒
clients.get(i).reader.close();
clients.get(i).writer.close();
clients.get(i).socket.close();
clients.remove(i);
}
if (serverSocket != null) {
serverSocket.close();// 關閉伺服器端連線
}
listModel.removeAllElements();// 清空使用者列表
isStart = false;
} catch (IOException e) {
e.printStackTrace();
isStart = true;
}
}
// 群發伺服器訊息
public void sendServerMessage(String message) {
for (int i = clients.size() - 1; i >= 0; i--) {
clients.get(i).getWriter().println("伺服器:" + message + "(多人傳送)");
clients.get(i).getWriter().flush();
}
}
// 伺服器執行緒
class ServerThread extends Thread {
private ServerSocket serverSocket;
private int max;// 人數上限
// 伺服器執行緒的構造方法
public ServerThread(ServerSocket serverSocket, int max) {
this.serverSocket = serverSocket;
this.max = max;
}
public void run() {
while (true) {// 不停的等待客戶端的連結
try {
Socket socket = serverSocket.accept();
if (clients.size() == max) {// 如果已達人數上限
BufferedReader r = new BufferedReader(
new InputStreamReader(socket.getInputStream()));
PrintWriter w = new PrintWriter(socket
.getOutputStream());
// 接收客戶端的基本使用者資訊
String inf = r.readLine();
StringTokenizer st = new StringTokenizer(inf, "@");
User user = new User(st.nextToken(), st.nextToken());
// 反饋連線成功資訊
w.println(" [email protected]伺服器:對不起," + user.getName()
+ user.getIp() + ",伺服器線上人數已達上限,請稍後嘗試連線!");
w.flush();
// 釋放資源
r.close();
w.close();
socket.close();
continue;
}
ClientThread client = new ClientThread(socket);
client.start();// 開啟對此客戶端服務的執行緒
clients.add(client);
listModel.addElement(client.getUser().getName());// 更新線上列表
contentArea.append(client.getUser().getName()
+ client.getUser().getIp() + "上線!\r\n");
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
// 為一個客戶端服務的執行緒
class ClientThread extends Thread {
private Socket socket;
private BufferedReader reader;
private PrintWriter writer;
private User user;
public BufferedReader getReader() {
return reader;
}
public PrintWriter getWriter() {
return writer;
}
public User getUser() {
return user;
}
// 客戶端執行緒的構造方法
public ClientThread(Socket socket) {
try {
this.socket = socket;
reader = new BufferedReader(new InputStreamReader(socket
.getInputStream()));
writer = new PrintWriter(socket.getOutputStream());
// 接收客戶端的基本使用者資訊
String inf = reader.readLine();
StringTokenizer st = new StringTokenizer(inf, "@");
user = new User(st.nextToken(), st.nextToken());
// 反饋連線成功資訊
writer.println(user.getName() + user.getIp() + "與伺服器連線成功!");
writer.flush();
// 反饋當前線上使用者資訊
if (clients.size() > 0) {
String temp = "";
for (int i = clients.size() - 1; i >= 0; i--) {
temp += (clients.get(i).getUser().getName() + "/" + clients
.get(i).getUser().getIp())
+ "@";
}
writer.println("[email protected]" + clients.size() + "@" + temp);
writer.flush();
}
// 向所有線上使用者傳送該使用者上線命令
for (int i = clients.size() - 1; i >= 0; i--) {
clients.get(i).getWriter().println(
"[email protected]" + user.getName() + user.getIp());
clients.get(i).getWriter().flush();
}
} catch (IOException e) {
e.printStackTrace();
}
}
@SuppressWarnings("deprecation")
public void run() {// 不斷接收客戶端的訊息,進行處理。
String message = null;
while (true) {
try {
message = reader.readLine();// 接收客戶端訊息
if (message.equals("CLOSE"))// 下線命令
{
contentArea.append(this.getUser().getName()
+ this.getUser().getIp() + "下線!\r\n");
// 斷開連線釋放資源
reader.close();
writer.close();
socket.close();
// 向所有線上使用者傳送該使用者的下線命令
for (int i = clients.size() - 1; i >= 0; i--) {
clients.get(i).getWriter().println(
"[email protected]" + user.getName());
clients.get(i).getWriter().flush();
}
listModel.removeElement(user.getName());// 更新線上列表
// 刪除此條客戶端服務執行緒
for (int i = clients.size() - 1; i >= 0; i--) {
if (clients.get(i).getUser() == user) {
ClientThread temp = clients.get(i);
clients.remove(i);// 刪除此使用者的服務執行緒
temp.stop();// 停止這條服務執行緒
return;
}
}
} else {
dispatcherMessage(message);// 轉發訊息
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
// 轉發訊息
public void dispatcherMessage(String message) {
StringTokenizer stringTokenizer = new StringTokenizer(message, "@");
String source = stringTokenizer.nextToken();
String owner = stringTokenizer.nextToken();
String content = stringTokenizer.nextToken();
message = source + "說:" + content;
contentArea.append(message + "\r\n");
if (owner.equals("ALL")) {// 群發
for (int i = clients.size() - 1; i >= 0; i--) {
System.out.println(i);
clients.get(i).getWriter().println(message + "(多人傳送)");
clients.get(i).getWriter().flush();
}
}
}
}
}
對於客戶端,功能分析如下:
1.連線和斷開伺服器
2.傳送和接受資訊(所有使用者和伺服器)
3.顯示線上使用者
步驟分析如下:
連線時,判斷是否重複連線,若不是重複連線則傳送與伺服器連線請求,並給伺服器傳送相應資訊(便於伺服器增加使用者),
並開啟接受訊息執行緒,而訊息執行緒要做的就是接受來自伺服器和其它客戶端執行緒傳送的訊息,並判斷屬於哪類訊息,如時伺服器關閉或者有新使用者上線或者下線和載入線上使用者列表的資訊,並執行相應操作
package com.example.li;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.GridLayout;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.HashMap;
import java.util.Map;
import java.util.StringTokenizer;
import javax.swing.DefaultListModel;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSplitPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.border.TitledBorder;
public class Client{
private JFrame frame;
private JList<User> userList;
private JTextArea textArea;
private JTextField textField;
private JTextField txt_port;
private JTextField txt_hostIp;
private JTextField txt_name;
private JButton btn_start;
private JButton btn_stop;
private JButton btn_send;
private JPanel northPanel;
private JPanel southPanel;
private JScrollPane rightScroll;
private JScrollPane leftScroll;
private JSplitPane centerSplit;
private DefaultListModel listModel;
private boolean isConnected = false;
private Socket socket;
private PrintWriter writer;
private BufferedReader reader;
private MessageThread messageThread;// 負責接收訊息的執行緒
private Map<String, User> onLineUsers = new HashMap<String, User>();// 所有線上使用者
// 主方法,程式入口
public static void main(String[] args) {
new Client();
}
// 執行傳送
public void send() {
if (!isConnected) {
JOptionPane.showMessageDialog(frame, "還沒有連線伺服器,無法傳送訊息!", "錯誤",
JOptionPane.ERROR_MESSAGE);
return;
}
String message = textField.getText().trim();
if (message == null || message.equals("")) {
JOptionPane.showMessageDialog(frame, "訊息不能為空!", "錯誤",
JOptionPane.ERROR_MESSAGE);
return;
}
sendMessage(frame.getTitle() + "@" + "ALL" + "@" + message);
textField.setText(null);
}
// 構造方法
public Client() {
textArea = new JTextArea();
textArea.setEditable(false);
textArea.setForeground(Color.blue);
textField = new JTextField();
txt_port = new JTextField("6666");
txt_hostIp = new JTextField("127.0.0.1");
txt_name = new JTextField("xiaoqiang");
btn_start = new JButton("連線");
btn_stop = new JButton("斷開");
btn_send = new JButton("傳送");
listModel = new DefaultListModel();
userList = new JList(listModel);
northPanel = new JPanel();
northPanel.setLayout(new GridLayout(1, 7));
northPanel.add(new JLabel("埠"));
northPanel.add(txt_port);
northPanel.add(new JLabel("伺服器IP"));
northPanel.add(txt_hostIp);
northPanel.add(new JLabel("姓名"));
northPanel.add(txt_name);
northPanel.add(btn_start);
northPanel.add(btn_stop);
northPanel.setBorder(new TitledBorder("連線資訊"));
rightScroll = new JScrollPane(textArea);
rightScroll.setBorder(new TitledBorder("訊息顯示區"));
leftScroll = new JScrollPane(userList);
leftScroll.setBorder(new TitledBorder("線上使用者"));
southPanel = new JPanel(new BorderLayout());
southPanel.add(textField, "Center");
southPanel.add(btn_send, "East");
southPanel.setBorder(new TitledBorder("寫訊息"));
centerSplit = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, leftScroll,
rightScroll);
centerSplit.setDividerLocation(100);
frame = new JFrame("客戶機");
// 更改JFrame的圖示:
frame.setIconImage(Toolkit.getDefaultToolkit().createImage(Client.class.getResource("qq.jpg")));
frame.setLayout(new BorderLayout());
frame.add(northPanel, "North");
frame.add(centerSplit, "Center");
frame.add(southPanel, "South");
frame.setSize(600, 400);
int screen_width = Toolkit.getDefaultToolkit().getScreenSize().width;
int screen_height = Toolkit.getDefaultToolkit().getScreenSize().height;
frame.setLocation((screen_width - frame.getWidth()) / 2,
(screen_height - frame.getHeight()) / 2);
frame.setVisible(true);
// 寫訊息的文字框中按回車鍵時事件
textField.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
send();
}
});
// 單擊發送按鈕時事件
btn_send.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
send();
}
});
// 單擊連線按鈕時事件
btn_start.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
int port;
if (isConnected) {
JOptionPane.showMessageDialog(frame, "已處於連線上狀態,不要重複連線!",
"錯誤", JOptionPane.ERROR_MESSAGE);
return;
}
try {
try {
port = Integer.parseInt(txt_port.getText().trim());
} catch (NumberFormatException e2) {
throw new Exception("埠號不符合要求!埠為整數!");
}
String hostIp = txt_hostIp.getText().trim();
String name = txt_name.getText().trim();
if (name.equals("") || hostIp.equals("")) {
throw new Exception("姓名、伺服器IP不能為空!");
}
boolean flag = connectServer(port, hostIp, name);
if (flag == false) {
throw new Exception("與伺服器連線失敗!");
}
frame.setTitle(name);
JOptionPane.showMessageDialog(frame, "成功連線!");
} catch (Exception exc) {
JOptionPane.showMessageDialog(frame, exc.getMessage(),
"錯誤", JOptionPane.ERROR_MESSAGE);
}
}
});
// 單擊斷開按鈕時事件
btn_stop.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
if (!isConnected) {
JOptionPane.showMessageDialog(frame, "已處於斷開狀態,不要重複斷開!",
"錯誤", JOptionPane.ERROR_MESSAGE);
return;
}
try {
boolean flag = closeConnection();// 斷開連線
if (flag == false) {
throw new Exception("斷開連線發生異常!");
}
JOptionPane.showMessageDialog(frame, "成功斷開!");
} catch (Exception exc) {
JOptionPane.showMessageDialog(frame, exc.getMessage(),
"錯誤", JOptionPane.ERROR_MESSAGE);
}
}
});
// 關閉視窗時事件
frame.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
if (isConnected) {
closeConnection();// 關閉連線
}
System.exit(0);// 退出程式
}
});
}
/**
* 連線伺服器
*
* @param port
* @param hostIp
* @param name
*/
public boolean connectServer(int port, String hostIp, String name) {
// 連線伺服器
try {
socket = new Socket(hostIp, port);// 根據埠號和伺服器ip建立連線
writer = new PrintWriter(socket.getOutputStream());
reader = new BufferedReader(new InputStreamReader(socket
.getInputStream()));
// 傳送客戶端使用者基本資訊(使用者名稱和ip地址)
sendMessage(name + "@" + socket.getLocalAddress().toString());
// 開啟接收訊息的執行緒
messageThread = new MessageThread(reader, textArea);
messageThread.start();
isConnected = true;// 已經連線上了
return true;
} catch (Exception e) {
textArea.append("與埠號為:" + port + " IP地址為:" + hostIp
+ " 的伺服器連線失敗!" + "\r\n");
isConnected = false;// 未連線上
return false;
}
}
/**
* 傳送訊息
*
* @param message
*/
public void sendMessage(String message) {
writer.println(message);
writer.flush();
}
/**
* 客戶端主動關閉連線
*/
@SuppressWarnings("deprecation")
public synchronized boolean closeConnection() {
try {
sendMessage("CLOSE");// 傳送斷開連線命令給伺服器
messageThread.stop();// 停止接受訊息執行緒
// 釋放資源
if (reader != null) {
reader.close();
}
if (writer != null) {
writer.close();
}
if (socket != null) {
socket.close();
}
isConnected = false;
return true;
} catch (IOException e1) {
e1.printStackTrace();
isConnected = true;
return false;
}
}
// 不斷接收訊息的執行緒
class MessageThread extends Thread {
private BufferedReader reader;
private JTextArea textArea;
// 接收訊息執行緒的構造方法
public MessageThread(BufferedReader reader, JTextArea textArea) {
this.reader = reader;
this.textArea = textArea;
}
// 被動的關閉連線
public synchronized void closeCon() throws Exception {
// 清空使用者列表
listModel.removeAllElements();
// 被動的關閉連線釋放資源
if (reader != null) {
reader.close();
}
if (writer != null) {
writer.close();
}
if (socket != null) {
socket.close();
}
isConnected = false;// 修改狀態為斷開
}
public void run() {
int a= 0;
String message = "";
while (true) {
System.out.println(a++);
try {
message = reader.readLine();
StringTokenizer stringTokenizer = new StringTokenizer(
message, "/@");
String command = stringTokenizer.nextToken();// 命令
if (command.equals("CLOSE"))// 伺服器已關閉命令
{
textArea.append("伺服器已關閉!\r\n");
closeCon();// 被動的關閉連線
return;// 結束執行緒
} else if (command.equals("ADD")) {// 有使用者上線更新線上列表
String username = "";
String userIp = "";
if ((username = stringTokenizer.nextToken()) != null
&& (userIp = stringTokenizer.nextToken()) != null) {
User user = new User(username, userIp);
onLineUsers.put(username, user);
listModel.addElement(username);
}
} else if (command.equals("DELETE")) {// 有使用者下線更新線上列表
String username = stringTokenizer.nextToken();
User user = (User) onLineUsers.get(username);
onLineUsers.remove(user);
listModel.removeElement(username);
} else if (command.equals("USERLIST")) {// 載入線上使用者列表
int size = Integer
.parseInt(stringTokenizer.nextToken());
String username = null;
String userIp = null;
for (int i = 0; i < size; i++) {
username = stringTokenizer.nextToken();
userIp = stringTokenizer.nextToken();
User user = new User(username, userIp);
onLineUsers.put(username, user);
listModel.addElement(username);
}
} else if (command.equals("MAX")) {// 人數已達上限
textArea.append(stringTokenizer.nextToken()
+ stringTokenizer.nextToken() + "\r\n");
closeCon();// 被動的關閉連線
JOptionPane.showMessageDialog(frame, "伺服器緩衝區已滿!", "錯誤",
JOptionPane.ERROR_MESSAGE);
return;// 結束執行緒
} else {// 普通訊息
textArea.append(message + "\r\n");
}
} catch (IOException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}
使用者類
package com.example.li;
public class User {
private String name;
private String ip;
public User(String name, String ip) {
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;
}
}