websocket实现即时聊天

                                      websocket的实现聊天及异常解决方案

看了很多人写的关于websocket的简单实现即时聊天(点对点+广播方式),鼓起勇气就想自己也写一个,虽然只是实现了简单的文字传送。当然特别感谢DyncRole大神的博客给了我许多的启发。嗯嗯,不太会说话,直接谈细节。

首先websocket协议几个关键部分就是分别对应到:

思路:

建立连接后,进行信息传输,一般只传简单的文字和必要的文件信息,所有样式都是在客户端拼接的。

1)服务器与客户端建立连接

 

  var ip_addr = document.location.hostname;//获取服务器地址
   console.info(ip_addr);	
    if ('WebSocket' in window){
      //拼接websocket对象
      ws = new WebSocket("ws://"+ip_addr+":8080/ostp/websocket");
    }else if ('MozWebSocket' in window){
	ws = new WebSocket(encodeURI("ws://"+ip_addr+":8080/ostp/websocket"));
    }
    else{
	   alert("not support");

    }

2)服务器与客户端建立连接成功后干什么

/**
 * AUTOOR:ggr
 * DESCRIPTION:即时通信关键js
 * TIME:2016-12-01
 */
	var ws = null;//定义websocket连接
	toName="all";//信息接收方,默认广播方式
	
	/**
	 * 实现窗口标题在收到消息时闪烁
	*/ 
	var flashStep=0;  //交替变量
	
	var flashTitleRun = false; //是否正在执行
	
	var normalTitle = "聊天室"; 
	
	//当前浏览器窗口是否处于焦点
	var isWindowFocus = true;

	function focusin() { isWindowFocus=true;}

	function focusout() { isWindowFocus=false;}

	//注册焦点变化监听器

	if ("onfocusin" in document){//for IE 

	    document.onfocusin = focusin;

	    document.onfocusout = focusout;

	} else {

	    window.onblur = focusout;

	    window.οnfοcus= focusin;

	}

	function flashTitle()  

	{  

	 //仅窗口不在焦点时闪烁title,回到焦点时停止闪烁并将title恢复正常

	 if(isWindowFocus){//当前处于焦点

	  document.title=normalTitle;

	  flashTitleRun = false;

	  return;//退出循环

	 }
	 
	 flashTitleRun = true;

	 flashStep++;  

	 if (flashStep==3) {flashStep=1;}  

	 if (flashStep==1) {
		 document.title="【您有新的消息】";	
	 }  

	 if (flashStep==2) {document.title="【                    】";}  

	 setTimeout("flashTitle()",200);  //循环

	} 

	//调用这个执行标题闪烁,而不是直接调用flashTitle,保证多次调用只会执行一次。

	function doFlashTitle(){
	 if(!flashTitleRun)    //没有执行时,才执行
		 flashTitle();
	}
	
	/**
	 * websocket部分
	 */
	function startWebSocket() {
		var ip_addr = document.location.hostname;
		console.info(ip_addr);	
		if ('WebSocket' in window){
			//alert(ip_addr);
			ws = new WebSocket("ws://"+ip_addr+":8080/ostp/websocket");
		}
		else if ('MozWebSocket' in window)
			ws = new WebSocket(encodeURI("ws://"+ip_addr+":8080/ostp/websocket"));
		else
			alert("not support");
		ws.onmessage = function(evt) {
			var data = evt.data;
			var o = eval("(" + data + ")");//将字符串转换成JSON  
			if (o.type == 'message') {
				setMessageInnerHTML(setmessage(o.data));
				//alert(setmessage(o.data));
				/*滚动条自动到底部*/
				var d=$(".chat01_content");
				d.scrollTop(d.prop("scrollHeight"));
				//提示音实现
				var temp3 = $("#message1")[0];
				temp3.volume = 0.6;
				temp3.speed  = 0.6;
				temp3.play();
				doFlashTitle();//实现标题闪烁
			} else if (o.type == 'user') {
				//提示音实现
				var temp2 = $("#message0")[0];
				temp2.speed  = 0.6;
				temp2.play();
				var userArry = o.data.split(',');
				$(".onlineuser").empty();//清空用户列表
				$(".onlineuser").append("
  • 所有用户
  • "); $.each(userArry, function(n, value) { if (value != self) { $(".onlineuser").append("
  • "+value+"
  • "); } }); } $(".to").click(function(){ //$('.chat01_content').empty(); $(this).css("background","#D1EEEE"); $(this).siblings().css("background","#FFFFFF"); var to = $(this).attr('id'); toName=to; }); }; ws.onclose = function(evt) { $('#denglu').html("离线"); }; ws.onopen = function(evt) { $('#denglu').html("在线"); $('#userName').html(self); var temp1 = $("#login")[0]; temp1.volume = 0.6; temp1.play(); }; //出现异常,直接关闭 ws.onerror = function (event) { if(ws.readyState==1){ onclose(event); } }; } function setMessageInnerHTML(innerHTML) { var temp = $('.chat01_content').html(); temp +=innerHTML + '
    '; $('.chat01_content').html(temp); } function sendMsg() { /** * 状态检验 */ if(($("#denglu").html()=="离线")||($("#denglu").html()=="正在登录")){ alert("请先登录!"); return; } var online = new Array(); online=$(".to"); if(online.size()>1){ var fromName = self; var content = $("#textarea").val(); //发送内容 if(content==""){ alert("请输入发送信息!"); return; } var time = getNowFormatDate(new Date()); var msg = fromName + "," + toName + "," + content + "," + time; $("#textarea").val(""); ws.send(msg); }else{ alert("无聊天对象!"); } } //样式拼装 function setmessage(e){ var message = e.split("|"); //发送方 var from = message[0]; //信息 var msg = message[1]; //时间 var time = message[2]; var img; if (from == $("#userName").text()) { img = ""; } else { img = ""; } message = "
    "+from+"
    "+msg +"
    "+time +"
    "; return message; } //格式化时间 function getNowFormatDate(data) { var date = data; var seperator1 = "-"; var seperator2 = ":"; var month = date.getMonth() + 1; var strDate = date.getDate(); if (month >= 1 && month <= 9) { month = "0" + month; } if (strDate >= 0 && strDate <= 9) { strDate = "0" + strDate; } var currentdate = date.getFullYear() + seperator1 + month + seperator1 + strDate + " " + date.getHours() + seperator2 + date.getMinutes() + seperator2 + date.getSeconds(); return currentdate; }
    遇到的问题及解决方案:


    1.远程的websocket失效(websocket连接失败):一般只能在局域网里面实现点对点和群发,因为系统的防火墙


    会拦截掉不安全的http 试探包,而websocket的机制就是首先发送一个http包进行握手,然后建立信任连接。


    2.关于非正常的关闭导致的读写异常,这是多线程里面的难题,也是整个网络编程里面最常见的问题之一,不正常的中断产生的原因包括:<1>网络不稳定导致连接忽然中断,而中断的时间点又恰恰是连接正在读,或写操作。<2>客户端不正常操作导致连接中断,<3>服务器端出现故障等等。找了很多地方,最后有人说用读写分离的方式可以解决,关于读写分离的话,geogle的CopyOnWriteArraySet提供了范例,基本思想就是写的
    时候先新建一个副本,在副本里面操作,一旦遇到异常也不会影响原来的数据,‘但是弊端就是要求更多的资源备用,同时不适用于读取十分频繁的操作逻辑。



    3.反正一句话:一旦遇到运行时的异常,直接关闭连接,保证服务器端的安全再说。


    这个onEoor方法实现便可,一旦有异常,直接关闭连接。

    @OnError
        public void onError(Session session, Throwable error){
            System.out.println("发生错误");
            //error.printStackTrace();
            System.out.println("自我修复中.......");
            onClose();//一旦出现问题直接关闭连接,保证服务器端安全性
        }

    演示:


    websocket实现即时聊天_第1张图片


    websocket实现即时聊天_第2张图片

    代码有点多:就不贴了。直接链个地址吧:http://download.csdn.net/detail/sundaysunshine/9699361



    你可能感兴趣的:(Web)