Java小程序之服務器的UI實現
Java小程序之服務器的UI實現
一、前言:
前面我們做的服務器都是沒有界面的,只是利用簡單的輸入輸出語句在控制臺中打印語句;今天,我們要讓服務器有界面,可視化操作;
二、UI功能需求:
三、 知識點分析 :
JTable 使用: TableModel 和 JScrollPane 、 Vector
JTable: 可以理解為表示數據的展示組件
TableModel: 用于封裝數據組件
Vector: 隊列
代碼框架
四、具體實現思路
1、先實現整個界面UI(功能暫時不著急)這里主要是JTable的使用,剛開始啟動按鈕和關閉按鈕都不能操作
2、最上方功能按鈕的實現,當界面初始化的時候,用一個線程開始不斷檢測輸入框中是否有內容,有內容則啟動按鈕可操作;啟動按鈕啟動后,創建服務器對象,等待客戶端的連接,并將啟動按鈕再次設置為不可操作,而關閉按鈕設置為可操作;
3、獲取客戶端輸入的賬戶和密碼,服務器校驗成功后,將該賬號的相關信息進行封裝,將封裝好的信息加到Table中顯示
4、顯示聊天功能,消息內容昨天實在控制臺中打印的,這次,我們這要將從客戶端接收到的消息放在服務器界面UI中顯示即可;
五、源代碼:
源代碼結構圖:
com .huaxin.server包:
MyServer類:(什么時候啟動服務器?當點擊啟動按鈕時,執行者下面的代碼)
package com.huaxin.server;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.BindException;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;
import java.util.ArrayList;
import javax.swing.JOptionPane;
import com.huaxin.UI.InfoPanel;
import com.huaxin.UI.TablePanel;
public class MyServer {
public static ArrayList<ServerThread> serverList = new ArrayList<ServerThread>();
public int port;
public ServerSocket server;
private TablePanel tablePanel;
private InfoPanel infoPanel;
public MyServer(int port, TablePanel tablePanel, InfoPanel infoPanel) {
this.port=port;
this.tablePanel=tablePanel;
this.infoPanel=infoPanel;
}
//啟動服務器
public void startServer(){
try {
//創建服務器對象
server = new ServerSocket(port);
System.out.println("服務器已經啟動......");
while(true){
//服務器連接客戶端,阻塞方法
Socket socket=server.accept();
System.out.println("有客戶端連進來了......");
ServerThread st = new ServerThread(socket,tablePanel,infoPanel);
st.start();
serverList.add(st);
}
}
catch (BindException e) {
JOptionPane.showMessageDialog(null, "端口號已經占用!請重新啟動服務器并輸入新的端口號!");
System.exit(0);
}
catch (SocketException e) {
JOptionPane.showMessageDialog(null, "服務器已關閉.....");
}
catch (Exception e) {
e.printStackTrace();
}
}
public void sendMsgToClient(String s) {
for (int i = 0; i <serverList.size(); i++) {
//群發系統消息
ServerThread st = MyServer.serverList.get(i);
//獲取每個客戶端的輸出流
OutputStream ous;
try {
ous = st.socket.getOutputStream();
st.sendMsg(ous,"The System send a msg :"+s);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
infoPanel.jta.append("System Msg :"+s+"\r\n");
}
}
ServerThread類:
package com.huaxin.server;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Vector;
import com.huaxin.UI.InfoPanel;
import com.huaxin.UI.TablePanel;
import javafx.scene.control.TabPane;
public class ServerThread extends Thread {
public Socket socket;
public InputStream ins;
public OutputStream os;
public String str;
public String name;
public String pwd;
private TablePanel tablePanel;
private InfoPanel infoPanel;
public ServerThread(Socket socket, TablePanel tablePanel, InfoPanel infoPanel) {
this.socket = socket;
this.tablePanel=tablePanel;
this.infoPanel=infoPanel;
}
public void run() {
Vector<String> vs=null;
try {
// 獲取輸入輸出流
ins = socket.getInputStream();
os = socket.getOutputStream();
str = "welcome to zhou's Server";
// 向客戶端輸出信息
sendMsg(os, str);
os.flush();
/************客戶端登錄以及驗證****************/
//獲取客戶端輸入的賬戶和密碼
getMsg();
//賬號和密碼校驗
boolean falg =loginCheck();
//校驗不通過時,循環校驗
while(!falg){
str="Fail to connect server......";
sendMsg(os, str);
os.flush();
str="please check your name and password and login again.....";
sendMsg(os, str);
os.flush();
getMsg();
falg =loginCheck();
}
//校驗成功后:開始聊天
str="successful connected..... you can chat with your friends now ......";
// str="yes";
sendMsg(os, str);
os.flush();
//登錄成功后,將用戶名,ip地址以及登錄時間加到TabelPanel中的Table中
//先獲取到TabelPanel中的Table對象,一層一層傳遞過來
String ip =socket.getRemoteSocketAddress().toString();
Date date = new Date();
SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String currentTime =sdf.format(date);
//封裝用戶名,ip地址以及時間
vs = new Vector<String>();
vs.add(name);
vs.add(ip);
vs.add(currentTime);
//將封裝好的vs加入到tablePanel面板的table中,并刷新面板
tablePanel.data.add(vs);
tablePanel.table.updateUI();
// 接受客戶端的信息
String s = readMsg(ins);
while (!s.equals("bye")) {
//在消息框中顯示發送的信息
infoPanel.jta.append(name+" say:"+s+"\r\n");
for (int i = 0; i < MyServer.serverList.size(); i++) {
//群發消息
ServerThread st = MyServer.serverList.get(i);
//不給自己發消息
if (st == this) continue;
OutputStream ous =st.socket.getOutputStream();
sendMsg(ous,name+" say:"+s);
os.flush();
}
//循環讀取信息
s = readMsg(ins);
}
// server.close();
} catch (Exception e) {
// e.printStackTrace();
System.out.println("客戶端不正常退出......");
}
//關閉客戶端后,將該用戶的記錄從向量隊里中移除,并刷新面板
if(vs!=null){
this.tablePanel.data.remove(vs);
this.tablePanel.table.updateUI();
}
// 關閉流、服務器、套接字
try {
ins.close();
os.close();
socket.close();
MyServer.serverList.remove(this);
} catch (IOException e) {
// System.out.println("這個地方有錯誤......");
e.printStackTrace();
}
}
//發送消息的函數
public void sendMsg(OutputStream os, String s) throws IOException {
// 向客戶端輸出信息
//
byte[] bytes = s.getBytes();
os.write(bytes);
os.write(13);
os.write(10);
os.flush();
}
//讀取客戶端輸入數據的函數
public String readMsg(InputStream ins) throws Exception {
// 讀取客戶端的信息
int value = ins.read();
// 讀取整行 讀取到回車(13)換行(10)時停止讀
String str = "";
while (value != 10) {
//點擊關閉客戶端時會返回-1值
if(value ==-1){
throw new Exception();
}
str = str + ((char) value);
value = ins.read();
}
str = str.trim();
return str;
}
//獲取客戶端賬號和密碼的函數
public void getMsg() throws Exception {
str="please enter your name :";
sendMsg(os, str);
os.flush();
name =readMsg(ins);
str="please enter your password :";
sendMsg(os, str);
os.flush();
pwd =readMsg(ins);
}
//校驗客戶端輸入的賬號和密碼的函數
public boolean loginCheck() throws Exception{
if(name.equals("zhou") && pwd.equals("zhou")
|| name.equals("user") && pwd.equals("pwd")
|| name.equals("huaxinjiaoyu") && pwd.equals("huaxinjiaoyu")){
return true;
}
return false;
}
}
com.huaxin.UI包:
(該包分為將整個面板的三個區域分出了三個類,在UIFrame中創建該三個類,并整合三個類,達到上圖的整體效果)
StartPanel類:(最上面的面板類)
package com.huaxin.UI;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.IOException;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;
import com.huaxin.server.MyServer;
import com.huaxin.server.ServerThread;
public class StartPanel extends JPanel{
public JTextField jtf;
public JButton startBtn;
public JButton stoptBtn;
public boolean flag=true;
public MyServer ms ;
public TablePanel tablePanel;
public InfoPanel infoPanel;
/*
* 最上面的面板類
*/
public StartPanel(TablePanel tablePanel, InfoPanel infoPanel) {
this.tablePanel=tablePanel;
this.infoPanel=infoPanel;
//創建相關組件
JLabel label =new JLabel("端口號");
jtf = new JTextField(10);
startBtn = new JButton("啟動服務");
//設置按鈕不可操作
startBtn.setEnabled(false);
stoptBtn = new JButton("關閉服務");
stoptBtn.setEnabled(false);
//設置背景顏色
this.setBackground(Color.green);
//設置面板大小
this.setPreferredSize(new Dimension(0,50));
this.setLayout(new FlowLayout());
//組件添加
this.add(label);
this.add(jtf);
this.add(startBtn);
this.add(stoptBtn);
//給兩個按鈕添加監聽器
startBtn.addActionListener(al);
stoptBtn.addActionListener(al);
//檢測輸入框是否為空
checkText();
}
//按鈕監聽器的具體實現
ActionListener al = new ActionListener() {
public void actionPerformed(ActionEvent e) {
String command =e.getActionCommand();
if(command.equals("啟動服務")){
//啟動服務的相關邏輯操作
flag=false;
int port =Integer.parseInt(jtf.getText());
ms = new MyServer(port,tablePanel,infoPanel);
infoPanel.ms=ms;
stoptBtn.setEnabled(true);
startBtn.setEnabled(false);
//用線程處理,避免阻塞
new Thread(){
public void run() {
ms.startServer();
};
}.start();
}else if(command.equals("關閉服務")){
try {
// flag=true;
//關閉服務邏輯實現
//移除所有的客戶端
while(ms.serverList.size()!=0) {
ms.serverList.get(0).socket.close();
ms.serverList.remove(0);
}
//服務器關閉
ms.server.close();
//設置按鈕可操作屬性
stoptBtn.setEnabled(false);
startBtn.setEnabled(true);
} catch (IOException e1) {
e1.printStackTrace();
}
}
}
};
//線程檢測文本輸入框是否有輸入
public void checkText(){
new Thread(){
public void run() {
while(flag){
//獲取文本輸入框的信息
String info=jtf.getText();
//信息不為空或者"",則可以啟動服務器
if(!(info==null || info.equals(""))){
startBtn.setEnabled(true);
}
try {
this.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
}.start();
}
}
TablePanel類:登錄用戶信息顯示面板
package com.huaxin.UI;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.util.Vector;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.table.DefaultTableModel;
public class TablePanel extends JPanel {
public JTable table;
public Vector<Vector<String>> data;
public TablePanel() {
//設置面板屬性
this.setPreferredSize(new Dimension(450,0));
this.setBackground(Color.white);
this.setLayout(new BorderLayout());
//創建表格
Vector<String> colNames = new Vector<String>();
colNames.add("用戶名");
colNames.add("IP地址");
colNames.add("登錄時間");
data =new Vector<Vector<String>>();
Vector<String> vs=new Vector<String>();
//創建表格模型
DefaultTableModel tm =new DefaultTableModel(data, colNames);
table = new JTable(tm);
//創建滾動條
JScrollPane jsp =new JScrollPane(table);
this.add(jsp);
}
}
InfoPanel類:聊天信息顯示面板
package com.huaxin.UI;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import com.huaxin.server.MyServer;
public class InfoPanel extends JPanel{
public MyServer ms ;
public JTextArea jta;
public JTextField jtf;
public InfoPanel() {
//設置信息面板的屬性
this.setPreferredSize(new Dimension(350,0));
this.setBackground(Color.blue);
this.setLayout(new BorderLayout());
//文本輸入域
jta = new JTextArea();
//設置自動換行
jta.setLineWrap(true);
JScrollPane jsp = new JScrollPane(jta);
this.add(jsp);
//面板底部的發送面板
jtf = new JTextField(25);
JButton btn = new JButton("發送");
//發送按鈕添加監聽器
btn.addActionListener(al);
JPanel panel=new JPanel();
panel.setLayout(new FlowLayout());
this.add(panel,BorderLayout.SOUTH);
panel.add(jtf);
panel.add(btn);
}
//監聽具體實現
ActionListener al = new ActionListener() {
public void actionPerformed(ActionEvent e) {
//獲取內容
String s=jtf.getText();
//為空的提示信息
if( s==null || "".equals(s)){
JOptionPane.showMessageDialog(null, "輸入框不能為空!");
}
else if(ms.server.isClosed()){
JOptionPane.showMessageDialog(null, "服務器已經關閉,不能發送消息!");
}else{
try {
//不為空,則將消息發送給服務器,服務器轉發給消息給每個客戶端
ms.sendMsgToClient(s);
//清空文本
jtf.setText("");
} catch (Exception e1) {
e1.printStackTrace();
}
}
}
};
}
UIFrame類:程序入口以及面板整合類:
package com.huaxin.UI;
import java.awt.BorderLayout;
import javax.swing.JFrame;
/*
* 服務器后臺界面類
*/
public class UIFrame extends JFrame {
public TablePanel tablePanel;
public InfoPanel infoPanel ;
public static void main(String[] args) {
UIFrame ui = new UIFrame();
ui.initFrame();
}
public void initFrame() {
// 設置窗體的相關屬性
this.setSize(800, 600);
this.setTitle("服務器后臺界面");
this.setDefaultCloseOperation(3);
this.setLocationRelativeTo(null);
this.setLayout(new BorderLayout());
tablePanel = new TablePanel();
infoPanel = new InfoPanel();
StartPanel startPanel = new StartPanel(tablePanel,infoPanel);
//在主頁面中添加三個面板
this.add(startPanel,BorderLayout.NORTH);
this.add(tablePanel, BorderLayout.CENTER);
this.add(infoPanel, BorderLayout.EAST);
this.setVisible(true);
}
}
六、運行結果:
先啟動界面UI類,輸入9090點擊啟動服務,開啟服務器,服務器一定要先打開,否則客戶端無法連接
利用系統自帶的telnet客戶端,啟動客戶端
輸入正確的密碼進行登錄:
當有用戶登錄進來后的界面顯示效果:
以同樣的方法開啟另外客戶端
界面UI小時效果:
在三個客戶端分別輸入聊天消息:
界面UI顯示效果:
測試系統發送功能:(在信息面板中的發送按鈕添加監聽)
七、總結:
1、不要著急,一步一步實現
2、服務那邊的代碼可以參看昨天的博客;
3、把界面的單個部分分成三個類出去實現,最后在一個類中創建三個類的對象,并將三個對象在該類中整合
4、一定按照思路來,越急躁的人越回一通亂來
個人經驗分享:以前我也嘗試照著書中的代碼一句一句去敲,先把一個類的代碼敲完,再去敲另一個了類的代碼,其實這樣是非常不好的,我們寫代碼的時候也不是一口氣就把一個類的代碼全部寫完,同時,定義的變量也不是一下就能想到要那么多的變量,只是在我們需要的時候去定義某個變量;所以,寫程序,思路真的非常非常重要;我把源代碼都貼上來,也不是讓某些人直接copy拿去交作業(當然,有部分人肯定是直接copy的),更重要的是幫助那些自學的朋友在某個地方卡殼的時候,參看一下的代碼,能順時頓悟;而浪費太多不必要的時間;最最重要的是能和大家一起學習討論和交流;嗯,相信我的意思大家都明白!
來自:http://blog.csdn.net/bluesky_usc/article/details/54353768