java吧 关注:1,239,197贴子:12,710,277

【JAVASE】从“零”开始做局域网聊天软件

取消只看楼主收藏回复

一楼送给度娘


IP属地:辽宁1楼2015-10-13 18:41回复
    聊天软件要分成两个部分
    一、服务器部分
    二、客户端部分


    IP属地:辽宁2楼2015-10-13 18:41
    回复
      第一步先确定服务器部分的样式。


      IP属地:辽宁3楼2015-10-13 18:42
      回复
        我设计的比较丑,下面来看代码实现
        一、首先建立一个common类(com.common.FrameVariable.java)用来保存窗体使用的对象信息
        package com.common;
        import java.awt.BorderLayout;
        import java.awt.GridLayout;
        import java.awt.event.ActionListener;
        import javax.swing.JButton;
        import javax.swing.JFrame;
        import javax.swing.JPanel;
        import javax.swing.JScrollPane;
        /**
        * 窗体公共变量(单例)
        * @author q群 484994461
        */
        public class FrameVariable {
        //单例的标准写法start
        private static FrameVariable instance = null;
          private FrameVariable(){}
        public static FrameVariable getInstance(){
            if(instance == null)
        instance = new FrameVariable();
            return instance;
        }
        //单例的标准写法end
          //容器部分
        //server窗体
        public JFrame serverFrame = null;
        //窗体内最顶层容器
        public JPanel serverPanel = null;
        //窗体内BorderLayout north部分容器
        public JPanel northPanel = null;
        //窗体内BorderLayout center部分容器
        public JPanel centerPanel = null;
        //窗体内BorderLayout Center滚动条
        public JScrollPane centerScroll = null;
        //布局部分
        //JFrame窗体layout
        public GridLayout frameLayout = null;
          //顶层容器layout
        public BorderLayout panelLayout = null;
          //north部分容器layout
        public GridLayout northLayout = null;
          //center部分容器layout
        public GridLayout centerLayout = null;
        //控件部分
        //开始/关闭按钮
        public JButton startButton = null;
        //踢人按钮
        public JButton knickButton = null;
          //监听部分
        public ActionListener listen = null;
        }          


        IP属地:辽宁4楼2015-10-13 18:43
        回复
          二、common类建立好后就来建立一个窗体程序(com.window.ServerWindow.java)用来实例和拼装窗体
          package com.window;
          import java.awt.BorderLayout;
          import java.awt.Color;
          import java.awt.GridLayout;
          import javax.swing.JButton;
          import javax.swing.JFrame;
          import javax.swing.JPanel;
          import javax.swing.JScrollPane;
          import com.common.FrameVariable;
          /**
          * 实现windows窗体类
          * @author q群 484994461
          */
          public class ServerWindow {
            //单例的标准写法start
            private FrameVariable variable;
            private static ServerWindow instance = null;
            public static ServerWindow getInstance(){
              if(instance == null)
                instance = new ServerWindow();
              return instance;
            }
          //单例的标准写法end
          //私有化成员函数(单例用)
            private ServerWindow(){
              variable = FrameVariable.getInstance();
              //初始化各个布局
              initLayout();
              //初始化窗体
              initJFrame();
              //初始化顶层容器
              initJPanel();
              //初始化North部分
              initNorth();
              //初始化Center部分
              initCenter();
              //组装各个零件
              instComponent();
            }
          /**
          * 初始化布局
          */
          private void initLayout(){
              //实例GridLayout会把容器平均分成纵向y份横向x份(y,x)
              variable.frameLayout = new GridLayout(1,1);
              //实例BorderLayout会把容器分成上(north),下(south),左(west),右(east),中间(center)5部分
              variable.panelLayout = new BorderLayout();
              //实例Center部分Layout
              variable.centerLayout = new GridLayout();
              //实例North部分Layout 1行2列
              variable.northLayout = new GridLayout(1,2);
              //North部分Layout各个部分水平距离20px
              variable.northLayout.setHgap(20);
            }
          /**
          * 初始化IFrame窗体
            */
          private void initJFrame(){
              //新建一个IFrame窗体
              variable.serverFrame = new JFrame("server stopped");
              //设置窗体左上角位置距离左侧100px,距离上方200px
              variable.serverFrame.setLocation(100,200);
              //设置窗体大小宽250px,高400px
              variable.serverFrame.setSize(250, 400);
              //设置IFrame顶层窗体布局为横向1份纵向1份
              variable.serverFrame.setLayout(variable.frameLayout);
            }
          /**
          * 初始化顶层容器
          */
          private void initJPanel(){
              //实例JPanel容器 并放置于IFrame顶层
              variable.serverPanel = new JPanel();
              variable.serverFrame.add(variable.serverPanel);
              //JPanel容器设置borderLayout
              variable.serverPanel.setLayout(variable.panelLayout);
            }
          /**
          * 初始化center部分
            */
          private void initCenter(){
              //实例Center部分JPanel容器
              variable.centerPanel = new JPanel();
              //实例Center部分滚动条 把JPanel容器加入进来
              variable.centerScroll = new JScrollPane(variable.centerPanel);
              variable.centerPanel.setLayout(variable.centerLayout);
            }
          /**
          * 初始化north部分
          */
          private void initNorth(){
              //实例North部分JPanel容器
              variable.northPanel = new JPanel();
              //设置布局 1行2列横向距离20px
              variable.northPanel.setLayout(variable.northLayout);
              //实例startButton
              variable.startButton = new JButton("start server");
              variable.startButton.setBackground(Color.PINK);
              //实例knickButton
              variable.knickButton = new JButton("knick");
              variable.knickButton.setBackground(Color.RED);
            }
          /**
          * 组装各个零件
          * 有小到大组装
          */
          private void instComponent(){
              //north部分
              //GridLayout 第1行第1格
              variable.northPanel.add(variable.startButton);
              //GridLayout 第1行第2格
              variable.northPanel.add(variable.knickButton);
          //顶层
          //把JScrollPane加入到顶层JPanel中并设置为center
              variable.serverPanel.add(variable.centerScroll,BorderLayout.CENTER);
              //把northPane加入到顶层JPanel并设置north
              variable.serverPanel.add(variable.northPanel,BorderLayout.NORTH);
          //窗体
          //把顶层JPanel加入到窗体中
              variable.serverFrame.add(variable.serverPanel);
            }
          /**
          * 显示窗体方法
          */
          public void show(){
              //设置默认关闭方法
              variable.serverFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
              //设置窗体可见
              variable.serverFrame.setVisible(true);
            }
          }      


          IP属地:辽宁5楼2015-10-13 18:45
          回复
            三、写一个控制类(com.control.Start.java)启动这个窗体
            package com.control;
            import com.window.ServerWindow;
            /**
            * 启动类
            * @author Q群 484994461
            *
            */
            public class Start {
              /**
            * 入口方法
              */
            public static void main(String[] args) {
                //实例ServerWindow
                ServerWindow sw = ServerWindow.getInstance();
                //调用显示方法
                sw.show();
              }
            }    


            IP属地:辽宁6楼2015-10-13 18:48
            回复


              IP属地:辽宁8楼2015-10-13 23:53
              回复
                额 到此为止 服务器窗体部分就算做完了
                接下来就是增加功能了
                这个以后继续更新这个帖子 继续做


                IP属地:辽宁9楼2015-10-14 00:54
                回复
                  五、写一个common工具类(com.common.Tool)用来编写各种方便易用的静态方法
                  package com.common;
                  import java.io.ByteArrayInputStream;
                  import java.io.IOException;
                  import java.io.ObjectInputStream;
                  import java.net.DatagramPacket;
                  import com.obj.MessageObject;
                  /**
                  * 工具类
                  * @author Q群 484994461
                  */
                  public class Tool {
                    //默认编码方式utf-8
                    public static final String DefaultCharSet = "UTF-8";
                  //虾米Mickey提供byte[]转int tool
                    public static final int bytesToint(byte[] bytes){
                      return (bytes[0] & 0xFF) << 24 | (bytes[1] & 0xFF) << 16 | (bytes[2] & 0xFF) << 8| bytes[3] & 0xFF;
                    }
                  //虾米Mickey提供int转byte[] tool
                    public static final byte[] intTobytes(int i){
                      return new byte[]{(byte) (i>>24),(byte) (i>>16),(byte) (i>>8),(byte) i};
                    }
                  //获取信息编码
                  public static final MessageObject getMessageObject(DatagramPacket datagramPacket) throws IOException{
                      try{
                  datagramPacket.getAddress().getHostAddress();
                        ObjectInputStream objectInputStream = new ObjectInputStream(new ByteArrayInputStream(datagramPacket.getData()));
                        MessageObject messageObject = (MessageObject)objectInputStream.readObject();
                        return messageObject;
                      }catch(IOException e){
                        throw new IOException("转换MessageObject对象异常:" + e.getMessage());
                      } catch (ClassNotFoundException e) {
                        throw new IOException("强制转换MessageObject对象异常:" + e.getMessage());
                      }
                  }
                  }      


                  IP属地:辽宁23楼2015-10-22 15:46
                  回复
                    七、写两个类继承runnable接口(com.control.run.ServerReceiveRun.java/com.control.run.ServerSendRun.java)用来多线程处理数据发送和数据接收


                    IP属地:辽宁25楼2015-10-22 15:49
                    回复
                      package com.control.run;
                      import java.io.IOException;
                      import java.net.DatagramPacket;
                      import com.action.ServerAction;
                      import com.common.Tool;
                      import com.obj.MessageObject;
                      /**
                      * 接收数据处理线程
                      * @author q群 484994461
                      */
                      public class ServerReceiveRun implements Runnable{
                      //接收到的udp包
                      private DatagramPacket datagramPacket;
                        //处理udp包
                      private ServerAction serverAction;
                        //消息对象
                      private MessageObject messageObject;
                        //成员函数
                      public ServerReceiveRun(DatagramPacket datagramPacket){
                          this.serverAction = ServerAction.getInstance();
                          this.datagramPacket = datagramPacket;
                        };
                      @Override
                      public void run() {
                      try {
                      messageObject = Tool.getMessageObject(datagramPacket);
                            switch(messageObject.type){
                            //登陆
                      case 1:
                      //客户端别名
                              serverAction.onLoad(messageObject,datagramPacket);
                              break;
                            //退出
                      case 2:
                      serverAction.onExit(messageObject);
                              break;
                            //获取在线信息
                            case 3:
                      serverAction.getUserList(messageObject);
                              break;
                            //转发消息
                            case 4:
                      serverAction.redirectMsg(messageObject);
                              break;
                            //转发群发消息
                            case 5:
                      serverAction.redirectMsgAll(messageObject);
                              break;
                            //ping
                      case 999:
                              serverAction.ping(messageObject);
                              break;
                            //非正常码
                            default:
                      serverAction.error(messageObject);
                              break;
                            }
                      } catch (IOException e) {
                            // TODO Auto-generated catch block
                            e.printStackTrace();
                          }
                      }
                      }        


                      IP属地:辽宁26楼2015-10-22 15:50
                      回复
                        package com.control.run;
                        import java.io.IOException;
                        import java.net.DatagramPacket;
                        import javax.swing.JOptionPane;
                        import com.common.LogicVariable;
                        /**
                        * 发送数据处理线程
                        * @author q群 484994461
                        */
                        public class ServerSendRun implements Runnable{
                          //逻辑对象
                        LogicVariable logicVariable;
                          //要发送的udp包
                        private DatagramPacket datagramPacket;
                        public ServerSendRun(DatagramPacket datagramPacket){
                            logicVariable = LogicVariable.getInstance();
                            this.datagramPacket = datagramPacket;
                          }
                          @Override
                        public void run() {
                            try {
                        logicVariable.serverSocket.send(datagramPacket);
                            } catch (IOException e) {
                              JOptionPane.showMessageDialog(null, "发送消息失败!");
                              System.exit(0);
                            }
                        }
                        }      


                        IP属地:辽宁27楼2015-10-22 15:52
                        回复
                          八、编写一个可序列化的消息类(com.obj.MessageObject)实现Serializable接口,实现Serializable接口既可以进行序列化
                          package com.obj;
                          import java.io.Serializable;
                          /**
                          * 消息对象
                          * @author q群 484994461
                          */
                          public class MessageObject implements Serializable {
                          /**
                          * 版本id
                          */
                          private static final long serialVersionUID = 8417965006582491082L;
                          //消息类型
                          public int type;
                          //客户端id
                          public int currentId;
                          //转发客户端id
                          public int intentId;
                          //用户名
                          public String name;
                          //正文
                          public String msg;
                          }


                          IP属地:辽宁31楼2015-10-22 16:20
                          回复
                            十一、编写ClientObject(com.obj.ClientObject)
                            package com.obj;
                            import java.net.InetAddress;
                            /**
                            * 客户端信息
                            * @author q群 484994461
                            */
                            public class ClientObject {
                              //id
                            private Integer id;
                              //名称
                            private String name;
                              //InetAddress对象
                              private InetAddress inetAddress;
                              //客户端打开端口
                              private int port;
                              //获取客户端ip地址或者域名
                              public String getAddress() {
                                return inetAddress.getHostAddress();
                              }
                            //获取客户端通信端口
                              public int getPort() {
                                return port;
                              }
                            //返回InetAddress对象
                              public InetAddress getInetAddress() {
                                return inetAddress;
                              }
                            //设置InetAddress对象
                              public void setInetAddress(InetAddress inetAddress) {
                                this.inetAddress = inetAddress;
                              }
                            public Integer getId() {
                                return id;
                              }
                            public void setId(Integer id) {
                                this.id = id;
                              }
                            public String getName() {
                                return name;
                              }
                            public void setName(String name) {
                                this.name = name;
                              }
                            public void setPort(int port) {
                                this.port = port;
                              }
                            }    


                            IP属地:辽宁35楼2015-10-22 18:49
                            回复
                              编写一个测试类模拟客户端登陆
                              package com.control;
                              import java.io.ByteArrayInputStream;
                              import java.io.ByteArrayOutputStream;
                              import java.io.IOException;
                              import java.io.ObjectInputStream;
                              import java.io.ObjectOutputStream;
                              import java.io.UnsupportedEncodingException;
                              import java.net.DatagramPacket;
                              import java.net.DatagramSocket;
                              import java.net.InetSocketAddress;
                              import com.obj.MessageObject;
                              public class Start {
                              public static void main(String[] args) {
                              // TODO Auto-generated method stub
                              MessageObject messageObject = new MessageObject();
                              try {
                              messageObject.name=new String("test".getBytes("utf-8"),"utf-8");
                              messageObject.msg = new String("test onload".getBytes("utf-8"),"utf-8");
                              } catch (UnsupportedEncodingException e) {
                              // TODO Auto-generated catch block
                              e.printStackTrace();
                              }
                              messageObject.type = 1;
                              ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
                              try {
                              ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
                              objectOutputStream.writeObject(messageObject);
                              objectOutputStream.flush();
                              objectOutputStream.close();
                              DatagramPacket datagramPacket = new DatagramPacket(byteArrayOutputStream.toByteArray(),byteArrayOutputStream.size(),new InetSocketAddress("127.0.0.1", 8888));
                              DatagramSocket datagramSocket = new DatagramSocket(10000);
                              datagramSocket.send(datagramPacket);
                              datagramPacket = new DatagramPacket(new byte[1024], 1024);
                              datagramSocket.receive(datagramPacket);
                              System.out.println(datagramPacket.getAddress().getHostAddress());
                              System.out.println(datagramPacket.getPort());
                              ByteArrayInputStream ByteArrayInputStream = new ByteArrayInputStream(datagramPacket.getData());
                              ObjectInputStream objectInputStream = new ObjectInputStream(ByteArrayInputStream);
                              messageObject = (MessageObject)objectInputStream.readObject();
                              System.out.println(messageObject.currentId);
                              System.out.println(messageObject.name);
                              } catch (IOException e) {
                              // TODO Auto-generated catch block
                              e.printStackTrace();
                              } catch (ClassNotFoundException e) {
                              // TODO Auto-generated catch block
                              e.printStackTrace();
                              }
                              }
                              }


                              IP属地:辽宁36楼2015-10-23 09:28
                              回复