WebSocket使用

WebSocket簡單使用

一、WebSocket單獨使用

1.創建WebSocket



import java.util.Date;

import javax.websocket.server.ServerEndpoint;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.DefaultFullHttpResponse;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.handler.codec.http.HttpVersion;
import io.netty.handler.codec.http.websocketx.CloseWebSocketFrame;
import io.netty.handler.codec.http.websocketx.PingWebSocketFrame;
import io.netty.handler.codec.http.websocketx.PongWebSocketFrame;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import io.netty.handler.codec.http.websocketx.WebSocketFrame;
import io.netty.handler.codec.http.websocketx.WebSocketServerHandshaker;
import io.netty.handler.codec.http.websocketx.WebSocketServerHandshakerFactory;
import io.netty.util.CharsetUtil;
@ServerEndpoint("/ws")
public class MyWebSocketHandler extends SimpleChannelInboundHandler<Object> {
	private WebSocketServerHandshaker handshaker;
	private static final String WEB_SOCKET_URL="ws://localhost:8080/start/websocket";
	

	//客户端與服務器創建連接的時候調用
	@Override
	public void channelActive(ChannelHandlerContext ctx) throws Exception {
		NettyConfig.group.add(ctx.channel());
		System.out.println("客戶端與服務端里連接開啟。。");
	}

	//客户端與服務器斷開連接的時候調用
	@Override
	public void channelInactive(ChannelHandlerContext ctx) throws Exception {
		NettyConfig.group.remove(ctx.channel());
		System.out.println("客戶端與服務端連接關閉");
	}

	//服務器接收客戶端信息發送過的數據結束之後調用
	@Override
	public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
		// TODO Auto-generated method stub
		super.channelReadComplete(ctx);
	}

	//工程出現異常時調用
	@Override
	public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
		cause.printStackTrace();
		ctx.close();
	}
	
	//服务端处理客户端websocket请求的核心方法
	@Override
	protected void messageReceived(ChannelHandlerContext context, Object msg) throws Exception {
		//處理客戶端向服務器發起http握手請求業務
		if(msg instanceof FullHttpRequest) {
			handHttpRequest(context, (FullHttpRequest)msg);
		}else if(msg instanceof WebSocketFrame) {//處理websocket連接業務
			handWebsocketFrame(context, (WebSocketFrame)msg);
		}
	}
	
	/**
	 * 處理客戶端與服務端之間的websocket業務
	 * @param context
	 * @param frame
	 */
	private void handWebsocketFrame(ChannelHandlerContext ctx,WebSocketFrame frame) {
		//判斷是否是關閉websocket的指令
		if(frame instanceof CloseWebSocketFrame) {
			handshaker.close(ctx.channel(), (CloseWebSocketFrame)frame.retain());
		}
		//判斷是否是ping消息
		if(frame instanceof PingWebSocketFrame) {
			ctx.channel().write(new PongWebSocketFrame(frame.content().retain()));
			return;
		}
		//判斷是否是二進制消息,如果是二進制消息,拋出異常
		if(!(frame instanceof TextWebSocketFrame)) {
			System.out.println("目前我們不支持二進制消息");
			throw new RuntimeException("【"+this.getClass().getName()+"】不支持消息");
		}
		//返回應答消息
		//獲取客戶端向服務器發送的消息
		String request=((TextWebSocketFrame)frame).text();
		System.out.println("服務器收到客戶端的消息=====>>>"+request);
		TextWebSocketFrame tws=new TextWebSocketFrame(new Date().toString()+ctx.channel().id()+"=====>>>"+request);
		//群發,服務器向每個鏈接上來的客戶端群發消息
		NettyConfig.group.writeAndFlush(tws);
	}
	
	/**
	 * 處理客戶端向服務器發起http握手請求業務
	 * @param ctx
	 * @param req
	 */
	private void handHttpRequest(ChannelHandlerContext ctx,FullHttpRequest req) {
	if(!req.decoderResult().isSuccess()||!("websocket".equals(req.headers().get("upgrad")))) {
		sendHttpResponse(ctx, req, new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.BAD_REQUEST));
		return;
	}
		WebSocketServerHandshakerFactory wsFactory=new WebSocketServerHandshakerFactory(WEB_SOCKET_URL, null, false);
		handshaker=wsFactory.newHandshaker(req);
		if(handshaker==null) {
			WebSocketServerHandshakerFactory.sendUnsupportedVersionResponse(ctx.channel());
		}else {
			handshaker.handshake(ctx.channel(), req);
		}
	}
	/**
	 * 服務端向客戶端相應消息
	 * @param ctx
	 * @param req
	 */
	private void sendHttpResponse(ChannelHandlerContext ctx,FullHttpRequest req,DefaultFullHttpResponse res) {
		if(res.status().code()!=200) {
			ByteBuf buf=Unpooled.copiedBuffer(res.status().toString(),CharsetUtil.UTF_8);
			res.content().writeBytes(buf);
			buf.release();
		}
		//服務端向客戶端發送數據
		ChannelFuture f=ctx.channel().writeAndFlush(res);
		if(res.status().code()!=200) {
			f.addListener(ChannelFutureListener.CLOSE);
		}
	}
}


