简单的Socket消息转发实现

来源:互联网 发布:怪医黑杰克 知乎 编辑:程序博客网 时间:2024/06/06 11:03

简单的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之后要清除以前的对象引用,以及消息队列中的消息,避免消息错误.

附上源码:源码下载地址

2 0
原创粉丝点击