简单的Socket消息转发实现

简单的Socket消息转发实现

闲来无事,就琢磨了一下socket;本节将实现一个简单的socket消息分发机制.功能比较简单,不喜勿喷.

现在成熟的消息分发有XMPP,MQTT等.分发协议参考JMS.
个人对MQTT比较熟悉.但是比较麻烦,用硬件设备实协议又要重新弄.所以就写个简单的搞下.

1.线程模型设计

程序线程模型如下图所示:

主线程分别开启子线程监听端口(这里只考虑同时连接一个设备和手机):

    public static Executor executor;

    public static void main(String[] args) throws IOException {
        executor = Executors.newFixedThreadPool(4);
        executor.execute(new PhoneServer());
        executor.execute(new DeviceServer());
        while(true);
    }

这里采用了线程池执行任务,没测性能,不知道具体情况.请自行测试或修改为创建线程.

当有socket连接到服务端时开启新的任务去维护输出流.当前线程则一直读取输入流数据.

public class PhoneServer implements Runnable {
        ServerSocket phoneServer;
        Socket phone;

        public PhoneServer() throws IOException {
            super();
            phoneServer = new ServerSocket(10001);
        }

        public void run() {
            phone = phoneServer.accept();
            in = new BufferedReader(new InputStreamReader(
                        phone.getInputStream()));

            out = new PrintWriter(phone.getOutputStream());
            o = new PhoneOut(out);
            MainThread.executor.execute(o);
            .....
        }

    }

2.消息分发

1.首先创建一个消息队列存放消息数据,这里为了方便就没有设计得更具体了.简单的搞一下

public class MessageEqueue extends Observable {
            private List mPhoneMessage;
            private List mDeviceMessage;

            private static MessageEqueue instance;

            public static MessageEqueue getInstance() {

                if (instance == null) {
                    instance = new MessageEqueue();
                }
                return instance;
            }

            /**
             * 
             */
            private MessageEqueue() {
                mPhoneMessage = new ArrayList();
                mDeviceMessage = new ArrayList();
            }
            ......
        }
创建两个list分别存放两个客户端发来的消息,如果想设计得更好请封装成消息对象.

2.观察者模式.让消息序列继承Observable类,当有新消息添加时,通知观察者更新输出数据.创建一个ObserverAction类封装更新信息:

public class ObserverAction {

        public static final int ACTION_PHONE_UPDATE = 1;
        public static final int ACTION_DEVICE_UPDATE = 2;
        private int action;

        private String msg ;


        /**
         * @param action
         * @param msg
         */
        public ObserverAction(int action, String msg) {
            super();
            this.action = action;
            this.msg = msg;
        }

        /**
         * @return the msg
         */
        public String getMsg() {
            return msg;
        }

        /**
         * @param msg the msg to set
         */
        public void setMsg(String msg) {
            this.msg = msg;
        }

        /**
         * @param action
         */
        public ObserverAction(int action) {
            super();
            this.action = action;
        }

        /**
         * @return the action
         */
        public int getAction() {
            return action;
        }

        /**
         * @param action
         *            the action to set
         */
        public void setAction(int action) {
            this.action = action;
        }

        /* (non-Javadoc)
         * @see java.lang.Object#toString()
         */
        public String toString() {
            return "ObserverAction [action=" + action + ", msg=" + msg + "]";
        }



        }

添加新消息时,通过notifyObservers()方法通知观察者.

    public synchronized void addDeviceMessage(String msg) {
        mDeviceMessage.add(msg);
        setChanged();
        notifyObservers(new ObserverAction(ObserverAction.ACTION_DEVICE_UPDATE,
                msg));
    }

此处创建两个ACTION标记,来区分数据更新对象

public static final int ACTION_PHONE_UPDATE = 1;
public static final int ACTION_DEVICE_UPDATE = 2;

3.读取消息

创建一个死循环来不断的接收客户端的消息,收到消息后添加到消息队列中,由输出任务向另一个客户端输出数据.

o = new PhoneOut(out);
    MainThread.executor.execute(o);
    MessageEqueue.getInstance().addObserver(o);
    while (true) {
        try {
            String str = in.readLine();
            if (str != null) {
                if (str.equals("end"))
                    break;
                MessageEqueue.getInstance().addDeviceMessage(str);
            }
        } catch (IOException e) {
            System.out.println(e.getMessage());
            break;
        }
    }

4.输出消息.
前面提到创建一个新任务维护输出流,该任务没有消息时处于wait状态,当新消息到来时被唤醒.

public class PhoneOut implements Observer, Runnable {

        private PrintWriter out;
        private boolean isFinish = false;

        private Object wait = new Object();

        /**
         * @param out
         */
        public PhoneOut(PrintWriter out) {
            super();
            this.out = out;
        }

        public void run() {
            while (!isFinish) {
                try {
                    synchronized (wait) {
                        String msg = null;
                        while ((msg = MessageEqueue.getInstance()
                                .getPhoneLastMessage()) != null) {
                            out.println("device:" + msg);
                            out.flush();
                        }
                        wait.wait();
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println("phone--end");
        }

        /**
         * @return the isFinish
         */
        public boolean isFinish() {
            return isFinish;
        }

        /**
         * @param isFinish
         *            the isFinish to set
         */
        public void setFinish(boolean isFinish) {
            this.isFinish = isFinish;
            synchronized (wait) {
                wait.notify();
            }
        }

        public void update(Observable o, Object action) {
            ObserverAction a = (ObserverAction) action;

            if (a.getAction() == ObserverAction.ACTION_PHONE_UPDATE) {
                // out.println("device:" + a.getMsg());
                // out.flush();
                synchronized (wait) {
                    wait.notify();
                }

            }
            // if()
        }
    }

观察者更新时会调用update(Observable o, Object action)方法来更新观察者的状态.

synchronized (wait) {
        String msg = null;
        while ((msg = MessageEqueue.getInstance()
                .getPhoneLastMessage()) != null) {
            out.println("device:" + msg);
            out.flush();
        }
        wait.wait();
    }

当消息队列中的消息被发送完之后,任务会进入wait状态,直到下一次被新消息唤醒.

总结

在监听任务中,为了重复监听,在run()方法中添加一个死循环来实现接受下一次的socket连接.

while (true) {
        phone = phoneServer.accept();
        System.out.println("PhoneServer.accept()");
        in = new BufferedReader(new InputStreamReader(
                phone.getInputStream()));

        out = new PrintWriter(phone.getOutputStream());
        o = new PhoneOut(out);
        ......
        phone.close();
        o.setFinish(true);
        phone = null;
        in = null;
        out = null;
        o = null;
    }

在关闭socket之后要清除以前的对象引用,以及消息队列中的消息,避免消息错误.

附上源码:源码下载地址

你可能感兴趣的:(Java入门)