Java小程序之服務器的UI實現

arew0625 7年前發布 | 18K 次閱讀 Java Java開發

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

 

 本文由用戶 arew0625 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!