`
147175882
  • 浏览: 132890 次
  • 性别: 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时,服务端解码会乱码,解码这块是不是得判断一下.

相关推荐

Global site tag (gtag.js) - Google Analytics