关于WebSocket网上已经有不少的介绍了,这里简单复制一下。

websocket采用全双工通信,使服务端也能主动向客户端发送数据。流程为:客户端向服务器发出建立websocket连接的请求,在websocket连接建立之后,客户端和服务端就可以通过TCP连接传输数据。

这里采用spring4.0的框架实现一个,有聊天用户列表(ip地址),显示发送人和和接收人,可以发送图片的例子。

废话少说,先上代码:代码资源下载地址


Java后端首先采用配置的形式实现端口:

WebSoketConfig .java

/**
 * @author sgchen项目启动时配置路径
 *
 */
@Configuration //定义配置文件
@EnableWebSocket //申明一个服务开启WebSocket
public class WebSoketConfig implements WebSocketConfigurer {

	@Override
	public void registerWebSocketHandlers(WebSocketHandlerRegistry handler) {
		// 支持的链接
		handler.addHandler(new WebSocketHandler(), "/sgchen").addInterceptors(new WebSocketHandInterceptor())
				.setAllowedOrigins("*");
		//SocketJs的链接
		handler.addHandler(new WebSocketHandler(), "/zh/sgchen").addInterceptors(new WebSocketHandInterceptor())
				.withSockJS();
	}

}

WebSocketHandInterceptor.java 

/**
 * @author Administrator
 *WebSocket拦截器
 */
public class WebSocketHandInterceptor implements HandshakeInterceptor {

	/**
	 * 初次握手之后
	 */
	@Override
	public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler handler, Exception e) {
	}

	/**
	 * 初次握手前
	 */
	@Override
	public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler handler,
			Map<String, Object> map) throws Exception {
		if(request instanceof ServletServerHttpRequest) {
			HttpServletRequest rq=((ServletServerHttpRequest) request).getServletRequest();
			//rq.setCharacterEncoding("UTF-8");
			if(ServletFileUpload.isMultipartContent(rq)) {
				int len=rq.getContentLength();
				ServletInputStream in =rq.getInputStream();
				byte[] buffer=new byte[len];
				in.read(buffer, 0, len);
				//rq.getSession().setAttribute("IMAGE_BLOB", buffer);
				map.put("IMAGE_BLOB",buffer);
			}
			//使用userName区分WebSocketHandler,以便定向发送消息
			String host = (String) rq.getRemoteHost();
			
			//存入数据,方便在hander中获取,这里只是在方便在webSocket中存储了数据,
			//并不是在正常的httpSession中存储,想要在平时使用的session中获得这里的数据,需要使用session 来存储一下
			map.put("REMOTE_HOST",host);
			//rq.getSession().setAttribute("WEBSOCKET_USERNAME", host);
			
		}
		return true;
	}

}

WebSocketHandler .java 类

package com.fiberhome.godway.zdrcommon.WebSocket;

/**
 * @author sgchen
 *WebSocket消息处理中心
 */
public class WebSocketHandler extends AbstractWebSocketHandler{
	
	public static final Logger	logger	= Logger.getLogger(WebSocketHandler.class);
	
	public static Collection<WebSocketSession> servers=Collections.synchronizedCollection(new ArrayList<WebSocketSession>());
	
	FileOutputStream os;

	//连接关闭之后
	@Override
	public void afterConnectionClosed(WebSocketSession session, CloseStatus closeStatus) throws Exception {
		// TODO Auto-generated method stub
		String host=(String) session.getAttributes().get("REMOTE_HOST");
		String message = "游客[" + host + "]退出聊天室!";
		logger.debug("连接关闭..."+closeStatus.toString());
		WebSocketUtil.removeSession(host);
		WebSocketUtil.notifyToAll(host,message);
	}

