手记

Java 使用mina跟webSocket通信

如何与WebSocket如何通信?
我这里使用的是mina,当然你也可使用socket作为通信.

JSP页面代码:

<script>
var socket = null;
layui.use('layim', function(layim){
      layim.on('sendMessage', function(res){
          socket.send(JSON.stringify({
              type: 'text' //随便定义,用于在服务端区分消息类型
              ,data:res
          }));

      });
    //监听收到的消息
      socket.onmessage = function(res){
         try{ 
              console.log("onmessage receiver msg:"+res.data);
              res = JSON.parse(res.data);//字符串转json
              if(res.emit === "text"){        
                    layim.getMessage(res.data);
              }
         }catch(e){
             console.error(e);
         }
      };

});  
//init websocket
$(function(){
    try {
        var url = "ws://127.0.0.1:9012";
        if ('WebSocket' in window) {
            socket = new WebSocket(url);
        } else if ('MozWebSocket' in window) {
            socket = new MozWebSocket(url);
        }
    } catch (e) {
        console.error(e);
    }
  //连接成功时触发
  socket.onopen = function(){
      socket.send("connected successful..."); 
  };

  // 监听Socket的关闭
  socket.onclose = function(event) { 
      console.debug("receiver msg:"+event);
  }; 
  socket.onerror=function(event){
      console.error("receiver error msg:"+event);
  };

});

</script>

> 后台使用mina集成springMVC

> xml代码部分:
 <!-- mina -->
 <bean class="org.springframework.beans.factory.config.CustomEditorConfigurer">
    <property name="customEditors">
        <map>
            <!-- spring升级后此配置已失效  会报错
            <entry key="java.net.SocketAddress">
                <bean class="org.apache.mina.integration.beans.InetSocketAddressEditor" />
            </entry> 
            -->
            <entry key="java.net.SocketAddress" value="org.apache.mina.integration.beans.InetSocketAddressEditor"/>

        </map>
    </property>
 </bean>

 <!-- 配置业务处理类 -->
 <bean id="serviceHandler" class="org.springboot.mina.ServerHandler" />
 <!-- 配置service -->
<bean id="ioAcceptor" class="org.apache.mina.transport.socket.nio.NioSocketAcceptor"
    init-method="bind" destroy-method="unbind">
    <property name="defaultLocalAddress" value=":9012" />
    <property name="handler" ref="serviceHandler" />
     <!--声明过滤器的集合-->
    <property name="filterChainBuilder" ref="filterChainBuilder" />
    <property name="reuseAddress" value="true" />
</bean>
<!-- 配置解码器 -->
<bean id="codec" class="org.apache.mina.filter.codec.ProtocolCodecFilter">
    <constructor-arg>
        <!-- <bean class="org.apache.mina.filter.codec.textline.TextLineCodecFactory" >
            <constructor-arg value="UTF-8"></constructor-arg>
        </bean> -->
        <!-- 自定义的 字符编码类 org.springboot.mina.codec.ICodeFactory-->
        <bean class="org.springboot.mina.codec.WebSocketCodecFactory" />
    </constructor-arg>
</bean>

<!-- 配置日志拦截器 -->
<bean id="logger" class="org.apache.mina.filter.logging.LoggingFilter"></bean>

<!-- 将日志和解码器添加 -->
<bean id="filterChainBuilder"
    class="org.apache.mina.core.filterchain.DefaultIoFilterChainBuilder">
    <property name="filters">
        <map>
            <!--mina自带的线程池filter-->
            <entry key="executor" value-ref="executorFilter" />
            <entry key="mdcInjectionFilter" value-ref="mdcInjectionFilter" />
            <entry key="codec" value-ref="codec" />
            <entry key="logger" value-ref="logger" />
             <!--心跳filter-->
            <!-- <entry key="keepAliveFilter" value-ref="keepAliveFilter" /> -->
        </map>
    </property>
</bean>

<!-- executorFilter多线程处理 -->  
<bean id="executorFilter" class="org.apache.mina.filter.executor.ExecutorFilter" />
<bean id="mdcInjectionFilter" class="org.apache.mina.filter.logging.MdcInjectionFilter">
    <constructor-arg value="remoteAddress" />
</bean>

> java中的几个关键类

编码器和解码器(必须,如果不按照以下操作,会导致握手不成功)