2.初始化鏈接時候的各個組件


import io.netty.channel.ChannelInitializer;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpServerCodec;
import io.netty.handler.stream.ChunkedWriteHandler;

/**
 * 初始化鏈接時候的各個組件
 *
 */
public class MyWebSocketChannelhandler extends ChannelInitializer<SocketChannel> {

	@Override
	protected void initChannel(SocketChannel e) throws Exception {
		e.pipeline().addLast("http-code", new HttpServerCodec());
		e.pipeline().addLast("aggregator", new HttpObjectAggregator(65536));
		e.pipeline().addLast("http-chunked", new ChunkedWriteHandler());
		e.pipeline().addLast("handler", new MyWebSocketHandler());
	}

}

3.頁面

<html>
	<head>
		<meta http-equiv="Content-Type" content="text/html";charset="UTF-8"/>
		<title>WebSocket客戶端</title>
		
	</head>
	<boby>
		<form onSubmit="return false;">
			<input type="text" name="message" value=""/>
			<br/><br/>
			<input type="button" value="發送WebSocket請求消息" onclick="send(this.form.message.value)"/>
			<hr color="red"/>
			<h2>客戶端接收到服務端返回的應答消息</h2>
			<textarea id="responseContent" style="width:1024px height:300px"></textarea>
				
		</form>
	</boby>
	<script type="text/javascript">  
			var socket;
			if(!window.WebSocket){
				window.WebSocket=window.MozWebSocket;
			}
			if(window.WebSocket){
				Socket=new WebSocket("ws://localhost:8080/api/ws");//此處ip為服務端
				socket.onmessage=function(event){
					var ta=document.getElementById('responseContent');
					ta.value+=event.data+"\'r\n";
				};
				socket.onopen=function(event){
					var ta=document.getElementById('responseContent');
					ta.value="你當前的瀏覽器支出websocket,請進行後續操作\r\n";
				};
				socket.onclose=function(event){
					var ta=document.getElementById('responseContent');
					ta.value="WebSocket連接已經關閉\r\n";
				};
			}else{
				alert("你的瀏覽器不支持WebSocket");
			}
			
			function send(message){
				
					socket.send(message);
				
			}
		</script>
</html>

4.啟動類


import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;

@Controller
@RequestMapping("/start")
public class StartWebSocket {
	
	@RequestMapping("/webSocket.do")
	@ResponseBody
	public void startWebSocket() {
		EventLoopGroup bossGroup=new NioEventLoopGroup();
		EventLoopGroup workGroup=new NioEventLoopGroup();
		try {
			ServerBootstrap b=new ServerBootstrap();
			b.group(bossGroup,workGroup);
			b.channel(NioServerSocketChannel.class);
			b.childHandler(new MyWebSocketChannelhandler());
			System.out.println("服務端開啟等待客戶端連接。。");
			/*
			 * Channel ch=b.bind(8088).sync().channel(); ch.closeFuture().sync();
			 */
		}catch(Exception e) {
			e.printStackTrace();
		}finally {
			//退出程序
			bossGroup.shutdownGracefully();
			workGroup.shutdownGracefully();
		}
	}
	
}

如此就能前後端的簡單通訊。

二、WebSocket+tomcat+spring實現客戶端向服務端通信以及服務端主動向客戶端發消息

1.創建WebSocket服務端



import java.io.IOException;
import java.util.concurrent.ConcurrentHashMap;

import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;

import org.springframework.stereotype.Component;

/**
 * @ServerEndpoint 注解是一个类层次的注解,它的功能主要是将目前的类定义成一个websocket服务器端,
 * 注解的值将被用于监听用户连接的终端访问URL地址,客户端可以通过这个URL来连接到WebSocket服务器端
 */
@ServerEndpoint("/ws/{uuid}")
@Component
public class WSServer {
	    //静态变量,用来记录当前在线连接数。应该把它设计成线程安全的。  
	    private static int onlineCount = 0;  
	  //concurrent包的线程安全Set,用来存放每个客户端对应的MyWebSocket对象。若要实现服务端与单一客户端通信的话,可以使用Map来存放,其中Key可以为用户标识
	    private static ConcurrentHashMap<Session, WSServer> ssMap= new ConcurrentHashMap<Session, WSServer>();

	    //與某个客户端的连接会话,需要通过它来给客户端发送数据  
	    private Session session;  
	    
	    //客戶端用戶uuid
	    private String uuid="";
	    /** 
	     * 连接建立成功调用的方法 
	     * @param session  可选的参数。session为与某个客户端的连接会话,需要通过它来给客户端发送数据 
	     */  
	    @OnOpen  
	    public void onOpen(Session session,@PathParam("uuid") String uuid){  
            	this.session = session; 
    	        this.uuid=uuid;
    	        ssMap.put(session, this);
    	        addOnlineCount();           //在线数加1  
    	        System.out.println("有新连接加入!当前在线人数为" + getOnlineCount());  
	    }  