	//初次连接执行
	@Override
	public void afterConnectionEstablished(WebSocketSession session) throws Exception {
		// TODO Auto-generated method stub
		logger.debug("连接成功...");
		String host=(String) session.getAttributes().get("REMOTE_HOST");
		WebSocketUtil.addSession(host, session);
		String message = "有新人[" + host + "]加入聊天室!";
		WebSocketUtil.notifyToAll(host,message);
		
	}

	
	//连接错误
	@Override
	public void handleTransportError(WebSocketSession session, Throwable throwable) throws Exception {
		// TODO Auto-generated method stub
		if (session.isOpen()) {
			session.close();
		}
		logger.error("连接错误关闭连接", throwable);
		WebSocketUtil.removeSession(session.getAttributes().get("REMOTE_HOST").toString());
	}

	@Override
	public boolean supportsPartialMessages() {
		// TODO Auto-generated method stub
		return true;
	}
	
	//二进制信息
	@Override
	protected void handleBinaryMessage(WebSocketSession session, BinaryMessage message) throws Exception {
		// TODO Auto-generated method stub
		ByteBuffer buffer=message.getPayload();
		os.write(buffer.array());
		//os.flush();
	}
	
	@Override
	protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
		//判断传输的消息类型
		String host=(String) session.getAttributes().get("REMOTE_HOST");
		String payLoad=(String) message.getPayload();
		if(payLoad.endsWith(":fileStart")) {
			os=new FileOutputStream(payLoad.split(":")[0]);
		}else if(payLoad.endsWith(":fileNoText")){
			os.close();
			String fileName=payLoad.split(":")[0];
			WebSocketUtil.sendImgToAll(host, fileName);
		}else {
			WebSocketUtil.sendMessageToAll(host,message.getPayload());
		}
	}
	
	

}

工具类:WebSocketUtil.java

public class WebSocketUtil {

	private static final Map<String, WebSocketSession> ONLINE_SESSION=new ConcurrentHashMap<>();
	
	private static final int STATUS=200;
	
	private static final int BINARY_TWO=2;

	public static final Logger	logger	= Logger.getLogger(WebSocketUtil.class);
	
	public static void addSession(String keyName,WebSocketSession session) {
		ONLINE_SESSION.put(keyName, session);
	}
	
	public static void removeSession(String keyName) {
		ONLINE_SESSION.remove(keyName);
	}
	
