Java小程序之服务器的UI实现

arew0625 8年前
   <p>Java小程序之服务器的UI实现</p>    <p><strong>一、前言:</strong></p>    <p>前面我们做的服务器都是没有界面的,只是利用简单的输入输出语句在控制台中打印语句;今天,我们要让服务器有界面,可视化操作;</p>    <p><strong>二、UI功能需求:</strong></p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/2b211912409eed41b7c9762cc11b284c.png"></p>    <p>三、 <strong>知识点分析</strong> <strong>:</strong></p>    <p>JTable 使用: TableModel 和 JScrollPane 、 Vector</p>    <p>JTable: 可以理解为表示数据的展示组件</p>    <p>TableModel: 用于封装数据组件</p>    <p>Vector: 队列</p>    <h2>代码框架</h2>    <p style="text-align:center"><strong><img src="https://simg.open-open.com/show/061b0263b6b55edbecfbf10e2f8579dd.png"> </strong></p>    <p><strong>四、具体实现思路</strong></p>    <p>1、先实现整个界面UI(功能暂时不着急)这里主要是JTable的使用,刚开始启动按钮和关闭按钮都不能操作</p>    <p>2、最上方功能按钮的实现,当界面初始化的时候,用一个线程开始不断检测输入框中是否有内容,有内容则启动按钮可操作;启动按钮启动后,创建服务器对象,等待客户端的连接,并将启动按钮再次设置为不可操作,而关闭按钮设置为可操作;</p>    <p>3、获取客户端输入的账户和密码,服务器校验成功后,将该账号的相关信息进行封装,将封装好的信息加到Table中显示</p>    <p>4、显示聊天功能,消息内容昨天实在控制台中打印的,这次,我们这要将从客户端接收到的消息放在服务器界面UI中显示即可;</p>    <p><strong>五、源代码:</strong></p>    <p>源代码结构图:</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/0694fe49a8768c3fd1ebb6e3062e1854.png"></p>    <p>com .huaxin.server包:</p>    <p>MyServer类:(什么时候启动服务器?当点击启动按钮时,执行者下面的代码)</p>    <pre>  <code class="language-java">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");   }  }</code></pre>    <p>ServerThread类:</p>    <pre>  <code class="language-java">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;   }  }</code></pre>    <p>com.huaxin.UI包:</p>    <p>(该包分为将整个面板的三个区域分出了三个类,在UIFrame中创建该三个类,并整合三个类,达到上图的整体效果)</p>    <p>StartPanel类:(最上面的面板类)</p>    <pre>  <code class="language-java">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();   }       }</code></pre>    <p>TablePanel类:登录用户信息显示面板</p>    <pre>  <code class="language-java">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);   }          }</code></pre>    <p>InfoPanel类:聊天信息显示面板</p>    <pre>  <code class="language-java">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();      }     }    }   };  }</code></pre>    <p>UIFrame类:程序入口以及面板整合类:</p>    <pre>  <code class="language-java">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);   }     }</code></pre>    <p>六、运行结果:</p>    <p>先启动界面UI类,输入9090点击启动服务,开启服务器,服务器一定要先打开,否则客户端无法连接</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/9a5baf769a24e56d5d5980a6dd531ee2.png"></p>    <p>利用系统自带的telnet客户端,启动客户端</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/f860145a6b080fcafd47a1aab5d4e6ee.png"></p>    <p>输入正确的密码进行登录:</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/d371916f5f03f27405a3b8f6b0e6f7ce.png"></p>    <p>当有用户登录进来后的界面显示效果:</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/a64051978720c5f1336225ed2de5df6b.png"></p>    <p>以同样的方法开启另外客户端</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/c94ee7e1fe0d39968c5a00814bb1ed54.png"></p>    <p>界面UI小时效果:</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/abf55484c941d1d9b6000725a09ee9c3.png"></p>    <p>在三个客户端分别输入聊天消息:</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/c6dc2ba80f090fdb54d5f0c5960cbd1d.png"></p>    <p>界面UI显示效果:</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/c4265302abe82bd94c1d1f60b285f696.png"></p>    <p>测试系统发送功能:(在信息面板中的发送按钮添加监听)</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/03daae7b284da45b673e9ebf4d52c51c.png"></p>    <p><img src="https://simg.open-open.com/show/0061f0d9caac8b838fe0172c7b077187.png"></p>    <p>七、总结:</p>    <p>1、不要着急,一步一步实现</p>    <p>2、服务那边的代码可以参看昨天的博客;</p>    <p>3、把界面的单个部分分成三个类出去实现,最后在一个类中创建三个类的对象,并将三个对象在该类中整合</p>    <p>4、一定按照思路来,越急躁的人越回一通乱来</p>    <p>个人经验分享:以前我也尝试照着书中的代码一句一句去敲,先把一个类的代码敲完,再去敲另一个了类的代码,其实这样是非常不好的,我们写代码的时候也不是一口气就把一个类的代码全部写完,同时,定义的变量也不是一下就能想到要那么多的变量,只是在我们需要的时候去定义某个变量;所以,写程序,思路真的非常非常重要;我把源代码都贴上来,也不是让某些人直接copy拿去交作业(当然,有部分人肯定是直接copy的),更重要的是帮助那些自学的朋友在某个地方卡壳的时候,参看一下的代码,能顺时顿悟;而浪费太多不必要的时间;最最重要的是能和大家一起学习讨论和交流;嗯,相信我的意思大家都明白!</p>    <p> </p>    <p>来自:http://blog.csdn.net/bluesky_usc/article/details/54353768</p>    <p> </p>