//编码器
public class MinaEncoder extends DemuxingProtocolEncoder {
    public MinaEncoder() {
        addMessageEncoder(MinaBean.class, new BaseSocketBeanEncoder());
    }

    private String buildResponseMsg(To to) {
        if (to == null) return "";
        ResponseMsgDto res = new ResponseMsgDto();
        res.setEmit(ChatMessageEnum.TEXT.getMessage());
        SendInfoDto send = new SendInfoDto();
        res.setData(send);
        send.setAvatar(to.getAvatar());
        send.setId(to.getId());
        if (to.getType().equalsIgnoreCase(ChatMessageTypeEnum.FRIEND.getType()))
            send.setContent("你好:tom!");
        else if(to.getType().equalsIgnoreCase(ChatMessageTypeEnum.GROUP.getType())){
            send.setContent("HI,我是新来的请多多关照!");
            send.setId(to.getId());
        }

        send.setMine(false);
        send.setType(to.getType());
        send.setTimestamp(new Date().getTime());
        send.setUsername(to.getUsername());
        return new Gson().toJson(res);

    }
    public static Object convertJSONToObject(String msg,Object obj){
        try {
            Object resObj = new Gson().fromJson(msg,obj.getClass());
            return resObj;
        } catch (ClassCastException e) {
            //不处理
        } catch (JsonSyntaxException e) {
            //不处理
        }
        return null;
    }

    class BaseSocketBeanEncoder implements MessageEncoder<MinaBean> {
        public void encode(IoSession session, MinaBean message,
                ProtocolEncoderOutput out) throws Exception {
            byte[] _protocol = null;

            if (message.isWebAccept()) {
                _protocol = message.getContent().getBytes("UTF-8");
            } else {
                try {
                    ReceiverMsgDto receiverMsg = new ReceiverMsgDto();
                    receiverMsg = (ReceiverMsgDto)convertJSONToObject((String) message.getContent(),receiverMsg);

                    String responseMsg = "";// 响应结果
                    if (receiverMsg != null) {
                        // 获取接收者
                        To to = receiverMsg.getData().getTo();
                        if (to != null) // 构建返回参数
                            responseMsg = buildResponseMsg(to);
                    }
                    message.setContent(responseMsg);
                } catch (ClassCastException e) {
                    //不处理
                } catch (JsonSyntaxException e) {
                    //不处理
                }
                _protocol = WebSocketUtil.encode(message.getContent());
            }

            int length = _protocol.length;
            IoBuffer buffer = IoBuffer.allocate(length);
            buffer.put(_protocol);
            buffer.flip();
            out.write(buffer);
        }
    }
public class MinaDecoder extends DemuxingProtocolDecoder {
    public static final byte MASK = 0x1;// 1000 0000
    public static final byte HAS_EXTEND_DATA = 126;
    public static final byte HAS_EXTEND_DATA_CONTINUE = 127;
    public static final byte PAYLOADLEN = 0x7F;// 0111 1111

    public MinaDecoder() {
        addMessageDecoder(new BaseSocketBeanDecoder());
    }

    class BaseSocketBeanDecoder extends MessageDecoderAdapter {
        public MessageDecoderResult decodable(IoSession session, IoBuffer in) {
            if (in.remaining() < 2) {
                return NEED_DATA;
            }
            in.get();// 第一个字节
            byte head2 = in.get();// 第二个字节
            byte datalength = (byte) (head2 & PAYLOADLEN);// 得到第二个字节后七位的值
            int length = 0;
            if (datalength < HAS_EXTEND_DATA) {// 第一种是消息内容少于126存储消息长度
                length = datalength;
            } else if (datalength == HAS_EXTEND_DATA) {// 第二种是消息长度大于等于126且少于UINT16的情况此值为126
                if (in.remaining() < 2) {
                    return NEED_DATA;
                }
                byte[] extended = new byte[2];
                in.get(extended);
                int shift = 0;
                length = 0;
                for (int i = extended.length - 1; i >= 0; i--) {
                    length = length + ((extended[i] & 0xFF) << shift);
                    shift += 8;
                }
            } else if (datalength == HAS_EXTEND_DATA_CONTINUE) {// 第三种是消息长度大于UINT16的情况下此值为127
                if (in.remaining() < 4) {
                    return NEED_DATA;
                }
                byte[] extended = new byte[4];
                in.get(extended);
                int shift = 0;
                length = 0;
                for (int i = extended.length - 1; i >= 0; i--) {
                    length = length + ((extended[i] & 0xFF) << shift);
                    shift += 8;
                }
            }

            int ismask = head2 >> 7 & MASK;// 得到第二个字节第一位的值
            if (ismask == 1) {// 有掩码
                if (in.remaining() < 4 + length) {
                    return NEED_DATA;
                } else {
                    return OK;
                }
            } else {// 无掩码
                if (in.remaining() < length) {
                    return NEED_DATA;
                } else {
                    return OK;
                }
            }
        }