	public static void sendMessage(WebSocketSession session,String message) {
		if(session==null) {
			return;
		}
		try {
			session.sendMessage(new TextMessage(message));
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
	public static void sendMessageToAll(String host, String message) {
		if(ONLINE_SESSION.isEmpty()) {
			return;
		}
		for (Entry<String, WebSocketSession> entry : ONLINE_SESSION.entrySet()) {
			if(entry.getValue().isOpen()) {
				String info = "用户[" + host + "]:" + message;
				String sendTime=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());
				String massageType="receiver";
				if(host.equals(entry.getKey())) {
					massageType="sender";
				}
				JSONObject infoMap=new JSONObject();
				infoMap.put("status", STATUS);
				infoMap.put("remark", info);
				infoMap.put("host", host);
				infoMap.put("total", getTotal());
				infoMap.put("message", message);
				infoMap.put("messageType", massageType);
				infoMap.put("sendTime", sendTime);
				sendMessage(entry.getValue(), infoMap.toString());
			}
		}
	}
	
	public static void notifyToAll(String host,String Message) {
		if(ONLINE_SESSION.isEmpty()) {
			return;
		}
		for (Entry<String, WebSocketSession> entry : ONLINE_SESSION.entrySet()) {
			if(entry.getValue().isOpen()) {
				JSONObject infoMap=new JSONObject();
				infoMap.put("status", STATUS);
				infoMap.put("remark", ONLINE_SESSION.keySet());
				infoMap.put("host", host);
				infoMap.put("total", WebSocketUtil.getTotal());
				infoMap.put("message", Message);
				infoMap.put("messageType", "notify");
				infoMap.put("sendTime", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
				sendMessage(entry.getValue(), infoMap.toString());
			}
		}
		
	}
	
	public static void sendImgToAll(String host,String fileName) {
		if(ONLINE_SESSION.isEmpty()) {
			return;
		}
		BinaryMessage byteMsg = null;
		File file=new File(fileName);
		InputStream in = null;
		int len=0;
		if(file.exists()) {
			try {
				in=new FileInputStream(fileName);
				byte[] bytes=new byte[(int) file.length()];
				len=in.read(bytes);
				logger.debug("the file size is "+len);
				byteMsg=new BinaryMessage(bytes);
				in.close();
				file.delete();
			} catch (Exception e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}finally {
				if(in!=null) {
					try {
						in.close();
					} catch (IOException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
			}
			
		}
		if(byteMsg==null) {
			return;
		}
		for (Entry<String, WebSocketSession> entry : ONLINE_SESSION.entrySet()) {
			if(entry.getValue().isOpen()) {
				String info = "image";
				String sendTime=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());
				String massageType="receiver";
				if(host.equals(entry.getKey())) {
					massageType="sender";
				}
				//ByteArrayInputStream blob=new ByteArrayInputStream(byteMsg.getPayload().array());
				JSONObject infoMap=new JSONObject();
				infoMap.put("status", STATUS);
				infoMap.put("remark", info);
				infoMap.put("host", host);
				infoMap.put("total", getTotal());
				infoMap.put("message", byteMsg.getPayload().array());
				infoMap.put("messageType", massageType);
				infoMap.put("sendTime", sendTime);
				sendMessage(entry.getValue(), infoMap.toString());
			}
		}
		
	}
	
	public static int getTotal() {
		return ONLINE_SESSION.size();
	}
}

前端boostrap+requireJs+templateJs实现界面渲染

webSocket.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>星空聊天室</title>
    <link rel="stylesheet" href="assets/css/bootstrap/bootstrap.min.css">
    <link rel="stylesheet" href="assets/css/webSocket.css">
</head>
<body>
<div class="content">
    <div class="page-header" id="tou">
       <span class="headerTop"> 聊天室</span>
        <div class="chartCount">当前在线人数<span class="num">0</span>人</div>
    </div>
    <div class="well" id="msg">
        <div class="chat-detail-bd">

        </div>
        <div class="nodata">
            暂无内容
        </div>
    </div>
    <div class="chat-list">
        <ul class="chats">

        </ul>
        <div class="nodata">
            暂无在线人员
        </div>
    </div>
    <div class="col-lg">
        <div class="input-group">
            <input type="text" class="form-control" placeholder="发送信息..." id="message">
            <span class="input-group-btn">
                <button class="btn btn-default" type="button" id="send">发送</button>
                <a class="img-upload"  type="button">
                   <!-- <img src="../assets/images/rec.png" alt="上传图片">-->图片
                </a>
            </span>
        </div>
        <div class="img-display" style="margin-top: 10px;display: none">
            <a href="#" class="thumbnail">
                <img src="" style="max-height: 90px;">
            </a>
        </div>
        <input type="file" style="display: none" class="img-file"/>
    </div>
</div>
<script src="assets/js/lib/jquery/1.8.3/jquery.js"></script>
<script src="assets/js/lib/bootstrap/3.2.0/bootstrap.js"></script>
<script src="assets/js/lib/template/template.js"></script>
<script src="assets/js/lib/require.js"></script>
<script src="assets/js/Websocket.js"></script>
<script>
    template.helper("startWith",function(v1,v2){
        return v1.startsWith(v2);
    })
</script>
<script id="userList" type="text/html">
    {{if data.messageType=='notify'}}
    {{each data.remark as value i}}
    <li class="each-chat">
        <span class="badge" style="font-size: 16px;">{{value}}</span>
    </li>
    {{/each}}
    {{/if}}
</script>
<script id="chartContext" type="text/html">
    {{if data.status=='200'}}
        {{if data.messageType=='receiver'}}
        <div class="detail-item-wrap clearfix">
            <div class="detail-item other">
                <i class="portrait"></i>
                <div class="item" type="text">
                    <div class="item-hd clearfix">
                        <span class="name">{{data.host}}</span>
                        <span class="time">{{data.sendTime}}</span>
                       <!-- <span class="detail">查看明细</span>-->
                    </div>
                    <div class="item-bd text">
                        {{if data.remark=='image'}}
                        <image style="width: auto;height: 200px;" src="{{data.message}}"></image>
                        {{else}}
                        <div class="text-line">{{data.message}}</div>
                        {{/if}}
                    </div>
                </div>
            </div>
        </div>
        {{/if}}
        {{if data.messageType=='sender'}}
        <div class="detail-item-wrap clearfix">
            <div class="detail-item me">
                <i class="portrait"></i>
                <div class="item" type="text">
                    <div class="item-hd clearfix">
                        <span class="name">{{data.host}}</span>
                        <span class="time">{{data.sendTime}}</span>
                     <!--   <span class="detail">查看明细</span>-->
                    </div>
                    <div class="item-bd text">
                        {{if data.remark=='image'}}
                        <image style="width: auto;height: 200px;" src="{{data.message}}"></image>
                        {{else}}
                        <div class="text-line">{{data.message}}</div>
                        {{/if}}
                    </div>
                </div>
            </div>
        </div>
        {{/if}}
        {{if data.messageType=='notify'}}
        <div class="detail-item-wrap clearfix">
            <div class="detail-item notify">
                <div class="text-line"><span style="color:#A63868;margin-right: 10px;">{{data.host}}</span>进入聊天室</div>
            </div>
        </div>
        {{/if}}
    {{/if}}
</script>
</body>
</html>

主要的js为:webSocket.js

define([], function () {
    var chartCount = 0;//内容计数器
    var totalCount = 100;//边界
    var websocket = null;
    var url = "10.0.18.25:8082/godway";
    var app = {
        init: function () {
            if ('WebSocket' in window) {
                websocket = new WebSocket("ws://10.0.18.25:8082/godway/sgchen");
            } else if ('MozWebSocket' in window) {
                websocket = new MozWebSocket("ws://sgchen");
            } else {
                websocket = new SockJS("http://" + url + "/ch/sgchen");
            }
            websocket.onopen = function () {
                $("#tou .headerTop").html("连接服务器成功!");
                $(".well .nodata").hide();
                $(".chat-list .nodata").hide();
            };
            websocket.onmessage = function (event) {
                var rspData = JSON.parse(event.data);
                if (rspData && rspData.remark=="image") {
                    //返回字节时
                    var url=URL.createObjectURL(app.base64ToBlob(rspData.message));
                    rspData.message=url;
                } 
                    app.setChartPersonTotal(rspData);
                    app.fillChartMessage(rspData);
                    app.fillOnlineMemu(rspData);
                $(".chat-detail-bd").animate({scrollTop:$(".chat-detail-bd").prop("scrollHeight")},400);
            };
            websocket.onerror = function (evnt) {
                console.log(evnt)
            };
            websocket.onclose = function (evnt) {
                $("#tou .headerTop").html("与服务器断开了连接!");
            }

            //监听窗口关闭事件,当窗口关闭时,主动去关闭websocket连接,防止连接还没断开就关闭窗口,server端会抛异常。
            //window.onbeforeunload = app.closeWebSocket();
            $('#send').bind('click', function () {
                app.sendTextMessage();
            });

            $("#message").keydown(function (event) {
                if (event.keyCode == 13) {
                    $('#send').click();
                }
            });

            //动态生成元素后的点击事件,jquery只有一次有效,所以调用live方法
            $(".img-file").live('change',function (event) {
                if(!app.imgIsValidate($(this)[0].files[0].name)){
                    alert("请选择图片类型");
                    return;
                }
                $(".img-display").show();
                var objectUrl=URL.createObjectURL($(this)[0].files[0]);
                $(".img-display .thumbnail img").attr("src",objectUrl);
                $(".img-display .thumbnail img").onload=function (ev) {
                    URL.revokeObjectURL(objectUrl);
                }

            });
            $(".img-upload").on('click', function (event) {
                $(".img-file").click();
            });
        },
        disconnect: function () {
            if (websocket != null) {
                websocket.close();
                websocket = null;
            }
        },

        reconnect: function () {
            if (websocket != null) {
                websocket.close();
                websocket = null;
            }
            if ('WebSocket' in window) {
                websocket = new WebSocket("ws://" + url + "/chat.sc");
            } else {
                websocket = new SockJS("http://" + url + "/sockjs/chat.sc");
            }
           /* websocket.onopen = app.onOpen();
            websocket.onmessage = app.onMesssage();
            websocket.onerror = app.onError();
            websocket.onclose = app.onClose();*/
        },
        //发送数据
        sendTextMessage: function () {
            if (websocket.readyState == 1) {  //0-CONNECTING;1-OPEN;2-CLOSING;3-CLOSED
                var message = document.getElementById('message').value;
                if (message) {
                    websocket.send(message);
                }
                document.getElementById('message').value = "";
                app.sendFile();
            } else {
                alert('未与服务器链接.');
            }
        },
        sendFile: function () {
            //如果图片存在先发送图片
            if ($(".img-display .thumbnail img").attr("src")) {
                var file=$(".img-file")[0].files[0];
                if(!file){
                    return;
                }
                if(!app.imgIsValidate(file.name)){
                    alert("你发送的不是图片");
                    return;
                }
                websocket.send(file.name+":fileStart");
                var reader=new FileReader();
                //以二进制形式读取文件
                reader.readAsArrayBuffer(file);
                //文件读取完毕后该函数响应
                reader.onload = function loaded(evt) {
                    var blob = evt.target.result;
                    //发送二进制表示的文件
                    websocket.send(blob);
                    websocket.send(file.name+":fileNoText");
                    //清空<input type="file">的值
                    var clearFile=$(".img-file")[0];
                    clearFile.outerHTML=clearFile.outerHTML;
                    clearFile.value="";
                }
                //发送完后
                $(".img-display").hide();
                //$(".img-display .thumbnail img").attr("src", "");
            }
        },
        //将消息显示在网页上
        fillChartMessage: function (rspData) {
            if(!rspData){
                return;
            }
            if (chartCount > totalCount) {
                $(".chat-detail-bd .detail-item-wrap:lt(41)").remove();
                chartCount = totalCount - 40;
            }
            var html = template('chartContext', {data: rspData});
            $("#msg .chat-detail-bd").append(html);
            chartCount++;
        },
        //设置人员总数
        setChartPersonTotal: function (rspData) {
            $(".page-header .chartCount .num").html(rspData.total);
        },

        //关闭WebSocket连接
        closeWebSocket: function () {
            websocket.close();
        },
        fillOnlineMemu: function (rspData) {
            if (rspData.messageType == "notify") {
                var html = template('userList', {data: rspData});
                $(".chat-list .chats").html(html);
            }
        },
        base64ToBlob:function(urlData){
            var binary=atob(urlData);
            var array=[];
            $.each(binary,function(i,val){
                array.push(binary.charCodeAt(i));
            })
            return new Blob([new Uint8Array(array),{type:"image/jpeg"}])
        },
        imgIsValidate:function(fileName){
            var fileType=fileName.substr(fileName.lastIndexOf(".")).toLowerCase();
            if(fileType!=".jpg" && fileType!=".jpeg" && fileType!=".png" && fileType!=".gif"){
                return false;
            }
            return true;
        }
    }
    return app.init()
})

服务器启动后项目界面的效果图为:


Java聊天对话功能 java聊天功能怎么做_Java聊天对话功能