	    /** 
	     * 连接关闭调用的方法 
	     */  
	    @OnClose  
	    public void onClose(){  
	        ssMap.remove(this.session);
	        subOnlineCount();           //在线数减1  
	        System.out.println("有一连接关闭!当前在线人数为" + getOnlineCount());  
	    }  

	    /** 
	     * 收到客户端消息后调用的方法 
	     * @param message 客户端发送过来的消息 
	     * @param session 可选的参数 
	     */  
	    @OnMessage  
	    public void onMessage(String message, Session session) {  
	        System.out.println("来自客户端的消息:" + message); 
	        WSServer tmp = ssMap.get(session);
	        try {
	            tmp.sendMessage(message);
	        } catch (IOException e1) {
	            e1.printStackTrace();
	        }
	    }  

	    /** 
	     * 发生错误时调用 
	     * @param session 
	     * @param error 
	     */  
	    @OnError  
	    public void onError(Session session, Throwable error){  
	        System.out.println("发生错误");  
	        error.printStackTrace();  
	    }  

	    /** 
	     * 这个方法与上面几个方法不一样。没有用注解,是根据自己需要添加的方法。 
	     * @param message 
	     * @throws IOException 
	     */  
	    public void sendMessage(String message) throws IOException{  
	    	this.session.getBasicRemote().sendText(message);  
	    }  
	    /**
	     * 发送信息给指定ID用户,如果用户不在线则返回不在线信息给自己
	     * @param message
	     * @param id
	     * @throws IOException
	     */
	    public void sendtoUser(String message,String uuid) throws IOException {
	        if (ssMap.get(uuid) != null) {
	            if(!uuid.equals(uuid)) {
	            	ssMap.get(uuid).sendMessage("用户" + uuid + "发来消息:" + " <br/> " + message);
	            }else {
	            	ssMap.get(uuid).sendMessage(message);
	            }
	        } 
	    }
	    public static synchronized int getOnlineCount() {  
	        return onlineCount;  
	    }  

	    public static synchronized void addOnlineCount() {  
	        WSServer.onlineCount++;  
	    }  

	    public static synchronized void subOnlineCount() {  
	        WSServer.onlineCount--;  
	    }
	
}

2.創建客戶端

<html>  
<head>  
    <meta charset="UTF-8">  
    <title>Java后端WebSocket的Tomcat??</title>  
</head>  
<body>  
    Welcome<br/><input id="text" type="text"/>  
    <button onclick="send()">發送消息</button>  
    <hr/>  
    <button onclick="closeWebSocket()">關閉WebSocket連接</button>  
    <hr/>  
    <div id="message"></div>  
</body>  

<script type="text/javascript">  
    var websocket = null;  
    //判??前??器是否支持WebSocket  
    if ('WebSocket' in window) {  
		var uuid='2';
        //websocket = new WebSocket("ws://localhost:8080/web1/websocket");
        websocket = new WebSocket("ws://localhost:8080/api/ws/{uuid}");
    }  
    else {  
        alert('當前瀏覽器 Not support websocket')  
    }  

    //連接發生錯誤的回調方法  
    websocket.onerror = function () {  
        setMessageInnerHTML("WebSocket連接發生錯誤");  
    };  

    //連接成功建立的回調方法  
    websocket.onopen = function () {  
        setMessageInnerHTML("WebSocket連接成功");  
    }  

    //接收到消息的回調方法  
    websocket.onmessage = function (event) {  
        setMessageInnerHTML(event.data);  
    }  

    //連接關閉的回調方法  
    websocket.onclose = function () {  
        setMessageInnerHTML("WebSocket連接關閉");  
    }  

    //監聽窗口關閉事件,當窗口關閉時,主動去關閉websocket聯接,防止連接還沒斷開就關閉窗口,server端??异常。  
    window.onbeforeunload = function () {  
        closeWebSocket();  
    }  

    //將消息顯示在网頁上  
    function setMessageInnerHTML(innerHTML) {  
        document.getElementById('message').innerHTML += innerHTML + '<br/>';  
    }  

    //關閉WebSocket連接  
    function closeWebSocket() {  
        websocket.close();  
    }  

    //發送消息  
    function send() {  
        var message = document.getElementById('text').value;  
        websocket.send(message);  
    }  
</script>  
</html>  

3.服務端向客戶端傳送消息的調用


import java.io.IOException;

import javax.annotation.Resource;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;


/**
 * 
 *通過長連接服務端主動發消息給客戶端
 */
@Controller
@RequestMapping("/test")
public class test {
	@Resource
	WSServer wsServer;
	@RequestMapping("/send.do")
	public void send(String uuid) {
		 //如果访问的地址中msg参数不为空值,发送msg的值给前端
	try {
		String msg="來了老弟";
		wsServer.sendMessage(msg);
		wsServer.onClose();
		System.out.println(msg);
	} catch (IOException e) {
		e.printStackTrace();
	}
	}
}

以上僅供參考

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值