轮询与连接,websocket简单实现在线聊天

时间: 2019-08-13阅读: 188标签: socket轮询:

WebSocket简介与消息推送

B/S架构的系统多使用HTTP协议,HTTP协议的特点:

1 无状态协议
2 用于通过 Internet 发送请求消息和响应消息
3 使用端口接收和发送消息,默认为80端口
底层通信还是使用Socket完成。

图片 1

HTTP协议决定了服务器与客户端之间的连接方式,无法直接实现消息推送,一些变相的解决办法:

双向通信与消息推送

轮询:客户端定时向服务器发送Ajax请求,服务器接到请求后马上返回响应信息并关闭连接。优点:后端程序编写比较容易。缺点:请求中有大半是无用,浪费带宽和服务器资源。实例:适于小型应用。

长轮询轮询与连接,websocket简单实现在线聊天。:客户端向服务器发送Ajax请求,服务器接到请求后hold住连接,直到有新消息才返回响应信息并关闭连接,客户端处理完响应信息后再向服务器发送新的请求。优点:在无消息的情况下不会频繁的请求,耗费资小。缺点:服务器hold连接会消耗资源,返回数据顺序无保证,难于管理维护。Comet异步的ashx,实例:WebQQ、Hi网页版、Facebook IM。

长连接:在页面里嵌入一个隐蔵iframe,将这个隐蔵iframe的src属性设为对一个长连接的请求或是采用xhr请求,服务器端就能源源不断地往客户端输入数据。优点:消息即时到达,不发无用请求;管理起来也相对便。缺点:服务器维护一个长连接会增加开销。实例:Gmail聊天

Flash Socket:在页面中内嵌入一个使用了Socket类的 Flash 程序JavaScript通过调用此Flash程序提供的Socket接口与服务器端的Socket接口进行通信,JavaScript在收到服务器端传送的信息后控制页面的显示。优点:实现真正的即时通信,而不是伪即时。缺点:客户端必须安装Flash插件;非HTTP协议,无法自动穿越防火墙。实例:网络互动游戏。

Websocket:
WebSocket是HTML5开始提供的一种浏览器与服务器间进行全双工通讯的网络技术。依靠这种技术可以实现客户端和服务器端的长连接,双向实时通信。
特点:
事件驱动
异步
使用ws或者wss协议的客户端socket

能够实现真正意义上的推送功能

缺点:

少部分浏览器不支持,浏览器支持的程度与方式有区别。

图片 2

JavaEE 7中出了JSR-356:Java API for WebSocket规范。不少Web容器,如Tomcat,Nginx,Jetty等都支持WebSocket。

1. 轮询:客户端定时向服务器发送Ajax请求,服务器接到请求后马上返回响应信息并关闭连接。
  优点:后端程序编写比较容易。
  缺点:请求中有大半是无用,浪费带宽和服务器资源。
  实例:适于小型应用。
2. 长轮询:客户端向服务器发送Ajax请求,服务器接到请求后hold住连接,直到有新消息才返回响应信息并关闭连接,客户端处理完响应信息后再向服务器发送新的请求。
  优点:在无消息的情况下不会频繁的请求。
  缺点:服务器hold连接会消耗资源。
  实例:WebQQ、Hi网页版、Facebook IM。

客户端定时向服务器发送Ajax请求,服务器接到请求后马上返回响应信息并关闭连接。优点:后端程序编写比较容易。缺点:请求中有大半是无用,浪费带宽和服务器资源。(而每一次的 HTTP 请求和应答都带有完整的 HTTP 头信息,这就增加了每次传输的数据量)实例:适于小型应用。

一、下面demo使用jetty实现:

项目结构:

图片 3

java后台代码:

图片 4图片 5

