WebSocket浅析(一):实现群聊功能

首先WebSocket打破了传统的web请求响应模式,实现管道式的实时通信,并且可以持续连接。

相对于传统 HTTP 每次请求-应答都需要客户端与服务端建立连接的模式,WebSocket 是类似 Socket 的 TCP 长连接的通讯模式,一旦 WebSocket 连接建立后,后续数据都以帧序列的形式传输。在客户端断开 WebSocket 连接或 Server 端断掉连接前,不需要客户端和服务端重新发起连接请求。在海量并发及客户端与服务器交互负载流量大的情况下,极大的节省了网络带宽资源的消耗,有明显的性能优势,且客户端发送和接受消息是在同一个持久连接上发起,实时性优势明显。

 

所需jar包:websocket-api.jar,tomcat从7.0版本开始支持WebSocket,并且已经包含了所需jar包

websocket在tomcat中的参考配置文件路径:

webapps\examples\WEB-INF\classes\websocket

 

webapps\examples\websocket

 

首先我们需要一个class去实现ServerApplicationConfig接口作为配置文件,此时必须重写两个方法,分别是getEndpointConfigs()、getAnnotatedEndpointClasses(),getEndPointConfigs 获取所有以接口方式配置的webSocket类,getAnnotatedEndpointClasses 扫描src下所有用@ServerEndPoint注解的类。通常我们使用注解扫描的方法去实现,毕竟方便吧,该方法需要传入一个一class作为元素的Set集合,该集合表示扫描到的类,并且返回这个Set集合

package com.asen.websocket.config;

import java.util.HashSet;
import java.util.Set;

import javax.websocket.Endpoint;
import javax.websocket.server.ServerApplicationConfig;
import javax.websocket.server.ServerEndpointConfig;

public class WebSocketConfig implements ServerApplicationConfig{

	@Override
	public Set<Class<?>> getAnnotatedEndpointClasses(Set<Class<?>> scanned) {
		
		Set<Class<?>> results = new HashSet<Class<?>>();
		
		for (Class<?> clazz : scanned) {
			if (clazz.getPackage().getName().startsWith("com.asen.websocket.")) {
				results.add(clazz);
			}
		}
		
		return results;
	}

	@Override
	public Set<ServerEndpointConfig> getEndpointConfigs(Set<Class<? extends Endpoint>> arg0) {
		// TODO Auto-generated method stub
		return null;
	}

}

 

此时我们需要一个类并添加@ServerEndpoint("/chat")注解,此时表示的请求路径为ws://IP地址:8080/工程名称/chat,注意这里用的是ws协议,并不是传统的http,在这里类中通常用到三个注解:@OnOpen这个注解下的类会在客户端发起请求的时候执行,@OnMessage注解下的类通常是在接收客户端发送数据到服务器的时候执行,@OnClose注解下的类通常是在客户端断开连接的时候执行。

 

常用的Api:

获取服务器发送的请求参数session.getQueryString();

向客户端发送数据session.getBasicRemote().sendText(msg);

package com.asen.websocket.service;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import javax.websocket.OnClose;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.ServerEndpoint;

import com.asen.websocket.entity.User;
import com.google.gson.Gson;

@ServerEndpoint("/chat")
public class SocketService {
	
	private Session session;
	private static String username;
	private static Set<SocketService> connections = new HashSet<SocketService>();
	private static List<String> usernames = new ArrayList<String>();
	
	/*
	 * websocket并不是单例的,每增加一名用户使用系统,就会创建一个websocket实例,
	 * 所以session这个全局变量有多份,不能用static修饰,
	 * 而广播消息的时候是根据session来判断发给谁的------------s.session.getBasicRemote().sendText(msg);
	 * 如果session用static修饰,那么消息只会发送给同一个人,并且当前系统有多少个用户,就会收到多少份相同的消息
	 */
	public SocketService(){
		System.out.println("创建了SocketServcie()实例");
	}

	@OnOpen
	public void connected(Session session) {
		this.session = session;
		connections.add(this);
		
		//获取服务器发送的请求参数
		String str = session.getQueryString();
		
		this.username = str.substring(str.indexOf("=") + 1);
		
		System.out.println(username + "开始使用本系统" + ", " + "sessionId为:" + session.getId());
		
		usernames.add(username);
		
		User user = new User();
		
		user.setNames(usernames);
		
		Gson gson = new Gson();
		
		broadcast(connections, gson.toJson(user));
	}
	