        public MessageDecoderResult decode(IoSession session, IoBuffer in,
                ProtocolDecoderOutput out) throws Exception {
            in.get();
            byte head2 = in.get();
            byte datalength = (byte) (head2 & PAYLOADLEN);
            if (datalength < HAS_EXTEND_DATA) {
            } else if (datalength == HAS_EXTEND_DATA) {
                byte[] extended = new byte[2];
                in.get(extended);
            } else if (datalength == HAS_EXTEND_DATA_CONTINUE) {
                byte[] extended = new byte[4];
                in.get(extended);
            }

            int ismask = head2 >> 7 & MASK;
            MinaBean message = new MinaBean();
            byte[] date = null;
            if (ismask == 1) {// 有掩码
                // 获取掩码
                byte[] mask = new byte[4];
                in.get(mask);

                date = new byte[in.remaining()];
                in.get(date);
                for (int i = 0; i < date.length; i++) {
                    // 数据进行异或运算
                    date[i] = (byte) (date[i] ^ mask[i % 4]);
                }
            } else {
                date = new byte[in.remaining()];
                in.get(date);
                message.setWebAccept(true);
            }
            message.setContent(new String(date, "UTF-8"));
            out.write(message);
            return OK;
        }
    }

``

//bean,用来接收websocket传递来的消息
public class MinaBean {
    private String content;
    private boolean isWebAccept=false;

    public String getContent() {
        return content;
    }

    public void setContent(String content) {
        this.content = content;
    }

    public boolean isWebAccept() {
        return isWebAccept;
    }

    public void setWebAccept(boolean isWebAccept) {
        this.isWebAccept = isWebAccept;
    }

    @Override
    public String toString() {
        return "MinaBean [content=" + content + "]";
    }
//服务类

/**
 * 
 * mina服务端业务处理类
 *
 */
public class ServerHandler extends IoHandlerAdapter {
    private static Logger log = LogManager.getLogger(ServerHandler.class);
    public static Map<Long, IoSession> ioSession = new HashMap<Long, IoSession>();

    public ServerHandler() {
    }

    @Override
    public void exceptionCaught(IoSession session, Throwable cause)
            throws Exception {
    }

