java-websocket即时通讯-聊天室

效果预览


有小伙伴需要源码,该文章中的代码年代久远,重新弄了一个seven-im,请移步 <seven-im,一个基于java springboot websocket的及时通讯(IM),在线聊天室、群聊、私聊>

聊天室截图

web即时通讯的解决方式


  • 轮询:简单粗暴,过多的请求,服务器表示压力很大

  • 长连接:发送请求到服务器,有消息时才返回,长时间占用资源

  • websocket:基于tcp新的协议,双向通信

    废话少说,上代码(部分关键代码)!

html

<style>
	#chatroom_container{width:600px;height:500px;margin:70px auto 0px auto;background:#fff;border-radius:4px;overflow:hidden;box-shadow:0px 0px 20px #666;}
	#east{float:right;width:130px;height:350px;}
	#center{float:left;width:469px;height:350px;box-sizing:border-box;padding:8px;border-right:1px solid #ddd;overflow-y:auto;}
	#south{width:100%;height:40px;border-top:1px solid #ddd;position:absolute;left:0;bottom:0;box-sizing:border-box;padding:10px;background:#fff;}
	#chat{width:100%;height:100%;box-sizing:border-box;}
	#sendButton{position:absolute;bottom:5px;right:6px;}
	.record{margin:6px 5px;}
	.onlineStaff{margin:3px 4px;padding:3px 4px;cursor:pointer;}
	.onlineStaff:hover{background:#f0f0f0;}
</style>

<div id="chatroom_container">
	<div id="north">交流平台</div>
	<div id="online"></div>
	<div id="center">
		<div id="console"></div>
	</div>
	<div id="east"></div>
	<div style="clear:both;"></div>
	<div id="south">
		<textarea id="chat" ></textarea>
		<div id="sendButton" onclick="Chat.sendMessage()">发送</div>
	</div>
</div>

js

var Chat = {};
Chat.socket = null;
Chat.connect = (function(host){
    if ('WebSocket' in window) {
        Chat.socket = new WebSocket(host);
    } else if ('MozWebSocket' in window) {
        Chat.socket = new MozWebSocket(host);
    } else {
        Console.log('Error: WebSocket is not supported by this browser.');
        return;
    }
    //websocket连接打开时
    Chat.socket.onopen = function () {
        document.getElementById('chat').onkeydown = function(event) {
            if (event.keyCode == 13) {
                Chat.sendMessage();
                event.returnValue = false;
                event.preventDefault();
            }
        };
    };
    //websocket连接关闭时
    Chat.socket.onclose = function () {
        document.getElementById('chat').onkeydown = null;
    };
    //websocket连接有消息时
    Chat.socket.onmessage = function (message) {
        var data = eval('(' + message.data + ')');
        //data即为后台发送的数据,在此根据数据内容进行判断属于Chat.info | Chat.self | Chat.other
    }
    
});
//初始化
Chat.initialize = function() {
    if (window.location.protocol == 'http:') {
        Chat.connect('ws://' + window.location.host + '/websocket/chat');
    } else {
        Chat.connect('wss://' + window.location.host + '/websocket/chat');
    }
};
//发送的方法
Chat.sendMessage = (function() {
    var message = document.getElementById('chat').value;
    if (message != '') {
        var data = {
    		'from':'',
    		'to':'',
    		'groupId':'',
    		'type':'',
    		'message':message
    	}//from,to,type为后期私聊,群里
    	var msg = JSON.stringify(data);
        Chat.socket.send(msg);
        document.getElementById('chat').value = '';
    }
});

var Console = {};//聊天主窗体
//负责展现系统提示,例如 小明进入
Console.info = (function(message) {
    var h = '<div style="text-align:center;color:#aca;margin:5px auto;">'+ message +'</div>';
    $('#center').append(h);
});
//负责展现自己发送的消息,显示在右边
Console.self=(function(data){
	var h = '';
	//将data解析为html片段
	$('#center').append(h);
});
//负责展现其他人发送的消息,显示在左边
Console.other=(function(data){
	var h = '';
	//将data解析为html片段
	$('#center').append(h);
});

Chat.initialize();

java后台

  • 核心处理类ChatWebSocket.java,负责websocket连接/关闭/收发消息的整个过程
  • 消息类WebSocketMessage.java,发送消息的载体
  • 消息处理类ServerEncoder.java,将消息进行编码转json设置
  • 配置类GetHttpSessionConfigurator.java,获取httpSession
ChatWebSocket.java

websocket连接/关闭/收发消息的整个过程

@ServerEndpoint(value = "/websocket/chat", configurator = GetHttpSessionConfigurator.class, encoders = ServerEncoder.class)
public class ChatWebSocket {
    //存储所有的websocket连接
    private static final Map<String, ChatWebSocket> CONNECTIONS = new LinkedHashMap<String, ChatWebSocket>();
	private User user;
	private Session session;
    
    /**
    * 当websocket握手成功时触发
    * 该方法参数是可选的,session指的是websocket的session,不是httpSession.
    * 获取httpSession需要通过EndpointConfig类,而EndpointConfig类是由@ServerEndpoin()中的configurator进行设置
    */
    @OnOpen
    public void start(Session session, EndpointConfig config){
        this.session = session;            
        HttpSession httpSession = (HttpSession) config.getUserProperties().get(HttpSession.class.getName());
        WebSocketMessage webSocketMessage = new WebSocketMessage();
        if(httpSession == null){
			webSocketMessage.setMessage("您未登录系统");
			ChatWebSocket.sendToOne(this,webSocketMessage);
			try {
				this.session.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}else{
		    //获取httpSession中登录的用户
		    User user = (User) httpSession.getAttribute("currentUser");
		    if(user == null){
				webSocketMessage.setMessage("登录错误,未知用户");
				ChatWebSocket.sendToOne(this,webSocketMessage);
				try {
					this.session.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}else{
				this.user = user;
				CONNECTIONS.put(this.user.getId(), this);
				webSocketMessage.setUserId(this.user.getId());
				ChatWebSocket.sendToOne(this,webSocketMessage);
				String msg = this.user.getName() + " 进入";
				ChatWebSocket.broadcastOnline(msg);
			}
		}
    }
    
    //当websocket握手关闭时触发
    @OnClose
    public void end(){
        CONNECTIONS.remove(this.user.getId());
		String msg = this.user.getName() + " 离开";
		ChatWebSocket.broadcastOnline(msg);
    }
    
    //当收到消息时
    @OnMessage
    public void incoming(String message){
        WebSocketMessage webSocketMessage = new WebSocketMessage();
		webSocketMessage.setUserId(this.user.getId());
		webSocketMessage.setUserName(this.user.getName());
		webSocketMessage.setMessage(message);
		webSocketMessage.setSendDate(new Date());
		ChatWebSocket.broadcast(webSocketMessage);
    }
    
    //发送至单独用户
    private static void sendToOne(ChatWebSocket chatWebSocket,
			WebSocketMessage webSocketMessage) {
		chatWebSocket.session.getAsyncRemote().sendObject(webSocketMessage);
	}
    
    //广播
    private static void broadcast(WebSocketMessage webSocketMessage) {
        Set<String> keys = CONNECTIONS.keySet();
        for (String key : keys) {
			ChatWebSocket chatWebSocket = CONNECTIONS.get(key);
			synchronized (chatWebSocket) {
				ChatWebSocket.sendToOne(chatWebSocket, webSocketMessage);
			}
		}
    }
    
    //广播在线用户
    private static void broadcastOnline(String msg) {
		Set<String> keys = CONNECTIONS.keySet();
		WebSocketMessage webSocketMessage = new WebSocketMessage();
		webSocketMessage.setType(WebSocketMessage.WEBSOCKETMESSAGE_TYPE_ALL);
		webSocketMessage.setMessage(msg);
		List<UserAO> userList = new ArrayList<UserAO>();
		for (String key : keys) {
			ChatWebSocket chatWebSocket = CONNECTIONS.get(key);
			userList.add(chatWebSocket.user);
		}
		webSocketMessage.getAttributes().put("user", userList);
		for (String key : keys) {
			ChatWebSocket chatWebSocket = CONNECTIONS.get(key);
			synchronized (chatWebSocket) {
				ChatWebSocket.sendToOne(chatWebSocket, webSocketMessage);
			}
		}
	}
    
}
WebSocketMessage.java

发送消息的载体

public class WebSocketMessage {
    private String userId;
	private String userName;
	private string from;
	private string to;
	private string groupId;
	...
	private String message;
	private Date sendDate;
	private String type;
	private Map<String,Object> attributes = new HashMap<String, Object>();
	
	public WebSocketMessage(){
		super();
	}
	
	public WebSocketMessage(String userName,String message,Date sendDate) {
		this.userName = userName;
		this.sendDate = sendDate;
		this.message = message;
	}
	
	//getter and setter ...
}
ServerEncoder.java

将消息进行编码转json设置

public class ServerEncoder implements Encoder.Text<WebSocketMessage> {
	@Override
	public void init(EndpointConfig paramEndpointConfig) {
		// TODO Auto-generated method stub
	}
	
	@Override
	public void destroy() {
		// TODO Auto-generated method stub
	}
	
	@Override
	public String encode(WebSocketMessage paramT) throws EncodeException {
	    //将WebSocketMessage转为json
		return JsonMapper.buildNormalMapper().toJson(paramT);
	}
}
GetHttpSessionConfigurator

获取httpSession

public class GetHttpSessionConfigurator extends Configurator {
	@Override
	public void modifyHandshake(ServerEndpointConfig sec,
			HandshakeRequest request, HandshakeResponse response) {
		HttpSession httpSession=(HttpSession) request.getHttpSession();
		if(httpSession!=null){
			sec.getUserProperties().put(HttpSession.class.getName(),httpSession);
		}        
	}
	
}

以上只是部分关键代码,简单的实现了即时通讯,若需要私聊,群聊,分组等功能,则需改造WebSocketMessage.java和前端js中Chat.socket.onmessage()

有小伙伴需要源码,该文章中的代码年代久远,重新弄了一个seven-im,请移步 <seven-im,一个基于java springboot websocket的及时通讯(IM),在线聊天室、群聊、私聊>

本聊天系统采用客户机/服务器(C/S)地模式来设计,是一个3层地C/S结构:数据库服务器→应用程序服务器端→应用程序客户端,其分层结构如下图所示。系统采用C/S结构,可以将任务合理分配到客户机端和服务器端,从而降低了系统的通讯开销。 1. 客户层 客户层也叫应用表示层,是应用程序地客户接口部分。给聊天工具设计一个客户层具用很多优点,这是因为客户层担负着用户与应用间地对话功能。它用于检查用户的输入数据,显示应用的输出数据。为了使用户能直观的进行操作,客户层需要使用接口。若聊天用户变更,系统只需改写显示控制和数据检查程序即可,而不影响其他两层。数据检查的内容限于数据的形式和值得范围,不包括有关业务的处理逻辑。 2. 服务层 服务层又叫功能层,相当于应用的本体,他是讲具体的业务出路逻辑编入程序中。例如,用户需要检索数据,系统没法将有关检索要求的信息一次性的传送给功能层:而用户登陆后,聊天登录信息是由功能层处理过的检索结果数据,他也是一次性传送给表示层的。在应用设计中,不许避免在表示层和功能层之间进行多次的数据交换,这就需要尽可能进行一次性的业务处理,达到优化整体设计的目的。 3. 数据层 数据层就是DBMS,本聊天工具使用了Microsoft公司的SQL Server2000能迅速执行大量的更新和检索,因此,从功能层传送到数据层的“要求”一般都使用SQL语言。
评论 15
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值