如何与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人点赞
评论
共同学习,写下你的评论
评论加载中...
作者其他优质文章
正在加载中
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