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>