	/*
	 *服务器通过参数msg接收客户端发送过来的数据
	 */
	@OnMessage
	public void getMsg(Session session, String msg) {
		
		User user = new User();
		user.setDate(new Date().toLocaleString());
		user.setFrom(this.username);
		user.setSendMsg(msg);
		
		Gson gson = new Gson();
		
		broadcast(connections, gson.toJson(user));
	}
	
	@OnClose
	public void close(Session session){
		connections.remove(this);
		usernames.remove(this.username);
		
		User user = new User();
		
		user.setNames(usernames);
		
		Gson gson = new Gson();
		
		broadcast(connections, gson.toJson(user));
	}
	
	//广播消息
	public static void broadcast(Set<SocketService> sockets, String msg){
		for (SocketService s : sockets){
			try {
				//向客户端发送数据
				s.session.getBasicRemote().sendText(msg);
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}
}

 

java聊天室程序源码 2 需求分析 2.1 业务需求 1. 与聊天室成员一起聊天。 2. 可以与聊天室成员私聊。 3. 可以改变聊天内容风格。 4. 用户注册(含头像)、登录。 5. 服务器监控聊天内容。 6. 服务器过滤非法内容。 7. 服务器发送通知。 8. 服务器踢人。 9. 保存服务器日志。 10.保存用户聊天信息。 2.2 系统功能模块 2.2.1 服务器端 1.处理用户注册 2.处理用户登录 3.处理用户发送信息 4.处理用户得到信息 5.处理用户退出 2.2.2 客户端 1.用户注册界面及结果 2.用户登录界面及结果 3.用户发送信息界面及结果 4.用户得到信息界面及结果 5.用户退出界面及结果 2.3 性能需求 运行环境:Windows 9x、2000、xp、2003,Linux 必要环境:JDK 1.5 以上 硬件环境:CPU 400MHz以上,内存64MB以上 3.1.2 客户端结构 ChatClient.java 为客户端程序启动类,负责客户端的启动和退出。 Login.java 为客户端程序登录界面,负责用户帐号信息的验证与反馈。 Register.java 为客户端程序注册界面,负责用户帐号信息的注册验证与反馈。 ChatRoom.java 为客户端程序聊天室主界面,负责接收、发送聊天内容与服务器端的Connection.java 亲密合作。 Windowclose 为ChatRoom.java的内部类,负责监听聊天室界面的操作,当用户退出时返回给服务器信息。 Clock.java 为客户端程序的一个小程序,实现的一个石英钟功能。 3. 2 系统实现原理 当用户聊天时,将当前用户名、聊天对象、聊天内容、聊天语气和是否私聊进行封装,然后与服务器建立Socket连接,再用对象输出流包装Socket的输出流将聊天信息对象发送给服务器端 当用户发送聊天信息时,服务端将会收到客户端用Socket传输过来的聊天信息对象,然后将其强制转换为Chat对象,并将本次用户的聊天信息对象添加到聊天对象集Message中,以供所有聊天用户访问。 接收用户的聊天信息是由多线程技术实现的,因为客户端必须时时关注更新服务器上是否有最新消息,在本程序中设定的是3秒刷新服务器一次,如果间隔时间太短将会增加客户端与服务器端的通信负担,而间隔时间长就会让人感觉没有时效性,所以经过权衡后认为3秒最佳,因为每个用户都不可能在3秒内连续发送信息。 当每次用户接收到聊天信息后将会开始分析聊天信息然后将适合自己的信息人性化地显示在聊天信息界面上。 4.1.1 问题陈述 1.接受用户注册信息并保存在一个基于文件的对象型数据库。 2.能够允许注册过的用户登陆聊天界面并可以聊天。 3.能够接受私聊信息并发送给特定的用户。 4.服务器运行在自定义的端口上#1001。 5.服务器监控用户列表和用户聊天信息(私聊除外)。 6.服务器踢人,发送通知。 7.服务器保存日志。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值