//Server.java
package com.mulThread;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import java.net.*;
import java.io.*;
import java.util.*;
/**
*
* @E-mail:[email protected]
*
* @author:young master
*
* @version 1.0
*
* @any questions,please reply to me for deeping improvements
*/
public class Server extends JFrame {
JPanel contentPane;
JMenuBar jMenuBar1 = new JMenuBar();
JMenu jMenuFile = new JMenu();
JMenuItem jMenuFileExit = new JMenuItem();
JMenu jMenuHelp = new JMenu();
JMenuItem jMenuHelpAbout = new JMenuItem();
JLabel statusBar = new JLabel();
BorderLayout borderLayout1 = new BorderLayout();
JPanel jPanel1 = new JPanel();
BorderLayout borderLayout2 = new BorderLayout();
JLabel jLabel1 = new JLabel();
static java.awt.List jList1 = new java.awt.List(13);
JScrollPane scrollpane = new JScrollPane(jList1);
// 以下爲網絡相關變量
static Vector clients = new Vector(10); // 用vector向量數組存儲連接客戶變量
static ServerSocket server = null; // 建立服務器socket
static int active_connects = 0; // 用來存儲目前連接的客戶數
static Socket socket = null; // 用來存儲一個套接字連接
// chatServer main method
public static void main(String[] args) {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (Exception e) {
e.printStackTrace();
}
Server chatServer1 = new Server(); // 實例化一個chatServer類
chatServer1.show();
System.out.println("Server starting ...");
try {
server = new ServerSocket(2525); // 使用端口2525初始化服務器套接字
} catch (IOException e) {
System.out.println("Error:" + e);
}
while (true) {
if (clients.size() < 5) // 當客戶數小於5個時開始連接
{
try {
socket = server.accept(); // 用來存儲連接上的客戶socket
if (socket != null) {
System.out.println(socket + "連接"); // 在控制檯打印客戶連接信息
}
} catch (IOException e) {
System.out.println("Error:" + e);
}
int i = 0;
do {
Client c = new Client(socket); // 定義並實例化一個Client線程類,一個就對應一個客戶連接
clients.addElement(c); // 加入clients數組中
if (checkName(c)) // 調用checkName方法驗證c的合法性
{
int connum = ++chatServer1.active_connects; // 定義connum來存儲活動連接數
String constr = "目前有" + connum + "客戶相連"; // 在狀態欄裏顯示連接數
chatServer1.statusBar.setText(constr);
Client listdata = (Client) clients.elementAt(i); // 將連接客戶的socket信息存儲進listdata數組
chatServer1.jList1.addItem(listdata.ip + "連接", i); // 將客戶socket信息寫入list框
c.start(); // 啓動線程
notifyRoom(); // 用notifyRoom方法來監視聊天室連接變化
// 不斷改變clients數組並刷新客戶端信息
} else {
// 如果名字不合法
c.ps.println("TAKEN");
disconnect(c);
}
i++;
break;
} while (i < clients.size());
} else // 如果clients數組超過了5個
{
try {
Thread.sleep(200);
} catch (InterruptedException e) {
}
}
}// end of while
}// end of main method
/** Construct the frame */
public Server() // chatServer類的構造器用來初始化一些UI信息
{
enableEvents(AWTEvent.WINDOW_EVENT_MASK);
try {
jbInit();
} catch (Exception e) {
e.printStackTrace();
}
}
/** Component initialization */
private void jbInit() throws Exception {
// setIconImage(Toolkit.getDefaultToolkit().createImage(Frame1.class.getResource("[Your
// Icon]")));
contentPane = (JPanel) this.getContentPane();
contentPane.setLayout(borderLayout1);
this.setSize(new Dimension(400, 300));
this.setTitle("簡易聊天服務器端");
statusBar.setText("目前的連接數爲:");
jMenuFile.setText("File");
jMenuFileExit.setText("Exit");
jMenuFileExit.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
jMenuFileExit_actionPerformed(e);
}
});
jMenuHelp.setText("Help");
jMenuHelpAbout.setText("About");
jMenuHelpAbout.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
jMenuHelpAbout_actionPerformed(e);
}
});
jPanel1.setLayout(borderLayout2);
jLabel1.setText("服務器端連接客戶");
jMenuFile.add(jMenuFileExit);
jMenuHelp.add(jMenuHelpAbout);
jMenuBar1.add(jMenuFile);
jMenuBar1.add(jMenuHelp);
this.setJMenuBar(jMenuBar1);
contentPane.add(statusBar, BorderLayout.SOUTH);
contentPane.add(jPanel1, BorderLayout.CENTER);
jPanel1.add(jLabel1, BorderLayout.NORTH);
jPanel1.add(scrollpane, BorderLayout.CENTER);
}// end of jbinit
/** File | Exit action performed */
public void jMenuFileExit_actionPerformed(ActionEvent e) // 實現退出菜單方法
{
sendClients(new StringBuffer("QUIT")); // 向客戶端發送斷開連接信息
closeAll(); // 調用closeAll方法關閉所有連接
System.exit(0);
}
/** Help | About action performed */
public void jMenuHelpAbout_actionPerformed(ActionEvent e) // 實現about對話框,意義不大,可以去掉
{
AboutChat dlg = new AboutChat(this);
Dimension dlgSize = dlg.getPreferredSize();
Dimension frmSize = getSize();
Point loc = getLocation();
dlg.setLocation((frmSize.width - dlgSize.width) / 2 + loc.x,
(frmSize.height - dlgSize.height) / 2 + loc.y);
dlg.setModal(true);
dlg.show();
}
/** Overridden so we can exit when window is closed */
protected void processWindowEvent(WindowEvent e) // 實現關閉服務器程序要進行的操作
{
super.processWindowEvent(e);
if (e.getID() == WindowEvent.WINDOW_CLOSING) {
jMenuFileExit_actionPerformed(null);
}
}
/* 以下實現各種方法 */
public static void notifyRoom() // 用來監視連接信息,不斷刷新clients數組並刷新客戶端用戶列表信息
{
StringBuffer people = new StringBuffer("PEOPLE");
for (int i = 0; i < clients.size(); i++) {
Client c = (Client) clients.elementAt(i);
people.append(":" + c.name);
}
sendClients(people); // 用sendClients方法向客戶端發送信息
}
public static synchronized void sendClients(StringBuffer msg) // 實現sendClients方法專用來向每個連接的客戶端發送信息
{
for (int i = 0; i < clients.size(); i++) {
Client c = (Client) clients.elementAt(i);
c.send(msg);
}
}
public static void closeAll() // 實現關閉所有連接信息
{
while (clients.size() > 0) // 遍歷clients數組刪除所有連接客戶信息
{
Client c = (Client) clients.firstElement();
try {
c.socket.close();
} catch (IOException e) {
System.out.println("Error:" + e);
} finally {
clients.removeElement(c);
}
}// end of while
}// end of closeAll method
public static boolean checkName(Client newclient) // 實現檢查連接客戶的socket信息是否合法
{
for (int i = 0; i < clients.size(); i++) {
Client c = (Client) clients.elementAt(i);
if ((c != newclient) && c.equals(newclient.name))
return false;
}
return (true);
}// end of checkName method
public static synchronized void disconnect(Client c) // 實現斷開單個客戶的方法
{
try {
jList1.addItem(c.ip + "斷開連接"); // 在服務器端程序的list框中顯示斷開信息
Server.active_connects--; // 將連接數減1
c.send(new StringBuffer("QUIT")); // 向客戶發送斷開連接信息
c.socket.close();
} catch (IOException e) {
System.out.println("Error:" + e);
} finally {
clients.removeElement(c); // 從clients數組中刪除此客戶的相關socket等信息
}
}
}
class Client extends Thread // 實現 Client線程類
{
Socket socket; // 用來存儲一個連接客戶的socket信息
String name; // 用來存儲客戶的連接姓名
String ip; // 用來存儲客戶的ip信息
DataInputStream dis; // 用來實現接受從客戶端發來的數據流
PrintStream ps; // 用來實現向客戶端發送信息的打印流
public void send(StringBuffer msg) // 實現想客戶端發送信息的方法
{
ps.println(msg); // 用打印流發送信息
ps.flush();
}
public Client(Socket s) // Client線程類的構造器
{
socket = s;
try {
dis = new DataInputStream(s.getInputStream()); // 存儲特定客戶socket的輸入流接受s這個客戶發送到服務器端的信息
ps = new PrintStream(s.getOutputStream()); // 存儲特定客戶socket的輸出流發送服務器給s這個客戶的信息
String info = dis.readLine(); // 讀取接受來的信息
StringTokenizer stinfo = new StringTokenizer(info, ":"); // 用StringTokenizer類來讀取用":"分段字符
String head = stinfo.nextToken(); // head用來存儲類似於關鍵字的頭信息
if (stinfo.hasMoreTokens())
name = stinfo.nextToken(); // 關鍵字後的第二段數據是客戶名信息
if (stinfo.hasMoreTokens())
ip = stinfo.nextToken(); // 關鍵字後的第三段數據是客戶ip信息
System.out.println(head); // 在控制檯打印頭信息
} catch (IOException e) {
System.out.println("Error:" + e);
}
}// end of Client constrator
public void run() // 線程運行方法
{
while (true) {
String line = null;
try {
line = dis.readLine(); // 讀取客戶端發來的數據流
} catch (IOException e) {
System.out.println("Error" + e);
Server.disconnect(this);
Server.notifyRoom();
return;
}
if (line == null) // 客戶已離開
{
Server.disconnect(this);
Server.notifyRoom();
return;
}
StringTokenizer st = new StringTokenizer(line, ":");
String keyword = st.nextToken();
if (keyword.equals("MSG")) // 如果關鍵字是MSG則是客戶端發來的聊天信息
{
StringBuffer msg = new StringBuffer("MSG:"); // 在服務器端再重新建立一個字符緩衝
msg.append(name);
msg.append(st.nextToken("\0"));
Server.sendClients(msg); // 再將某個客戶發來的聊天信息發送到每個連接客戶的聊天欄中
} else if (keyword.equals("QUIT")) // 如果關鍵字是QUIT則是客戶端發來斷開連接的信息
{
Server.disconnect(this); // 服務器斷開與這個客戶的連接
Server.notifyRoom(); // 繼續監聽聊天室並刷新其他客戶的聊天人名list
this.stop();
}
}
}
} // end of class Client
//ClientChat.java
package com.mulThread;
import java.awt.*;
import java.awt.event.*;
import java.applet.*;
import java.net.*;
import java.io.*;
import java.util.*;
/**
*
* @E-mail:[email protected]
*
* @author:young master
*
* @version 1.0
*
* @any questions,please reply to me for deeping improvements
*/
public class ClientChat extends Applet {
/* 以下用於定義UI變量 */
Panel panel1 = new Panel(); // 用於放置輸入姓名和連接兩個按鈕
BorderLayout borderLayout1 = new BorderLayout();
Panel panel2 = new Panel(); // 用於放置聊天信息顯示和聊天人員列表
Panel panel3 = new Panel(); // 用於放置發送信息區域
FlowLayout flowLayout1 = new FlowLayout();
FlowLayout flowLayout2 = new FlowLayout();
Label label1 = new Label();
TextField name_txt = new TextField(15);
Button button1 = new Button();
Button button2 = new Button();
TextArea chat_txt = new TextArea(15, 30);
Label label2 = new Label();
Button button3 = new Button();
TextField msg_txt = new TextField(20);
java.awt.List list1 = new java.awt.List(13);
/* 以下定義數據流和網絡變量 */
Socket soc = null; // 定義連接套接字
PrintStream ps = null; // 定義打印流
Listen listen = null; // 定義一個客戶端線程
public void init() // 初始化圖形界面
{
resize(475, 350);
this.setLayout(borderLayout1);
panel2.setLayout(flowLayout1);
panel3.setLayout(flowLayout2);
label1.setText("用戶名:");
button1.setLabel("連接");
button2.setLabel("斷開連接");
chat_txt.setEditable(false);
panel2.setBackground(Color.cyan);
panel1.setBackground(Color.cyan);
label2.setText("聊天信息:");
button3.setLabel("發送");
msg_txt.setText("請輸入聊天信息");
panel3.setBackground(Color.cyan);
this.add(panel1, BorderLayout.NORTH);
panel1.add(label1, null);
panel1.add(name_txt, null);
panel1.add(button1, null);
panel1.add(button2, null);
this.add(panel2, BorderLayout.CENTER);
panel2.add(chat_txt, null);
panel2.add(list1, null);
this.add(panel3, BorderLayout.SOUTH);
panel3.add(label2, null);
panel3.add(msg_txt, null);
panel3.add(button3, null);
}
public boolean action(Event evt, Object obj) // 事件觸發代碼
{
if (evt.target instanceof Button) {
String label = (String) obj;
if (label.equals("連接")) // 如果點擊連接後
{
if (soc == null) {
try {
soc = new Socket(InetAddress.getLocalHost(), 2525); // 使用端口2525實例化一個本地套接字
System.out.println(soc); // 在控制檯打印實例化的結果
ps = new PrintStream(soc.getOutputStream()); // 將ps指向soc的輸出流
StringBuffer info = new StringBuffer("INFO: "); // 定義一個字符緩衝存儲發送信息
// 其中INFO爲關鍵字讓服務器識別爲連接信息
// 並將name和ip用":"分開,在服務器端將用一個
// StringTokenizer類來讀取數據
String userinfo = name_txt.getText() + ":"
+ InetAddress.getLocalHost().toString();
ps.println(info.append(userinfo));
ps.flush();
listen = new Listen(this, name_txt.getText(), soc); // 將客戶端線程實例化
listen.start(); // 啓動線程
} catch (IOException e) {
System.out.println("Error:" + e);
disconnect();
}
} // end of if
}// end of if
else if (label.equals("斷開連接")) // 如果點擊斷開連接按鈕則運行disconnect()
{
disconnect();
} else if (label.equals("發送")) // 如果點擊發送按鈕
{
if (soc != null) {
StringBuffer msg = new StringBuffer("MSG: "); // 定義並實例化一個字符緩衝存儲發送的聊天信息
// 其中MSG爲關鍵詞
try {
String msgtxt = new String(msg_txt.getText());
} catch (Exception e) {
}
ps.println(msg.append(msg_txt.getText())); // 用打印流發送聊天信息
ps.flush();
}
}
}
return true;
} // end of method action
public void disconnect() // 客戶端點擊斷開連接要運行的方法
{
if (soc != null) {
try {
listen.suspend();
ps.println("QUIT"); // 用打印流發送QUIT信息通知服務器斷開此次通信
ps.flush();
soc.close(); // 關閉套接字
} catch (IOException e) {
System.out.println("Error:" + e);
} finally {
}
}// end of if
}
class Listen extends Thread // 客戶端線程類用來監聽服務器傳來的信息
{
String name = null; // 用來存儲客戶端連接後的name信息
DataInputStream dis = null; // 用來實現客戶端接受服務器數據的輸入流
PrintStream ps = null; // 用來實現從客戶端發送數據到服務器的打印流
Socket socket = null; // 用來存儲客戶端的socket信息
ClientChat parent = null; // 用來存儲當前運行的chatApplet實例
public Listen(ClientChat p, String n, Socket s) // Listen類的構造器
{
// 接受參數
parent = p;
name = n;
socket = s;
try {
// 實例化兩個數據流
dis = new DataInputStream(s.getInputStream());
ps = new PrintStream(s.getOutputStream());
} catch (IOException e) {
System.out.println("Error:" + e);
parent.disconnect();
}
} // end of Listen constractor
public void run() // 線程運行方法
{
String msg = null;
while (true) {
try {
msg = dis.readLine();
} // 讀取從服務器傳來的信息
catch (IOException e) {
System.out.println("Error:" + e);
parent.disconnect();
}
if (msg == null) // 如果從服務器傳來的信息爲空則斷開此次連接
{
parent.listen = null;
parent.soc = null;
parent.list1.clear();
return;
}
StringTokenizer st = new StringTokenizer(msg, ":"); // 用StringTokenizer類來實現讀取分段字符
String keyword = st.nextToken(); // 讀取信息頭即關鍵字用來識別是何種信息
if (keyword.equals("PEOPLE")) // 如果是PEOPLE則是服務器發來的客戶連接信息
// 主要用來刷新客戶端的用戶列表
{
parent.list1.clear();
while (st.hasMoreTokens()) // 遍歷st取得目前所連接的客戶
{
String str = st.nextToken();
parent.list1.addItem(str);
}
} else if (keyword.equals("MSG")) // 如果關鍵字是MSG則是服務器傳來的聊天信息
// 主要用來刷新客戶端聊天信息區將每個客戶的聊天內容顯示出來
{
String usr = st.nextToken();
parent.chat_txt.appendText(usr);
parent.chat_txt.appendText(st.nextToken("\0"));
parent.chat_txt.appendText("\n\n");
} else if (keyword.equals("QUIT")) // 如果關鍵字是QUIT則是服務器關閉的信息
// 用來切斷此次連接
{
System.out.println("Quit");
try {
parent.listen.stop();
parent.listen = null;
parent.soc.close();
parent.soc = null;
} catch (IOException e) {
System.out.println("Error:" + e);
}
parent.list1.clear();
return;
}
}
} // end of run method
} // end of Listen inner class
} // end of chatApplet class
//AboutChat.java
package com.mulThread;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.border.*;
/**
*
* @E-mail:[email protected]
*
* @author:young master
*
* @version 1.0
*
* @any questions,please reply to me for deeping improvements
*/
public class AboutChat extends JDialog implements ActionListener {
JPanel panel1 = new JPanel();
JPanel panel2 = new JPanel();
JPanel insetsPanel1 = new JPanel();
JPanel insetsPanel2 = new JPanel();
JPanel insetsPanel3 = new JPanel();
JButton button1 = new JButton();
JLabel imageLabel = new JLabel();
JLabel label1 = new JLabel();
JLabel label2 = new JLabel();
JLabel label3 = new JLabel();
JLabel label4 = new JLabel();
BorderLayout borderLayout1 = new BorderLayout();
BorderLayout borderLayout2 = new BorderLayout();
FlowLayout flowLayout1 = new FlowLayout();
GridLayout gridLayout1 = new GridLayout();
String product = "product:少爺精簡版--聊天服務器端";
String author = "author:young master " +
"any questions,please reply to me for deeping improvements";
String email = "@E-mail:[email protected]";
String comments = "本聊天室服務器端實現了多線程客戶連接和顯示連接信息";
public AboutChat(Frame parent) {
super(parent);
enableEvents(AWTEvent.WINDOW_EVENT_MASK);
try {
jbInit();
} catch (Exception e) {
e.printStackTrace();
}
pack();
}
private void jbInit() throws Exception {
//imageLabel.setIcon(new ImageIcon(Frame1_AboutBox.class.getResource("[Your Image]")));
this.setTitle("少爺精簡版--聊天服務器端");
setResizable(false);
panel1.setLayout(borderLayout1);
panel2.setLayout(borderLayout2);
insetsPanel1.setLayout(flowLayout1);
insetsPanel2.setLayout(flowLayout1);
insetsPanel2.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
gridLayout1.setRows(4);
gridLayout1.setColumns(1);
label1.setText(product);
label2.setText(author);
label3.setText(email);
label4.setText(comments);
insetsPanel3.setLayout(gridLayout1);
insetsPanel3.setBorder(BorderFactory.createEmptyBorder(10, 60, 10, 10));
button1.setText("Ok");
button1.addActionListener(this);
insetsPanel2.add(imageLabel, null);
panel2.add(insetsPanel2, BorderLayout.WEST);
this.getContentPane().add(panel1, null);
insetsPanel3.add(label1, null);
insetsPanel3.add(label2, null);
insetsPanel3.add(label3, null);
insetsPanel3.add(label4, null);
panel2.add(insetsPanel3, BorderLayout.CENTER);
insetsPanel1.add(button1, null);
panel1.add(insetsPanel1, BorderLayout.SOUTH);
panel1.add(panel2, BorderLayout.NORTH);
}
/**Overridden so we can exit when window is closed*/
protected void processWindowEvent(WindowEvent e) {
if (e.getID() == WindowEvent.WINDOW_CLOSING) {
cancel();
}
super.processWindowEvent(e);
}
/**Close the dialog*/
void cancel() {
dispose();
}
/**Close the dialog on a button event*/
public void actionPerformed(ActionEvent e) {
if (e.getSource() == button1) {
cancel();
}
}
}