    @Override
    public void messageReceived(IoSession session, Object message) {
        try {
            System.out.println("addr:" + session.getRemoteAddress() + ",message:\n" + message);
            MinaBean minaBean = (MinaBean) message;
            // 是否是握手请求
            if (minaBean.isWebAccept()) {
                MinaBean sendMessage = minaBean;
                sendMessage.setContent(WebSocketUtil.getSecWebSocketAccept(minaBean
                        .getContent()));
                session.write(sendMessage);
                ioSession.put(session.getId(), session);
            } else {
                //chat 
                chat(session,minaBean);
            }

        } catch (ClassCastException e) {
            // 不处理
        } catch (JsonSyntaxException e) {
            // 不做处理
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    @Override
    public void messageSent(IoSession session, Object message) throws Exception {
        System.out.println("server send msg to client ,message:" + message);
        log.debug("------------server send msg to client ,message:" + message);
    }

    @Override
    public void sessionClosed(IoSession session) throws Exception {
        // 将hash中的session移除。
        // Memcached.remove(""+session.getId());
        ioSession.remove(session.getId());
        System.out.println(session + "退出map");
    }

    @Override
    public void sessionCreated(IoSession session) throws Exception {
        System.out.println("=========cread session,"
                + session.getRemoteAddress().toString());
        log.debug(session.getRemoteAddress().toString()
                + "----------------------create");
    }

    @Override
    public void sessionIdle(IoSession session, IdleStatus status)
            throws Exception {
        System.out.println("IDLE " + session.getIdleCount(status));
    }

    @Override
    public void sessionOpened(IoSession session) throws Exception {
        System.out.println(session + "加入map");
    }

    public static Map<Long, IoSession> getIoSession() {
        return ioSession;
    }
    /**
     * 聊天
     * @param session
     * @param minaBean
     */
    public void chat(IoSession session,MinaBean minaBean){
        ReceiverMsgDto receiverMsg = new ReceiverMsgDto();
        receiverMsg = (ReceiverMsgDto) MinaEncoder.convertJSONToObject((String) minaBean
                .getContent(), receiverMsg);
        if (receiverMsg != null) {
            // 获取接收者
            To to = receiverMsg.getData().getTo();
            if (to != null) {
                // 1-1
                if (to.getType().equalsIgnoreCase(ChatMessageTypeEnum.FRIEND.getType())) {
                    IoSession is = ioSession.get(session.getId());
                    if (is != null)
                        is.write(minaBean);
                } else {
                    // 1-n
                    Collection<IoSession> ioSessionSet = session.getService()
                            .getManagedSessions().values();
                    for (IoSession is : ioSessionSet) {
                        is.write(minaBean);
                    }
                }

            }
        }
    }

    public static void setIoSession(Map<Long, IoSession> ioSession) {
        ServerHandler.ioSession = ioSession;
    }
//controller
public class ChatController {
    private static Logger log = LogManager.getLogger(ChatController.class);
    //
    private static final String USER = "USER",GROUP = "GROUP";
    private static final String IMG_SUFFIX = "images/chat/";
    @Autowired
    private ChatUserService  chatUserService;
    @Autowired
    private ChatGroupService chatGroupService;

    @RequestMapping("/to-chat.do")
    public ModelAndView toChat(){
        ModelAndView mv = new ModelAndView();
        mv.setViewName("chat/chat.jsp");
        return mv;

    }
}

//地址栏访问:http://localhost/SpringMVC/to-chat.do

后端查看初始化连接的日志
28511 [pool-4-thread-1] INFO org.apache.mina.filter.logging.LoggingFilter - RECEIVED: MinaBean [content=connected successful...]
addr:/127.0.0.1:57822,message:
MinaBean [content=connected successful...]
聊天信息:(通过日志可以看到前端发过来的信息,及后端响应给前端的数据)
1080378 [pool-4-thread-2] INFO org.apache.mina.filter.logging.LoggingFilter - RECEIVED: MinaBean [content={"type":"text","data":{"mine":{"username":"tom","avatar":"http://127.0.0.1/SpringMVC/images/header/tom.jpg","id":1,"mine":true,"content":"你好"},"to":{"id":2,"username":"timor","status":"online","avatar":"http://127.0.0.1/SpringMVC/images/header/timor.jpg","createTime":"Nov 30, 2016 4:01:02 PM","updateTime":"Nov 30, 2016 4:01:01 PM","name":"timor","type":"friend"}}}]
addr:/127.0.0.1:57822,message:
MinaBean [content={"type":"text","data":{"mine":{"username":"tom","avatar":"http://127.0.0.1/SpringMVC/images/header/tom.jpg","id":1,"mine":true,"content":"你好"},"to":{"id":2,"username":"timor","status":"online","avatar":"http://127.0.0.1/SpringMVC/images/header/timor.jpg","createTime":"Nov 30, 2016 4:01:02 PM","updateTime":"Nov 30, 2016 4:01:01 PM","name":"timor","type":"friend"}}}]
1080433 [pool-4-thread-2] INFO org.apache.mina.filter.logging.LoggingFilter - SENT: MinaBean [content={"emit":"text","data":{"username":"timor","avatar":"http://127.0.0.1/SpringMVC/images/header/timor.jpg","type":"friend","content":"你好:tom!","id":2,"mine":false,"timestamp":1503987895304}}]
server send msg to client ,message:MinaBean [content={"emit":"text","data":{"username":"timor","avatar":"http://127.0.0.1/SpringMVC/images/header/timor.jpg","type":"friend","content":"你好:tom!","id":2,"mine":false,"timestamp":1503987895304}}]
//前端,按F12可以看到消息接收成功
onmessage receiver msg:{"emit":"text","data":{"username":"timor","avatar":"http://127.0.0.1/SpringMVC/images/header/timor.jpg","type":"friend","content":"你好:tom!","id":2,"mine":false,"timestamp":1503987895304}}
//转载请注明出处,谢谢
5人推荐
随时随地看视频
慕课网APP

热门评论

为毛原创不给分?下次不分享了

为毛原创不给分?下次不分享了

能请教你一个问题吗

查看全部评论