package edu.nf.ws.server;import javax.websocket.OnClose;import javax.websocket.OnMessage;import javax.websocket.OnOpen;import javax.websocket.Session;import javax.websocket.server.PathParam;import javax.websocket.server.ServerEndpoint;import java.io.IOException;import java.util.Set;/** * @author wangl * @date 2018-12-05 * websocket服务端 */@ServerEndpoint("/chat/server/{userName}")public class ChatServer {    /**     * 当有客户端连接到服务端的时候就会调用这个方法     * session代表客户端和服务端的一个连接会话对象     * ,由容器负责创建和维护     */    @OnOpen    public void onOpen(Session session, @PathParam("userName") String userName){        System.out.println("有客户端连接..."+userName);        //将用户名保存到当前用户会话的属性中(有点类似作用域的概念)        session.getUserProperties().put("user", userName);    }    /**     * 客户端和服务器之间通信的方法,     * 服务端每当接收到客户端的消息就会调用这个方法     * ,注意:必须指定一个String类型的参数,表示接收到客户端的文本消息     */    @OnMessage    public void onMessage(String message, Session session) throws IOException{        System.out.println("接收消息..." + message);        //将消息发送给所有人        sendAllUser(message, session);    }    /**     * 当客户端关闭或者断开连接时,服务端会调用此方法     * @param session     */    @OnClose    public void opnClose(Session session) throws IOException{        System.out.println("客户端失去连接...");        //关闭会话        session.close();    }    private void sendAllUser(String message, Session session) throws IOException{        //获取所有人的会话对象        Set<Session> users = session.getOpenSessions();        //获取发送人        String sendUser = session.getUserProperties().get("user").toString();        //发送给所有人        for (Session user : users) {            user.getBasicRemote().sendText(sendUser + " : " + message);        }    }}

View Code

html代码:

图片 6图片 7

<!DOCTYPE html><html lang="en"><head>    <meta charset="UTF-8">    <title>Title</title></head><body>    <div id="loginDiv">    用户名:<input type="text" id="userName" name="userName"/>    <input type="button" id="login" value="login"/>    </div>    <div id="container" style="display: none">        <div id="content"></div>        <input type="text" name="msg" id="msg"/>        <input type="button" id="send" value="send"/>    </div><script src="js/jquery-3.3.1.min.js"></script><script>    $(function(){        var ws;        //登陆        $('#login').on('click',function(){            var userName = $('#userName').val();            //创建websocket对象并连接服务端             ws = new WebSocket('ws://localhost:8080/chat/server/' + userName);            //客户端打开连接时会回调此方法            /*ws.onopen = function(){                //...            }*/            //客户端关闭或断开连接时执行此方法            /*ws.onclose = function(){                //...            }*/             //接收服务端发送的消息             ws.onmessage = function{                $('#content').append(message.data + "<br>");             }             $('#loginDiv').css('display','none');             $('#container').css('display','block');        });        //发送消息        $('#send').on('click',function(){            var msg = $('#msg').val();            //发送消息            ws.send;        });    })</script></body></html>

View Code

图片 8


另外,对于长连接和socket连接也有区分:
1. 长连接:在页面里嵌入一个隐蔵iframe,将这个隐蔵iframe的src属性设为对一个长连接的请求,服务器端就能源源不断地往客户端输入数据。
  优点:消息即时到达,不发无用请求。
  缺点:服务器维护一个长连接会增加开销。
  实例:Gmail聊天
2. Flash Socket:在页面中内嵌入一个使用了Socket类的 Flash 程序JavaScript通过调用此Flash程序提供的Socket接口与服务器端的Socket接口进行通信,JavaScript在收到服务器端传送的信息后控制页面的显示。
  优点:实现真正的即时通信,而不是伪即时。
  缺点:客户端必须安装Flash插件;非HTTP协议,无法自动穿越防火墙。
  实例:网络互动游戏。以上是四种请求方式的介绍和优缺点比较。

长轮询:

二、spring+jetty实现

项目结构:

图片 9

pom 配置:

图片 10图片 11

<?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>edu.nf</groupId>    <artifactId>spring-ws</artifactId>    <version>1.0-SNAPSHOT</version>    <packaging>war</packaging>    <properties>        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>        <java.version>1.8</java.version>        <maven.compiler.source>1.8</maven.compiler.source>        <maven.compiler.target>1.8</maven.compiler.target>        <maven.compiler.compilerVersion>1.8</maven.compiler.compilerVersion>        <!-- spring版本 -->        <spring.version>5.1.1.RELEASE</spring.version>        <servlet.version>4.0.1</servlet.version>        <jackson.version>2.9.7</jackson.version>    </properties>    <!-- 添加依赖 -->    <dependencies>        <dependency>            <groupId>javax.servlet</groupId>            <artifactId>javax.servlet-api</artifactId>            <version>${servlet.version}</version>            <scope>provided</scope>        </dependency>        <dependency>            <groupId>org.springframework</groupId>            <artifactId>spring-webmvc</artifactId>            <version>${spring.version}</version>        </dependency>        <dependency>            <groupId>org.springframework</groupId>            <artifactId>spring-websocket</artifactId>            <version>${spring.version}</version>        </dependency>        <dependency>            <groupId>com.fasterxml.jackson.core</groupId>            <artifactId>jackson-databind</artifactId>            <version>${jackson.version}</version>        </dependency>    </dependencies>    <!-- war插件 -->    <build>        <plugins>            <plugin>                <groupId>org.apache.maven.plugins</groupId>                <artifactId>maven-war-plugin</artifactId>                <version>2.2</version>                <configuration>                    <warSourceDirectory>web</warSourceDirectory>                    <!-- 指定web.xml路径 -->                    <webXml>webWEB-INFweb.xml</webXml>                </configuration>            </plugin>        </plugins>    </build></project>

View Code

web.xml配置:

图片 12图片 13

<?xml version="1.0" encoding="UTF-8"?><web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"         version="4.0">        <servlet>        <servlet-name>dispatcher</servlet-name>        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>        <init-param>            <param-name>contextConfigLocation</param-name>            <param-value>classpath:dispatcher-servlet.xml</param-value>        </init-param>    </servlet>    <servlet-mapping>        <servlet-name>dispatcher</servlet-name>        <url-pattern>/</url-pattern>    </servlet-mapping>    </web-app>

View Code

ServerEndpointHandler

图片 14图片 15

package edu.nf.demo.websocket;import edu.nf.demo.entity.Users;import org.springframework.web.socket.CloseStatus;import org.springframework.web.socket.TextMessage;import org.springframework.web.socket.WebSocketSession;import org.springframework.web.socket.handler.TextWebSocketHandler;import java.util.Map;import java.util.concurrent.ConcurrentHashMap;/** * @author wangl * @date 2018-12-06 * websocket服务端 */public class ServerEndpointHandler extends TextWebSocketHandler {    /**     * 维护一个用户列表(key存放用户名,value存放每一个用户的WebSocketSession)     */    private static Map<String, WebSocketSession> users = new ConcurrentHashMap<>();    /**     * 客户端建立连接之后执行此方法     * @param session 每当客户端连接后,容器会为其创建一个Session对象,     *                这个对象在Spring中就是WebSocketSession     * @throws Exception     */    @Override    public void afterConnectionEstablished(WebSocketSession session) throws Exception {        System.out.println("客户端建立了连接...");        //获取用户名,getAttributes方法得到的是一个Map,        //这个map里面存放了握手拦截器将HttpSession作用域拷贝过去的数据        Users user = session.getAttributes().get("user");        //将用户的session保存到用户列表中        users.put(user.getUserName(), session);    }    /**     * 每当客户端发送消息时执行此方法(onmessage)     * @param session     * @param message TextMessage对象表示接收客户端的文本消息对象,     *                它的getPayload方法将获取具体消息内容     * @throws Exception     */    @Override    protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {        System.out.println("接收客户端消息..." + message.getPayload;        //获取用户名        Users sendUser = session.getAttributes().get("user");        //群发消息        for(String userName : users.keySet{            //重新构建一个TextMessage对象            TextMessage newMessage = new TextMessage(sendUser.getUserName() + " : " + message.getPayload;            //发送所有人            users.get.sendMessage(newMessage);        }    }    /**     * 哭护短关闭或断开连接时执行此方法     * @param session     * @param status     * @throws Exception     */    @Override    public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {        System.out.println("客户端断开连接...");        session.close();    }}

View Code

UserController

图片 16图片 17

package edu.nf.demo.controller;import edu.nf.demo.controller.vo.ResponseVO;import edu.nf.demo.entity.Users;import org.springframework.http.HttpStatus;import org.springframework.web.bind.annotation.PostMapping;import org.springframework.web.bind.annotation.RestController;import javax.servlet.http.HttpSession;/** * @author wangl * @date 2018-12-06 */@RestControllerpublic class UserController {    @PostMapping("/userLogin")    public ResponseVO login(Users user, HttpSession session){        //执行用户验证        //代码省略.......        //验证成功后将用户放入会话作用域        session.setAttribute("user", user);        ResponseVO vo = new ResponseVO();        vo.setCode(HttpStatus.OK.value;        vo.setData("index.html");        return vo;    }}

View Code

index.html(登录成功的聊天网页)

图片 18图片 19

<!DOCTYPE html><html lang="en"><head>    <meta charset="UTF-8">    <title>Title</title>    <script src="js/jquery-3.3.1.min.js"></script></head><body>  <div id="content"></div>  <input type="text" id="msg" name="msg"/>  <input type="button" value="send"/><script>    $(function () {       var ws = new WebSocket('ws://localhost:8080/websocket');       ws.onmessage = function  {           $('#content').append(event.data + '<br>');       }       $(':button').on('click',function () {           var msg = $('#msg').val();           ws.send;       });    })</script></body></html>

View Code

运行结果:

图片 20

原理参照博客:

客户端向服务器发送Ajax请求,服务器接到请求后hold住连接,直到有新消息才返回响应信息并关闭连接(或到了设定的超时时间关闭连接),客户端处理完响应信息后再向服务器发送新的请求。优点:在无消息的情况下不会频繁的请求,节省了网络流量,解决了服务端一直疲于接受请求的窘境缺点:服务器hold连接会消耗资源,需要同时维护多个线程,服务器所能承载的TCP连接数是有上限的,这种轮询很容易把连接数顶满。实例:WebQQ、Hi网页版、Facebook IM。

长连接:

在页面里嵌入一个隐蔵iframe,将这个隐蔵iframe的src属性设为对一个长连接的请求,服务器端就能源源不断地往客户端输入数据。优点:消息即时到达,不发无用请求。缺点:服务器维护一个长连接会增加开销。实例:Gmail聊天

Flash Socket:

在页面中内嵌入一个使用了Socket类的 Flash 程序JavaScript通过调用此Flash程序提供的Socket接口与服务器端的Socket接口进行通信,JavaScript在收到服务器端传送的信息后控制页面的显示。优点:实现真正的即时通信,而不是伪即时。缺点:客户端必须安装Flash插件;非HTTP协议,无法自动穿越防火墙。实例:网络互动游戏。

WebSocket:

WebSocket 协议本质上是一个基于 TCP 的协议。为了建立一个 WebSocket 连接,客户端浏览器首先要向服务器发起一个 HTTP 请求,这个请求和通常的 HTTP 请求不同,包含了一些附加头信息,其中附加头信息”Upgrade: WebSocket”表 明这是一个申请协议升级的 HTTP 请求,服务器端解析这些附加的头信息然后产生应答信息返回给客户端,客户端和服务器端的 WebSocket 连接就建立起来了,双方就可以通过这个连接通道自由的传递信息,并且这个连接会持续存在直到客户端或者服务器端的某一方主动的关闭连接。

本文由澳门威斯尼人平台登录发布于Web前端,转载请注明出处:轮询与连接,websocket简单实现在线聊天

相关阅读