精华内容
下载资源
问答
  • Servlet实现WebSocket的简单聊天室(二)

    千次阅读 2020-04-26 17:06:28
    >勿以恶小而为之,勿以善小而不为--------------------------刘备 > >劝诸君,多行善事积福报,莫作恶 主要内容: Servlet实现 WebSocket

    勿以恶小而为之,勿以善小而不为--------------------------刘备

    劝诸君,多行善事积福报,莫作恶

    上一章简单介绍了WebSocket的了解(一),如果没有看过,请观看上一章

    本文代码参考了李刚老师编写的 轻量级Java EE 企业应用实战(Struts2+Spring4+Hibernate 整合开发) 一书 中的"Servlet 3.0 的Web 模块支持" 章节代码。

    一. Servlet 实现 WebSocket

    Servlet 实现 WebSocket 需要 @ServerEndpoint 注解, 该注解可以实现Web Socket, 需要用 3.1 版本,Tomcat服务器最好是 8.0+ 版本。

    通过@ServerEndPoint 注解的Servlet 类中需要提供四个方法, 服务器连接时操作,服务器断开时操作,服务器接收到客户端消息时操作,异常错误操作 来分别对应前端浏览器的四个事件。 这四个方法,要想起作用,需要分别添加 @OnOpen, @OnClose, @OnMessage,@OnError 四个注解。

    即:

    • 被 @OnOpen 注解标识的方法,可以处理连接时操作
    • 被 @OnClose 注解标识的方法,可以处理 断开时操作
    • 被 @OnMessage 注解标识的方法, 可以处理接收消息时操作
    • 被 @OnError 注解标识的方法,可以处理异常错误时操作

    二. Servlet 实现 WebSocket 的详细开发步骤

    二.一 创建动态项目(Dynamic Web Project) WebSocket

    Servlet 版本号是 3.1, Tomcat 版本号是 8.0

    有图片

    有图片

    有图片

    有图片

    有图片

    二.二 服务器端代码

    创建 ChatEntPoint.java 类, 用 @ServerEndPoint 注解标记起来

    package com.yjl.socket;
    
    import java.io.IOException;
    import java.util.Set;
    import java.util.concurrent.CopyOnWriteArraySet;
    import java.util.concurrent.atomic.AtomicInteger;
    
    import javax.websocket.OnClose;
    import javax.websocket.OnError;
    import javax.websocket.OnMessage;
    import javax.websocket.OnOpen;
    import javax.websocket.Session;
    import javax.websocket.server.ServerEndpoint;
    
    
    //用注解  @ServerEndPoint @ServerEndPoint 
    @ServerEndpoint(value="/websocket/chat")
    public class ChatEntPoint {
    	
    	//用于构建用户名称,是前缀    
    	private static final String GUEST_PREFIX="访客";
    	
    	//定义后面的名称,是个不重复的索引值,从0开始。  AtomicInteger AtomicInteger 
    	private static final AtomicInteger connectionIds=new AtomicInteger(0);
    	
    	//定义Set 集合,用于存放客户端连接, 不重复
    	private static final Set<ChatEntPoint> clientSet=new CopyOnWriteArraySet<>();
    	
    	//显示的昵称
    	private String nickName;
    	
    	//session 对象,用于接收 
    	private Session session;
    	
    	//构造方法,初始化 每一个 ChatEntPoint 对象的  nickName,构建成 访问+数字的形式 
    	public ChatEntPoint(){
    		//后面生成一个不重复的数字 
    		nickName=GUEST_PREFIX+connectionIds.getAndIncrement();
    	}
    	
    	// OnOpen ,表示每一个客户端连接时的事件 
    	@OnOpen
    	public void start(Session session){
    		//单独接收一个客户端与服务器端的Session 
    		this.session=session;
    		//添加到这里面
    		clientSet.add(this);
    		
    		//设置消息
    		String message=String.format("【%s %s 】",nickName,"加入聊天室");
    		
    		//用于发送消息
    		broadcast(message);
    		
    	}
    	//OnClose 注解, 每一个关闭时的事件
    	@OnClose
    	public void end(){
    		//移除掉
    		clientSet.remove(this);
    		//格式化消息
    		String message=String.format("【%s %s】",nickName,"离开聊天室");
    		//发送消息
    		broadcast(message);
    	}
    	//OnMessage, 接收客户端发送过来的消息的事件  OnMessage OnMessage 
    	@OnMessage
    	public void incoming(String message){
    		String filteredMessage=String.format("【%s:%s】",nickName,filter(message));
    		//发送消息
    		broadcast(filteredMessage);
    	}
    	// 服务器端错误时的,事件处理
    	@OnError
    	public void onError(Throwable t) throws Throwable{
    		
    		System.out.println("WebScoket 服务端错误"+t.getMessage());
    	}
    	//发送消息的方法
    	private static void broadcast(String message){
    		//遍历每一个客户端
    		for(ChatEntPoint client:clientSet){
    			try{
    				//同步操作
    				synchronized(client){
    					//通过 session.getBasicRemote() .sendText() 发送消息  getBasicRemote(). sendText() 
    					client.session.getBasicRemote().sendText(message);
    				}
    			}catch(IOException e){
    				System.out.println("聊天错误,向客户端 "+client+"发送消息出现错误 ");
    				//移除这个不存在的客户端
    				clientSet.remove(client);
    				
    				try{
    					//关闭这个session 
    					client.session.close();
    				}catch(IOException e2){
    					e2.printStackTrace();
    				}
    				//发送消息, 也检测一下,是否还有其他死客户端
    				String msg=String.format("【%s %s 】",client.nickName,"已经断开连接");
    				broadcast(msg);
    			}
    		}
    	}
    	//格式化前端传递过来的消息
    	private static String filter(String message){
    		if(null==message){
    			return null;
    		}
    		//定义数据
    		char[] content=new char[message.length()];
    		
    		//往 char 数据里面放置数据  
    		message.getChars(0,message.length(), content, 0);
    		
    		//判断后,进行转换
    		StringBuilder result=new StringBuilder(content.length+50);
    		
    		for(int i=0;i<content.length;i++){
    			switch(content[i]){
    			case '<':{
    				result.append("&lt;");
    				break;
    			}
    			case '>':{
    				result.append("&gt;");
    				break;
    			}
    			case '&':{
    				result.append("&amp;");
    				break;
    			}
    			case '"':{
    				result.append("&quot;");
    				break;
    			}
    			default:{
    				result.append(content[i]);
    			}
    			}
    		}
    		return result.toString();
    	}
    	
     	
    }
    
    

    二.三 客户端代码

    在 index.jsp 页面,添加客户端代码

    <%@ page language="java" contentType="text/html; charset=UTF-8"
        pageEncoding="UTF-8"%>
    <!DOCTYPE html>
    <html>
    <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <title>主页</title>
    </head>
    <body>
    
    <script>
    
    //定义WebSocket 对象  new WebSocket () 协议是 ws 协议 
    // 注意 url 的地址 ,暂时是固化了 
    var webSocket=new WebSocket("ws://127.0.0.1:80/WebSocket/websocket/chat");
    
    //发送消息
    var sendMsg=function(){
    	//获取元素
    	var inputElement=document.getElementById("msg");
    	//调用  webSocket.send(text) 方法,发送给服务器端消息  
    	webSocket.send(inputElement.value);
    	//清空输入框
    	inputElement.value="";
    }
    
    //回车事件,发送消息 
    var send=function(event){
    	if(event.keyCode==13){
    		sendMsg();
    	}
    }
    
    //退出按钮事件
    var closeWS=function(){
    	//调用close 方法,进行关闭
    	webSocket.close();
    	
    	//清空消息
    	document.getElementById("show").innerHTML="";
    	//清空输入框内的消息
    	document.getElementById("msg").value="";
    }
    
    //webSocket.onopen 当连接时,绑定事件,避免出来未连接,就点击按钮.
    webSocket.onopen=function(){
    	//回车事件
    	document.getElementById('msg').onkeydown=send;
    	//发送按钮事件
    	document.getElementById("sendBn").onclick=sendMsg;
    	//退出事件
    	document.getElementById("closeBn").onclick=closeWS;
    	console.log("WebSocket 连接成功!!");
    }
    //接收消息 onmessage  
    webSocket.onmessage=function(event){
    	var show=document.getElementById("show");
    	//event.data 用于获取消息   event.data 用于获取消息,并且拼装
    	show.innerHTML+=event.data+"<br/>";
    	//滚动条处理
    	show.scrollTop=show.scrollHeight;
    }
    //webSocket 关闭 onclose 事件
    webSocket.onclose=function(){
    	//去掉事件
    	document.getElementById("msg").onkeydown=null;
    	document.getElementById("sendBn").onclick=null;
    	//退出
    	document.getElementById("closeBn").onclick=null;
    	//提示已经被关闭了
    	console.log("WebSocket 已经被关闭了!!");
    }
    
    
    
    </script>
    
    <!--展示的div -->
    <div style="width:600px;height:400px; overflow-y:auto;border:1px solid #333;" id="show">
    </div>
    <br/>
    <!-- 填入内容的框 -->
    <input type="text" size="80" id="msg" name="msg" placeholder="请输入聊天内容">
    <!-- 发送按钮框 -->
    <input type="button" value="发送" id="sendBn" name="sendBn">
    <br/>
    <br/>
    <!-- 退出登录按钮框 -->
    <input type="button" value="退出登录" id="closeBn" name="closeBn">
    
    </body>
    </html>
    

    二.四 重启服务器,验证 WebSocket

    操作1: 打开窗口1, 输入网址: http://localhost/WebSocket/

    窗口1显示:

    有图片

    操作2: 再打开一个窗口2, 输入网址: http://localhost/WebSocket/

    窗口2 显示:

    有图片

    此时,窗口1 显示:

    有图片

    操作3: 在窗口1发送消息, “你好,我是两个蝴蝶飞”,点击发送按钮

    此时,窗口1显示:
    有图片

    窗口2显示:

    有图片

    可以发现,自动推送消息到各个客户端。

    操作4: 再打开一个窗口3, 输入网址: http://localhost/WebSocket/

    窗口3显示:

    有图片

    并不会接收到他参与之前的消息记录,保证了消息的隐私性。

    窗口1 显示:

    有图片

    窗口2 显示:

    有图片

    操作5: 点击窗口2的 退出登录按钮,点击之后

    此时窗口2 显示:

    有图片

    窗口1显示:

    有图片

    窗口3 显示:

    有图片

    操作6: 直接点击 窗口3的 X ,直接关闭网页:

    窗口1 显示:

    有图片

    可以接收到 窗口3退出的事件

    窗口2 已经被关闭了,所以没有任何接收信息。
    在这里插入图片描述

    本章节代码链接为:

    
    链接:https://pan.baidu.com/s/1DIOzdoLIS-jhIwZKIq3sDA 
    提取码:txrs 
    

    谢谢您的观看!!!

    展开全文
  • websocket+servlet简单案例代码
  • webSocket 案例 原生使用servlet

    千次阅读 2016-04-01 16:32:19
    原生使用servlet创建WebSocket案例 注意问题:  1、在网上的案例中 一般会引入两个jar包 catalina.jar,websocket-api.jar 两个jar包但是在项目启动时会出现报错信息所以我做的如下案例将catalina.jar去掉了  2...
    原生使用servlet创建WebSocket案例 
    
    注意问题:
                    1、在网上的案例中 一般会引入两个jar包  catalina.jar, websocket-api.jar 两个jar包但是在项目启动时会出现报错信息所以我做的如下案例将catalina.jar去掉了
                    2、 catalina.jar, websocket-api.jar 其实这两个jar包在tomcat7.0.68中是存在的 所以在运行项目于时一定要引入tomcat中的jar文件否则项目就会报错,原因就是这两个jar对tomcat中其他的jar有依赖关        系,在项目启动时 就会找不到相关的jar
                    
    所以推测一下 在网上有一种说法是 在使用tomcat时要查看版本说是tomcat7.0以上 但是如果是以上的方法 可以尝试一下 不一定适用tomcat7.0以上 导入相关的jar包 应该也能实现 我就没有尝试 太懒了 ~
    其实  catalina.jar, websocket-api.jar 直接引入tomcat中也能使用 上面的就是记录一下 项目中遇到的问题 。留着以后发现问题。
    servlet端
    package com.webscoletdemo.action;
    import  java.io.IOException;
    import java.nio.ByteBuffer;
    import java.nio.CharBuffer;
    import java.util.ArrayList;
    import java.util.List;
    import javax.servlet.http.HttpServletRequest;
    import javax.xml.ws.WebServiceFeature;
    import org.apache.catalina.websocket.MessageInbound;
    import org.apache.catalina.websocket.WebSocketServlet;
    import org.apache.catalina.websocket.WsOutbound;

    public class WebScoketAction extends WebSocketServlet {  
        private static final long serialVersionUID = -4853540828121130946L;  
        private static List<MyMessageInbound> mmiList = new ArrayList<MyMessageInbound>();  

        @Override  
        protected MyMessageInbound createWebSocketInbound(String str,  
                HttpServletRequest request) {  
            request.getParameter("");       
            return new MyMessageInbound();  
        }  

        private class MyMessageInbound extends MessageInbound {  
            WsOutbound myoutbound;  
            //开启websocket
            public void onOpen(WsOutbound outbound) {  
                try {  
                    System.out.println("Open Client.");  
                    this.myoutbound = outbound;  
                    mmiList.add(this);  
                    outbound.writeTextMessage(CharBuffer.wrap("Hello!"));  
                } catch (IOException e) {  
                    e.printStackTrace();  
                }  
            }  

            //关闭websocket
            public void onClose(int status) {  
                System.out.println("Close Client.");  
                mmiList.remove(this);  
            }  
           //当服务器端收到信息的时候,就对所有的连接进行遍历并且把收到的信息发送给所有用户  
            public void onTextMessage(CharBuffer cb) throws IOException {  
                System.out.println("Accept Message : " + cb);  
                for (MyMessageInbound mmib : mmiList) {  
                    CharBuffer buffer = CharBuffer.wrap(cb);  
                    mmib.myoutbound.writeTextMessage(buffer);  
                    mmib.myoutbound.flush();  
                }  
            }  


            public void onBinaryMessage(ByteBuffer bb) throws IOException {  

            }  
        }  
    }  
    index.jsp jsp端
    <%@ page language="java" import="java.util.*" pageEncoding="utf-8"%>
    <%
    String path = request.getContextPath();
    String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
    %>

    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
    <html>
      <head>
        <base href="<%=basePath%>">

        <title>My JSP 'index.jsp' starting page</title>
        <meta http-equiv="pragma" content="no-cache">
        <meta http-equiv="cache-control" content="no-cache">
        <meta http-equiv="expires" content="0">    
        <meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
        <meta http-equiv="description" content="This is my page">
        <!--
        <link rel="stylesheet" type="text/css" href="styles.css">
        -->
        <script type="text/javascript">
    var socket = new WebSocket("ws://localhost:8080/WebSocketDemo/aa.do?aaa=11");
    socket.onopen = function(){
        //浏览器socket开始进入
        console.log("open");
        //向服务器端发送信息
        socket.send("open+hello");
    };
    socket.onmessage = function(evt)
    {
        console.log("message");
        console.log("onmessage:"+evt.data);
    };
    socket.onclose = function(evt){
        console.log("close");
        console.log("onclose:"+evt.data);
    };
    socket.onerror = function(evt)
    {
        console.log("error");
        console.log("onerror:"+evt.data);
    };
    function send()
    {
        //socket.send("sendMessage");
    }
    function closeWebSocket(){
         socket.close();
    }

    </script>
      </head>

      <body>
        This is my JSP page. <br>
         Welcome<br/>
       <button οnclick="send()">Send</button>    <button οnclick="closeWebSocket()">Close</button>
        <div id="message">
        </div>
      </body>
    </html>


    web.xml 端
    <?xml version="1.0" encoding="UTF-8"?>
    <web-app version="3.0" 
        xsi:schemaLocation=" http://java.sun.com/xml/ns/javaee 
      <display-name></display-name>    
      <welcome-file-list>
        <welcome-file>index.jsp</welcome-file>
      </welcome-file-list>
      <servlet>
        <servlet-name>aa</servlet-name>
        <servlet-class>com.webscoletdemo.action.WebScoketAction</servlet-class>
        <load-on-startup>1</load-on-startup>
      </servlet>
      <servlet-mapping>
        <servlet-name>aa</servlet-name>
        <url-pattern>*.do</url-pattern>
      </servlet-mapping>
    </web-app>

    lib:  websocket-api.jar
    展开全文
  • Servlet3.0的出现是servlet史上最大的变革,其中的许多新特性大大的简化了web应用的开发,为广大劳苦的程序员...Websocket实时通信 一、注解配置  Servlet3.0新规范顺应了时代的潮流,使用注解配置,取代混乱的web.

         Servlet3.0的出现是servlet史上最大的变革,其中的许多新特性大大的简化了web应用的开发,为广大劳苦的程序员减轻了压力,提高了web开发的效率。主要新特性有以下几个:

    • 引入注解配置
    • 支持web模块化开发
    • 程序异步处理
    • 改进文件上传API
    • 非阻塞式IO读取流
    • Websocket实时通信

    一、注解配置
         Servlet3.0新规范顺应了时代的潮流,使用注解配置,取代混乱的web.xml全局配置。在这之前我们在创建servlet,filter,listener时,都是在web.xml中配置。

    //创建一个servlet需要在web.xml中配置如下内容
    <servlet>
            <servlet-name>myFirstServlet</servlet-name>
            <servlet-class>Test.myServlet</servlet-class>
        </servlet>
        <servlet-mapping>
            <servlet-name>myFirstServlet</servlet-name>
            <url-pattern>/aaa</url-pattern>
        </servlet-mapping>
    //我们只使用一行代码完成servlet的配置
    @WebServlet(name = "myFirstServlet",urlPatterns = {"/aaaa"})
    
    public class myServlet extends HttpServlet {
    
        @Override
        public void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    
            RequestDispatcher rd =  req.getRequestDispatcher("/default.jsp");
            rd.forward(req,resp);
        }
    }

    关于filter和listener的注解配置方法和上述形式一样,在3.0新规范中主要提供了以下一些注解用于配置:

    • Websocket :用于配置socket
    • WebInitParam :用于配置初始化参数,往往和servlet和filter结合使用
    • WebListener :用于配置Listener
    • WebFilter :用于配置Filter
    • MultipartConfig :用于文件上传(后面会详细介绍)
    • 还有一些,暂时没有涉及,就不列举了

    二、Servlet3.0 Web模块化开发
         在这之前我们对于web应用中的各个Servlet,Filter,Listener都是需要在web.xml中进行配置,如果只是本项目中的各个点的配置,那倒还好,但是如果我们引入框架,是不是每个框架中的各种配置也是需要在我们的web.xml中配置?这无疑会导致我们唯一的web.xml中内容混乱。Servlet3.0新规范提出了模块化开发,也就是每个Servlet,Filter,Listener都可以有属于自己的配置文件,功能和web.xml一样,它只负责配置当前的servlet。然后我们只需要将配置文件和自己写的Servlet等内容打包成jar,引入到具体项目中即可。(就像我们想要使用了某个功能,引入了从网上下载的jar包到项目中)下面我们看如何使用,由于Servlet,Filter,Listener的配置类似,此处以Servlet为例作为演示:
         首先我们写一个servlet类:

    public class MyServlet extends HttpServlet {
    
        @Override
        public void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException{
            RequestDispatcher rd = req.getRequestDispatcher("/default.jsp");
            rd.forward(req,resp);
        }
    }

    然后我们创建一个web-fragment.xml文件,这就是属于此Servlet自己的配置文件,功能类似于Web.xml,只是这个是私有的。键入以下内容:

    <?xml version="1.0" encoding="UTF-8"?>
    <web-fragment 
        xmlns="http://java.sun.com/xml/ns/javaee"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="3.0"
        xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
        http://java.sun.com/xml/ns/javaee/web-fragment_3_0.xsd"
        metadata-complete="false">
    
        <servlet>
            <servlet-name>myServlet</servlet-name>
            <servlet-class>Test.MyServlet</servlet-class>
        </servlet>
    
        <servlet-mapping>
            <servlet-name>myServlet</servlet-name>
            <url-pattern>/index</url-pattern>
        </servlet-mapping> 
    </web-fragment>

    我们可以对比看出,web.xml文件和web-fragment.xml文件除了头部的不一样,一个是web-app,一个是web-fragment,别处几乎一样。我们创建的这个servlet主要负责拦截URL为index的请求,并转向default.jsp页面。

    接下来我们看如何打包jar,然后再次为我们项目使用。第一步,无论你是用javac命令还是用IDE编译,首先我们需要将此.java文件编译成class文件。在你的电脑的任意位置创建一个空文件夹,将编译后的class文件及其包复制进去,因为我们MyServlet在Test包下,此处我就是将Test文件夹复制进去(你们需要根据自己建立的文件进行操作)

    然后创建一个空文件夹,命名为META-INF,一定要这样命名,因为等我们把jar包引入到项目中之后,一旦web应用启动时,就会去我们引入的jar包的此文件夹下查找web-fragment.xml文件并加载,如果没有找到就不会加载,我们的配置也就不会生效。此时我们文件夹中的内容如下:

    这里写图片描述

    将刚刚写完的web-fragment.xml文件复制到META-INF下,然后我们将这两个文件夹压缩成zip格式,然后修改zip为jar即可(因为jar和zip的区别就在于jar中多了一个META-INF文件夹,如果我们已经手动添加了,那他们这两种格式就是一样了)

    这里写图片描述

    此处我们使用手动添加META-INF文件夹,然后压缩zip格式的形式来完成打包jar的工作,你也可以使用jdk自带jar命令来完成打包操作,效果是一样的。然后我们将此jar包复制到任意web应用的WEB-INF/lib下,这就是web应用的所有外部引入包所存放的地方。然后我们启动web容器:

    这里写图片描述

    结果如上,当我们请求index,拦截器拦截并调向default.jsp页面。这样我们就完成了通过引入外部的jar包而不需要做任何配置,使用了其功能。可能此例并没有很好的展示了这种模块化开发的优势,等到我们学到框架的时候就可以很直观的感受到这种方式的简洁,易于携带。

    三、异步处理
         在传统的servlet开发中,如果servlet调用了一个耗时很长的逻辑处理方法,那么此servlet必须待在原地等待方法调用结束,这是很低效的一种形式。servlet3.0提出了异步处理的概念,也就是释放了主程序,大大提高了运行效率。
         Servlet3.0中异步处理主要是通过接口AsyncContext来实现的,我们可以通过HttpServletRequest对象来过去该接口的实现对象。

    AsyncContext getAsyncContext();

    在使用异步处理之前,我们还需要配置指定当前的servlet是支持异步处理。有两种方法,第一种是在web.xml中配置

    <async-supported>true</async-supported>

    或者使用webservlet指定属性asyncSupported=true。下面用一个实例演示如何使用servlet的异步处理机制:

    @WebServlet(name = "myservlet",urlPatterns = "/index",asyncSupported = true)
    public class MyServlet extends HttpServlet {
        @Override
        public void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException{
    
            resp.setContentType("text/html;charset=UTF-8");
            PrintWriter writer =  resp.getWriter();
            writer.println("servlet 开始:"+new Date()+"<br />");
            writer.flush();
    
            AsyncContext asy = req.startAsync();
            asy.setTimeout(4000);
            asy.start(new MyInfo(asy));
    
            writer.println("servlet 结束:"+new Date()+"<br />");
            writer.flush();
        }
    }

    我们可以看到,这个servlet非常简单,截取URL为index的请求,首先打印启动时间,然后通过request的startAsync方法创建AsyncContext 对象,设置过期时间,启动异步处理。这个线程类代码如下:

    public class MyInfo extends Thread {
    
        private AsyncContext asyncContext;
    
        public MyInfo(AsyncContext as){
            this.asyncContext = as;
        }
        @Override
        public void run(){
            try {
                Thread.sleep(3000);
                PrintWriter pw = asyncContext.getResponse().getWriter();
                pw.println("hello walker:"+new Date()+"<br />");
                asyncContext.complete();
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    一个构造方法接受AsyncContext 对象,run方法中,先打印一句话然后结束异步调用。我们看看结果:

    这里写图片描述

    通过时间我们可以看到servlet开始和结束几乎同时,而我们的异步处理却相差三秒钟,正是我们sleep的三秒钟。虽然我们实现了在servlet中异步调用别的线程来处理一些逻辑,但是我们还是不能完全控制整个异步处理中的各个过程,比如何时开始,何时结束等。Servlet3.0中的AsyncListener接口提供了以下几个方法帮助我们监控整个过程:

    • onStartAsync(AsyncEvent event) :当异步调用开始时触发
    • onComplete(AsyncEvent event) :当异步完成时触发
    • onError(AsyncEvent event) :当异步调用出错的时候触发
    • onTimeout(AsyncEvent event):当异步调用超时时候触发

    想要实现监控异步调用,首先需要编写一个类继承自AsyncListener然后实现如上四个方法,之后这个类就是一个可以监控异步调用的监听器。

    public class MyAsyncListener implements AsyncListener {
    
        public void onComplete(AsyncEvent var1) throws IOException{
            System.out.println("异步调用结束了。。。");
        }
    
        public void onTimeout(AsyncEvent var1) throws IOException{
            System.out.println("异步调用超时了。。。");
        }
    
        public void onError(AsyncEvent var1) throws IOException{
            System.out.println("异步调用出错了。。。");
        }
    
        public void onStartAsync(AsyncEvent var1) throws IOException{
            System.out.println("异步调用开始了。。。");
        }
    }

    在我们的Servlet主程序中使用以下语句绑定此异步监听器:

    asy.addListener(new MyAsyncListener());

    此时异步处理的四个结点的动态,我们都是实时掌控的。但是需要注意一点的是:虽然理论上我们是可以监听四个状态的,但是其实异步开始这个事件我们是没法监听的,也就是异步开始的方法永远不会被触发,原因是在注册AsyncContext 的时候,已经开始了异步,然而我们却在注册之后才绑定监听器,自然是不能监听到异步开始这个事件的。

    四、文件上传API
         对于传统的文件上传,我们是需要借助于外部工具的,例如:common-fileupload等。自从servlet3.0新规范以来,改进了文件上传API。

    <body>
        <h1>这是index页面</h1>
        <form method="post" action="/submit" enctype="multipart/form-data">
              姓名:<input type="text" name="name" /><br /><br />
              头像:<input type="file" name="mFile" /><br /><br />
              <input type="submit" value="提交" />
        </form>
      </body>

    我们知道,在html中上传文件的表单用type=”file”来指定,这是一点,还有一点就是from标签的enctype属性,他指定了表单参数的编码方式,主要有以下三种:

    • application/form-data :这是enctype的默认值,指定了这个值就表名表单只会提交所有input标签中的value值,对于我们的文件,提交的就是文件名。
    • multipart/form-data:这种方式是将参数以二进制存储,上传文件的内容也会被封装成二进制流提交。
    • text/plain:这种方式主要用于发送邮件

    对于需要上传文件功能的我们自然选择第二个参数值,正如上述代码展示的一样。下面我们写一个servlet用于处理上传的信息。

    @WebServlet(name = "myServlet",urlPatterns = {"/submit"})
    @MultipartConfig   //处理文件上传的servlet需要配置此注解
    public class FileUpload extends HttpServlet {
    
        public void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException{
            resp.setContentType("text/html;charset=UTF-8");
            PrintWriter writer = resp.getWriter();
            Part part = req.getPart("mFile");
            writer.println("文件类型:"+part.getContentType()+"<br />");
            writer.println("文件名:"+part.getName()+"<br />");
            part.write("C:\\Users\\Administrator\\Desktop\\photo.jpg");
        }
    }

    在servlet3.0中采用Part接口来处理文件上传,可以通过HtppServletRequest的以下两个方法来获取此接口对象:

    Part getPart(String name);
    Collection<Part> getParts();

    一个part对应于我们一个文件上传域,也就是一个input类型为file的元素。part中有以下一些方法:

        String getContentType();   //返回文件类型,如image/png
    
        String getName();          //返回文件名
    
        String getSubmittedFileName();
    
        long getSize();          //返回文件的大小
    
        void write(String var1) throws IOException;   //将文件写入到服务器磁盘
    
        void delete() throws IOException;          //删除此文件
    
        String getHeader(String var1);            //获取指定文件名的值
    
        Collection<String> getHeaders(String var1); //获取指定文件名的所有的值
    
        Collection<String> getHeaderNames();   //获取所有Header 的name集合

    在上面的程序中,我们使用了其中一些方法。打印了文件类型,文件名,最后将文件保存到本地桌面上。下面是运行的结果截图:

    这里写图片描述
    这里写图片描述
    这里写图片描述

    综上就是关于文件上传API的基本使用情况,还有一些内容留待下篇。。

    展开全文
  • 最近研究了两种服务器推送技术,一种是**WebSocket**技术,一种是基于**Servlet3.0**实现的服务器异步推送技术。 1. WebSocket(聊天室) WebSocket客户端连接类(javaee-api-7.0.jar) import java.io....

    最近研究了两种服务器消息推送技术,一种是WebSocket技术,一种是基于Servlet3.0实现的服务器异步消息推送技术,具体实现如下:

    1. WebSocket(聊天室)

    WebSocket客户端连接类(javaee-api-7.0.jar)

    import java.io.IOException;
    import java.text.SimpleDateFormat;
    import java.util.Date;
    
    import javax.websocket.OnClose;
    import javax.websocket.OnError;
    import javax.websocket.OnMessage;
    import javax.websocket.OnOpen;
    import javax.websocket.Session;
    import javax.websocket.server.ServerEndpoint;
    
    /**
     * @ServerEndpoint 功能主要是将CharRoomServer 定义为websocket服务器端
     */
    @ServerEndpoint("/ws/charRoomServer")
    public class CharRoomServer {
    
    	// 会话,主要用于向客户端发送数据
    	private Session session;
    	
    	public Session getSession() {
    		return session;
    	}
    
    	/**
    	 * websocket连接
    	 * @param session
    	 */
    	@OnOpen
    	public void onOpen(Session session){
    		this.session = session;
    		SessionManager.add(this);     
    	}
    	
    	/**
    	 * websocket断开
    	 */
    	@OnClose
    	public void onClose(){
    		SessionManage.remove(this);
    	}
    
    	/**
    	 * websocket消息
    	 * @param message
    	 * @param session
    	 */
    	@OnMessage
    	public void onMessage(String message, Session session) {
    		Date date = new Date();
    		SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    		String messageFormat = "{\"date\":\"%s\",\"name\":\"%s\",\"content\":\"%s\"}";
    		String msg = String.format(messageFormat, sdf.format(date),
    				"客户端" + session.getId(), message);
    		// 广播
    		SessionManage.broadCast(msg);
    	}
    	
    	/**
    	 * websocket错误
    	 * @param session
    	 * @param error
    	 */
    	@OnError
    	public void onError(Session session, Throwable error){
    		error.printStackTrace();
    	}
    	
    	/**
    	 * 广播消息方法
    	 * @param message
    	 * @throws IOException
    	 */
    	public void sendMessage(String message) throws IOException{
    		this.session.getBasicRemote().sendText(message);
    	}
    
    }
    

    SessionManage类

    import java.io.IOException;
    import java.text.SimpleDateFormat;
    import java.util.ArrayList;
    import java.util.Collection;
    import java.util.Collections;
    import java.util.Date;
    
    public class SessionManage {
    
    	private static Collection<CharRoomServer> charRoomServers = Collections.synchronizedCollection(new ArrayList<CharRoomServer>());
    	
    	/**
    	 * 广播
    	 * @param msg
    	 */
    	public static void broadCast(String msg){
    		for (CharRoomServer charRoomServer : charRoomServers) {
    			try {
    				charRoomServer.sendMessage(msg);
    			} catch (IOException e) {
    				// TODO Auto-generated catch block
    				e.printStackTrace();
    			}
    		}
    	}
    	
    	/**
    	 * 新增客户端服务器
    	 * @param charRoomServer
    	 */
    	public static void add(CharRoomServer charRoomServer){
    		// 使用时间构造登录信息
    		Date date = new Date();
    		SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    		String messageFormat = "{\"date\":\"%s\",\"name\":\"系统\",\"content\":\"%s\"}";
    		String content = "欢迎客户端" + charRoomServer.getSession().getId();
    		String message = String.format(messageFormat, sdf.format(date), content);
    		// 广播
    		broadCast(message);
    		// 加入服务组
    		charRoomServers.add(charRoomServer);
    	}
    	
    	/**
    	 * 客户端服务器退出
    	 * @param charRoomServer
    	 */
    	public static void remove(CharRoomServer charRoomServer){
    		// 客户端服务器退出
    		charRoomServers.remove(charRoomServer);
    		// 使用时间构造退出信息
    		Date date = new Date();
    		SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    		String messageFormat = "{\"date\":\"%s\",\"name\":\"系统\",\"content\":\"%s\"}";
    		String name = "客户端" + charRoomServer.getSession().getId();
    		String message = String.format(messageFormat, sdf.format(date), name + "已退出");
    		// 广播
    		broadCast(message);
    	}
    	
    }
    
    

    2. Servlet3.0(SpringBoot:DeferredResult)

    Application 入口类

    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.scheduling.annotation.EnableAsync;
    import org.springframework.scheduling.annotation.EnableScheduling;
    
    @SpringBootApplication
    @EnableAsync
    @EnableScheduling
    public class Application {
    
    	public static void main(String[] args) {
    		SpringApplication.run(Application.class, args);
    	}
    
    }
    

    AsyncController异步控制器

    import org.springframework.scheduling.annotation.Scheduled;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    import org.springframework.web.context.request.async.DeferredResult;
    
    /**
     * 异步控制器
     */
    @RequestMapping("/async")
    @RestController
    public class AsyncController {
        private DeferredResult<String> deferredResult;
    
        @RequestMapping("/getDeferredResult")
        public DeferredResult<String> getDeferredResult() {
            deferredResult = new DeferredResult<String>();
            return deferredResult;
        }
    
        /**
         * 每2秒执行一次
         */
        @Scheduled(fixedDelay = 2000)
        public void refreshDeferredResult() {
            if (deferredResult != null) {
                String result = "getDeferredResult:" + System.currentTimeMillis();
                deferredResult.setResult(result);
            }
        }
    
        @RequestMapping("/getResult")
        public String getResult() {
            return "getResult";
        }
    }
    

    前端页面关键js代码

    function getDeferredResult() {
        $.ajax({
            url:"/async/getDeferredResult",
            type:"post",
            success:function(data){
                $("table").html(data);
                getDeferredResult();
            }
        });
    }
    getDeferredResult();
    

    pom文件配置

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
    
        <groupId>com.example.async</groupId>
        <artifactId>async</artifactId>
        <version>0.0.1-SNAPSHOT</version>
    
        <!-- 编码和版本 -->
        <properties>
            <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
            <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
            <java.version>1.8</java.version>
            <maven.compiler.plugin.version>3.7.0</maven.compiler.plugin.version>
        </properties>
    
        <parent>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-parent</artifactId>
            <version>1.5.3.RELEASE</version>
            <relativePath/> <!-- lookup parent from repository -->
        </parent>
    
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
        </dependencies>
    </project>
    
    展开全文
  • Java实现WebSocket

    2017-10-27 15:35:37
    Java现实WebSocket 无所不能的Java系列文章,涵盖了Java的思想,应用开发,设计模式,程序架构等,通过我的经验去诠释Java的强大。 说起Java,真的有点不知道从何说起。Java是一门全领域发展的语言,从基础的来讲有...
  • WebSocket + Vue 简单聊天的实现1. 后端大体结构2. 前提练习3. 监听 Netty 启动4. WebSocket 服务类4.1 WebSocketServer4.2 初始化器4.3 助手类4.3.1 POJO 类4.3.2 枚举类4.3.3 关联类5. 前端信息6. 浏览器展示 1. ...
  • 基于JSP+Servlet+Tomcat8.5+WebSocket实现的网页聊天室 一、准备好开发环境 注意:(Tomcat的版本最好是7.0以上,由于这是基于Tomcat内部集成的Websocket开发的网页聊天室,就不需要导入Websocket jar包了,如果是...
  • socket servlet websocket 概念 区别与联系

    千次阅读 2016-11-17 15:10:30
    狭义的Servlet是指Java语言实现的一个接口,广义的Servlet是指任何实现了这个Servlet接口的类,一般情况下,人们将Servlet理解为后者。 Servlet运行于支持Java的应用服务器中。从原理上讲,Servlet可以响应...
  • java实现websocket server/client

    千次阅读 2019-08-08 18:44:07
    最近在项目中有一个场景,在内网的应用需要接受外网应用的指令。...记录一下websocket server/client的java实现 一、websocket server @Configuration public class WebSocketConfig extends...
  • webSocket

    2019-12-12 15:25:47
    webSocket 1_首先创建动态Web项目 2_导入jar 3_创建BitCoinServer类 package com.kemeng.bitcoin; import java.io.IOException; import javax.websocket.OnClose; import javax.websocket.OnError; import ...
  • 利用Spring MVC实现WebSocket实时通信,可以集成到SSH项目中 配置的修改: 1.struts.xml,不拦截socket的请求,我的位置为:/pages/mess/gc <constant name="struts.action.excludePattern" value="/pages/mes/...
  • 有个类叫GenericServlet,是一个通用的,不特定于任何协议的Servlet,它实现Servlet接口。而HttpServlet继承于GenericServlet,因此HttpServlet也实现Servlet接口。 敲黑板 x2:在web.xml中配置启动...
  • 大三在校学生,学了点websocket的内容,当作笔记; 实现了群发,单独通信也自然会了,就是session.getBasicRemote().sendText(msg);这个语句 群发:就是把当前连接的人的session for循环发送 单独:就是接受发送...
  • 鄙人有个需求,基于websocket实现浏览器与服务器的通信。 由于之前没有使用过websocket,在网上大致搜索了一圈,寻找websocket的集成方案。 由于事物繁忙,不能再像以前那样每个步骤精细化截图,因此凭着记忆记录...
  • 如果不是很清楚WebSocket协议,可以参考这篇博客。 包结构 Servlet 3.1以上的版本制定了WebSocket的编程规范,位于包...javax.servlet.websocket.server下包含了创建和配置WebSocket服务端所需的注解、接口和类。 M...
  • Tomcat如何实现WebSocket

    千次阅读 2017-11-28 14:17:09
    Tomcat如何实现WebSocket 原创 2016年11月25日 10:06:20 标签:tomcat /websocket /html5 /ajax 8016 WebSocket协议属于HTML5标准,越来越多浏览器已经原生支持WebSocket,它能让...
  • 最近遇到一个需求,要求将websocket注册为一个服务在后台运行,持续接收推送。 使用该方法需要引入...首先将客户端注册为servlet,这样就可以实现在后台持续接收推送。 public class FaceSocketClientManag...
  • 基于spring boot + netty实现高性能的websocket
  • 使用原生的 javax.websocket 实现websocket

    千次阅读 2018-03-26 17:57:00
     在websocket中获取httpsession对象 /** * @Title:GetHttpSessionConfigurator.java * @Description:Comment for created Java file * @author 张颖辉 * @date 2018年3月8日下午6:48:00 * @version 1.0 *...
  • 在线聊天室一、项目简介后端技术项目需求数据库设计项目结构...websocketwebsocket是一个应用层的协议,使用websocket实现“消息推送” JDBC:实现数据库连接管理 json:解析和封装数据,使用Gson库 使用的开发工
  • 1、什么是WebSocketWebSocket 是一种自然的全双工、双向、单套接字连接。使用WebSocket,你的HTTP 请求变成打开WebSocket 连接(WebSocket 或者WebSocket over TLS(TransportLayer Security,传输层安全性,原...
  • java 实现websocket的两种方式

    千次阅读 2019-01-28 10:08:24
    package com.hc.config; import java.io.IOException; import java.util.concurrent.CopyOnWriteArraySet; ...import java.util.Random;...import javax.websocket.*;...import javax.websocket.server.ServerEndpoi
  • 昨天使用HTML5的websocket与Tomcat实现了多人聊天,那是最简单也是最基本的,其中注意的就是开发环境,要满足jdk1.7和tomcat8,当然了tom7 的7.063也行! 今天是国庆的最后一天,苦逼的加班,继续搞代码!令人欣慰的...
  • 使用websocket通信,客户端采用C#开发界面,服务端使用Java开发,最终实现Java服务端向C#客户端发送消息和文件,C#客户端实现语音广播的功能。 Java服务端设计 package servlet.websocket; import java.io....
  • WebSocket是JavaEE7新支持的: Javax.websocket.server包含注解,类,接口用于创建和配置服务端点 Javax.websocket包则包含服务端点和客户断电公用的注解,类,接口,异常 目录结构 一、Maven依赖 <!-- ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 11,786
精华内容 4,714
关键字:

servlet实现websocket