`
147175882
  • 浏览: 133989 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

基于mina 的java服务器和html5 websocket的简单在线聊天室

阅读更多
想学学html5的开发。就做个websocket的聊天室程序。其实都很简单,把协议用对就好。
具体的websocket的握手程序和解码编码都可以参照下面的链接
http://www.cnblogs.com/pctzhang/archive/2012/02/19/2358496.html

websocket的代码就简单得不想写了

下面是主要的java代码基于mina。如果有兴趣的话就下整个工程自己玩玩吧


package websocket;

import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.mina.core.buffer.IoBuffer;
import org.apache.mina.core.service.IoHandlerAdapter;
import org.apache.mina.core.session.IdleStatus;
import org.apache.mina.core.session.IoSession;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * @author amu
 *
 */
public class WebSocketIoHandler extends IoHandlerAdapter {

    public static final String INDEX_KEY = WebSocketIoHandler.class.getName() + ".INDEX";
    private static Logger LOGGER = LoggerFactory.getLogger(WebSocketIoHandler.class);
    
    Map<Long, IoSession> ioSessionMap = new HashMap<Long, IoSession>();
    
    public void messageReceived(IoSession session, Object message) throws Exception {
    	IoBuffer buffer = (IoBuffer)message;
    	
    	byte[] b = new byte[buffer.limit()];  
    	buffer.get(b); 

    	Long sid = session.getId();

    	if (!ioSessionMap.containsKey(sid)) {
    		LOGGER.info("user '{}',has been created" + sid);
    		ioSessionMap.put(sid, session);
    		
        	String m = new String(buffer.array());
			String sss = getSecWebSocketAccept(m);
			
			buffer.clear();
			buffer.put(sss.getBytes("utf-8"));
			
			buffer.flip();
			session.write(buffer);
			buffer.free();
    	} else {
    		String m = decode(b);
    		LOGGER.info("from client is :" + m);
        	buffer.clear();

        	byte[] bb = encode(m);

        	buffer.put(bb);
        	buffer.flip();
        	
        	synchronized (ioSessionMap) {
            	Collection<IoSession> ioSessionSet = ioSessionMap.values();
            	for (IoSession is : ioSessionSet) {
    				if (is.isConnected()) {
    					System.out.println("response message to " + is);
    					is.write(buffer.duplicate());
    				}
        		}
            }
    		buffer.free();
    	}
    }

    @Override   
    public void sessionOpened(IoSession session) throws Exception {
        session.setAttribute(INDEX_KEY, 0);
    }

    @Override   
    public void sessionIdle( IoSession session, IdleStatus status ) throws Exception {   
        System.out.println( "IDLE " + session.getIdleCount( status ));   
    } 
    
	public static String getSecWebSocketAccept(String key) {
		String secKey = getSecWebSocketKey(key);

		String guid = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
		secKey += guid;
		try {
			MessageDigest md = MessageDigest.getInstance("SHA-1");
			md.update(secKey.getBytes("iso-8859-1"), 0, secKey.length());
			byte[] sha1Hash = md.digest();
			secKey = base64Encode(sha1Hash);
		} catch (Exception e) {
			e.printStackTrace();
		}

		String rtn = "HTTP/1.1 101 Switching Protocols\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Accept: "
				+ secKey + "\r\n\r\n";
		return rtn;
	}
	
	public static String getSecWebSocketKey(String req) {
		Pattern p = Pattern.compile("^(Sec-WebSocket-Key:).+",
				Pattern.CASE_INSENSITIVE | Pattern.MULTILINE);
		Matcher m = p.matcher(req);
		if (m.find()) {
			String foundstring = m.group();
			return foundstring.split(":")[1].trim();
		} else {
			return null;
		}

	}


	public static String base64Encode(byte[] input) {
		return new String(org.apache.mina.util.Base64.encodeBase64(input));
	}

	// / <summary>
	// /判断传入数据是否存在掩码
	// / 传入数据:hi
	// / socket接收到的二进制数据:
	// / 1000000110000010 1101011011101001
	// / 111110 111000 10111110 10000000
	// / 掩码异或的操作:
	// / 111110 111000 10111110 10000000
	// / 进行异或^ 111110 111001 11010110 11101001
	// / 结果: 1101000 1101001
	// / 数据样例:
	// / [0] 129 byte
	// / [1] 130 byte
	// / [2] 214 byte
	// / [3] 233 byte
	// / [4] 62 byte
	// / [5] 56 byte
	// / [6] 190 byte
	// / [7] 128 byte
	// / </summary>
	// / <returns></returns>
	public static String decode(byte[] receivedDataBuffer)
			throws UnsupportedEncodingException {
		String result = null;

		// 计算非空位置
		int lastStation = receivedDataBuffer.length - 1;
		
		// 利用掩码对org-data进行异或
		int frame_masking_key = 1;
		for (int i = 6; i <= lastStation; i++) {
			frame_masking_key = i % 4;
			frame_masking_key = frame_masking_key == 0 ? 4 : frame_masking_key;
			frame_masking_key = frame_masking_key == 1 ? 5 : frame_masking_key;
			receivedDataBuffer[i] = (byte) (receivedDataBuffer[i] ^ receivedDataBuffer[frame_masking_key]);
		}

		result = new String(receivedDataBuffer, 6, lastStation - 5, "UTF-8");

		return result;

	}

	// / 对传入数据进行无掩码转换
	public static byte[] encode(String msg) throws UnsupportedEncodingException {
		// 掩码开始位置
		int masking_key_startIndex = 2;

		byte[] msgByte = msg.getBytes("UTF-8");

		// 计算掩码开始位置
		if (msgByte.length <= 125) {
			masking_key_startIndex = 2;
		} else if (msgByte.length > 65536) {
			masking_key_startIndex = 10;
		} else if (msgByte.length > 125) {
			masking_key_startIndex = 4;
		}

		// 创建返回数据
		byte[] result = new byte[msgByte.length + masking_key_startIndex];

		// 开始计算ws-frame
		// frame-fin + frame-rsv1 + frame-rsv2 + frame-rsv3 + frame-opcode
		result[0] = (byte) 0x81; // 129

		// frame-masked+frame-payload-length
		// 从第9个字节开始是 1111101=125,掩码是第3-第6个数据
		// 从第9个字节开始是 1111110>=126,掩码是第5-第8个数据
		if (msgByte.length <= 125) {
			result[1] = (byte) (msgByte.length);
		} else if (msgByte.length > 65536) {
			result[1] = 0x7F; // 127
		} else if (msgByte.length > 125) {
			result[1] = 0x7E; // 126
			result[2] = (byte) (msgByte.length >> 8);
			result[3] = (byte) (msgByte.length % 256);
		}

		// 将数据编码放到最后
		for (int i = 0; i < msgByte.length; i++) {
			result[i + masking_key_startIndex] = msgByte[i];
		}
		
		return result;
	}
}


分享到:
评论
16 楼 nihaomawobuhaoa 2015-11-05  
要是前台发图片的话,mina解码该怎么处理?
15 楼 sujianchinaouya 2015-09-14  
用你这个代码试了一下,客户端发送的消息长度超过125时,服务端解码会乱码,解码这块是不是得判断一下.
14 楼 兵腾傲宇 2015-09-06  
好用啊,就是如果能够创建多个聊天室就完美了。。。楼主,应该怎么做呢?
13 楼 147175882 2014-12-10  
147175882 写道
zhumin042 写道
ghostwjk 写道
用你这个代码试了一下,客户端发送的消息长度超过125时,服务端解码会乱码,解码这块是不是得判断一下.

http://www.haogongju.net/art/1789637


数据包太长的话。udp传输会有粘包和断包的现象。需要进行处理。度娘一下你就知道了

错了。是tcp传输。

具体可以仔细看下tcp/ip的传输协议。socket开发中经常会遇到
12 楼 147175882 2014-12-10  
zhumin042 写道
ghostwjk 写道
用你这个代码试了一下,客户端发送的消息长度超过125时,服务端解码会乱码,解码这块是不是得判断一下.

http://www.haogongju.net/art/1789637


数据包太长的话。udp传输会有粘包和断包的现象。需要进行处理。度娘一下你就知道了
11 楼 wlwnl 2014-12-05  
感谢楼主!楼主是好人。
10 楼 wlwnl 2014-12-05  
[i][/i]
9 楼 wlwnl 2014-12-05  
8 楼 yan127422_1 2013-08-26  
不错,感谢lz
7 楼 k112211k 2013-07-04  
很有用,支持,正好最近在写一个mina服务器 websocket 的项目,在其中遇到了一些问题,就是用上面的代码,客户端和服务器能连接上,看服务器接不到包,有加到你的QQ吗?咱们细聊
6 楼 zhumin042 2013-01-17  
zhumin042 写道
ghostwjk 写道
用你这个代码试了一下,客户端发送的消息长度超过125时,服务端解码会乱码,解码这块是不是得判断一下.

http://www.haogongju.net/art/1789637

5 楼 zhumin042 2013-01-17  
ghostwjk 写道
用你这个代码试了一下,客户端发送的消息长度超过125时,服务端解码会乱码,解码这块是不是得判断一下.

http://www.haogongju.net/art/1789637
4 楼 lingcen000 2012-12-24  
我用java写了个客户端,访问你这个服务器程序,编码这边不知道怎么弄,大神能帮忙看一下么

public class WebSocketClient
{
    public static void main(String[] args) throws IOException
    {

        NioSocketConnector connector = new NioSocketConnector();
        connector.getFilterChain().addLast("logger", new LoggingFilter());
        connector.getFilterChain().addLast("codec",
                new ProtocolCodecFilter(new TextLineCodecFactory(Charset.forName("UTF-8")))); // 设置编码过滤器
        connector.setHandler(new WebSocketIoHandlerClient());// 设置事件处理器
        ConnectFuture cf = connector.connect(new InetSocketAddress("127.0.0.1", WebSocketServer.PORT));// 建立连接
        cf.awaitUninterruptibly();// 等待连接创建完成

        if (cf.isConnected())
        {
            IoSession session = cf.getSession();
            writeMsg(session, "s");
            // writeMsg(session, "quit");

            session.getCloseFuture().awaitUninterruptibly();// 等待连接断开
        }
        connector.dispose();
    }

    public static void writeMsg(IoSession session, String msg) throws IOException
    {

        // byte[] bb = { (byte) 0x81, (byte) 0x81, (byte) 0xBD, (byte) 0x78, (byte) 0X9C, (byte) 0x10, (byte) 0xCE };
        // byte[] bb = { (byte) 0x81, (byte) 0x81, (byte) 0x57, (byte) 0x64, (byte) 0X1A, (byte) 0xD9, (byte) 0x24 };
//上面这2个都是从页面访问时得到的,是可以用的
        IoBuffer buffer = IoBuffer.allocate(8);
        buffer.setAutoExpand(true);
        byte[] bb = WebSocketPushHandler.encode(msg);
        buffer.put(bb);
        buffer.flip();
        if (session.isConnected())
        {
            session.write(buffer);
        }
        buffer.free();
    }
}
3 楼 coolcooldool 2012-11-28  
这个很有用啊,支持楼主!~
2 楼 lsyoung 2012-11-14  
这个需要配什么呢?
1 楼 ghostwjk 2012-09-18  
用你这个代码试了一下,客户端发送的消息长度超过125时,服务端解码会乱码,解码这块是不是得判断一下.

相关推荐

    基于html5的javaweb聊天室

    【基于HTML5的JavaWeb聊天室】是一种使用HTML5、Java和相关技术构建的实时通信应用,它允许用户通过Web浏览器进行在线对话。这个Java聊天室的Demo是为初学者设计的,旨在帮助他们理解如何将这些技术整合到实际项目中...

    基于Java的mina框架

    基于Java的米娜框架,报告对使用基于Java、websocket协议的网页聊天室的过程和技术做了详细的叙述首先,对现有网页进行了分析与评价。首先, 启动后台服务器,然后连接站点,客户端在pc端输入网站或者在手机端扫...

    mina+spring实现多人聊天室程序

    Mina框架和Spring框架的结合,为开发者提供了一种高效、灵活的方式来实现这样的系统,特别是多人聊天室程序。下面我们将详细探讨如何利用这两个强大的工具来构建一个稳定且功能丰富的聊天室应用。 Mina框架,全称...

    Vue+Java 通过websocket实现服务器与客户端双向通信操作

    WebSocket是一种在浏览器和服务器之间建立持久性连接的协议,允许实时、双向数据传输,非常适合实时应用,如聊天室、股票交易系统或者在线游戏。 1. **Vue.js 部分**: Vue.js 的部分主要涉及以下组件和方法: - ...

    原创nio socket mina+javascript+flash实现commet长连接网页聊天室

    【标题】"原创nio socket mina+javascript+flash实现commet长连接网页聊天室"揭示了一个基于Java NIO(Non-blocking I/O)的Socket通信框架Mina与JavaScript、Flash技术结合,实现COMET(Comet是使服务器向浏览器推...

    JAVA上百实例源码以及开源项目源代码

    JAVA+JSP的聊天室 8个目标文件 简单 JavaScript万年历 显示出当前时间及年份,还可以选择年份及月份和日期 Java编写的HTML浏览器 一个目标文件 摘要:Java源码,网络相关,浏览器 Java编写的HTML浏览器源代码,一个很...

    MINA 2.0.7 官方示例

    MINA (Multipurpose Infrastructure for Network Applications) 是一个高性能、跨平台的Java框架,主要用于开发网络应用程序,如服务器和客户端应用。MINA 提供了一个高级的网络应用编程接口(API),使得开发者可以...

    spring mina

    Spring Mina是一个基于Java的网络通信框架,它简化了创建高性能和高可靠性的网络应用程序的过程。这个"spring mina"的示例项目很可能是为了演示如何在Spring框架中集成Mina,以便构建服务器端和客户端的网络通信应用...

    java IM即时通讯

    5. 群组与聊天室:支持用户创建群组和聊天室,管理成员权限,进行多用户聊天。 6. 安全性:IM系统必须确保通信的隐私,防止中间人攻击,通常采用SSL/TLS协议进行加密。 三、Java IM技术栈 1. Spring Boot:用于...

    XMPP客户端Java类库:Smack

    XML即时通讯协议(Extensible Messaging and Presence Protocol,简称XMPP)是一种基于XML的开放标准,用于实现实时通讯和在线状态管理。它被广泛应用于即时消息、VoIP、文件传输等领域,尤其在企业级通信解决方案中...

Global site tag (gtag.js) - Google Analytics