WebSocket初探

WebSocket初探

大家好,又见面了,我是全栈君,祝每个程序员都可以多学几门语言。

       众所周知,socket是编写网络通信应用的基本技术,网络数据交换大多直接或间接通过socket进行。对于直接使用socket的client与服务端,一旦连接被建立则均可主动向对方传送数据,而对于使用更上层的HTTP/HTTPS协议的应用,因为它们是非连接协议,所以通常仅仅能由client主动向服务端发送请求才干获得服务端的响应并取得相关的数据。而当前越来越多的应用希望可以及时获取服务端提供的数据,甚至希望可以达到接近实时的数据交换(比如非常多站点提供的在线客户系统)。为达到此目的,通常採用的技术主要有轮询、长轮询、流等,而伴随着HTML5的出现,相对更优异的WebSocket方案也应运而生。

一、            非WebSocket方案简单介绍

1.      轮询

       轮询是由client定时向服务端发起查询数据的请求的一种实现方式。早期的轮询是通过不断自己主动刷新页面而实现的(在那个基本是IE统治浏览器的时代,那不断刷新页面产生的噪声就难以让人忍受),后来随着技术的发展,特别是Ajax技术的出现,实现了无刷新更新数据。但本质上这些方式均是client定时轮询服务端,这样的方式的最显著的缺点是假设client数量庞大而且定时轮询间隔较短服务端将承受响应这些client海量请求的巨大的压力。

2.      长轮询

       在数据更新不够频繁的情况下,使用轮询方法获取数据时client常常会得到没有数据的响应,显然这样的轮询是一个浪费网络资源的无效的轮询。长轮询则是针对普通轮询的这样的缺陷的一种改进方案,其详细实现方式是假设当前请求没有数据能够返回,则继续保持当前请求的网络连接状态,直到服务端有数据能够返回或者连接超时。长轮询通过这样的方式降低了client与服务端交互的次数,避免了一些无谓的网络连接。可是假设数据变更较为频繁,则长轮询方式与普通轮询在性能上并无显著差异。同一时候,添加�连接的等待时间,往往意味着并发性能的下降。

3.      流

      所谓流是指client在页面之下向服务端发起一个长连接请求,服务端收到这个请求后响应它并不断更新连接状态,以确保这个连接在client与服务端之间一直有效。服务端能够通过这个连接将数据主动推送到client。显然,这样的方案实现起来相对照较麻烦,并且可能被防火墙阻断。

二、            WebSocket简单介绍

1.      WebSocket协议简单介绍

       WebSocket是为解决client与服务端实时通信而产生的技术。其本质是先通过HTTP/HTTPS协议进行握手后创建一个用于交换数据的TCP连接,此后服务端与client通过此TCP连接进行实时通信。

WebSocket规范当前还没有正式版本号,草案变化也较为迅速。Tomcat7(本文中的例程来自7.0.42)当前支持RFC 6455(http://tools.ietf.org/html/rfc6455)定义的WebSocket,而RFC 6455眼下还未冻结,将来可能会修复一些Bug,甚至协议本身也可能会产生一些变化。

        RFC6455定义的WebSocket协议由握手和传输数据两个部分组成。

    来自client的握手信息类似例如以下:

GET /chat HTTP/1.1
Host: server.example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Origin: http://example.com
Sec-WebSocket-Protocol: chat, superchat
Sec-WebSocket-Version: 13

        服务端的握手信息类似例如以下:

HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
Sec-WebSocket-Protocol: chat


 

        一旦client和服务端都发送了握手信息而且成功握手,则传输数据部分将開始。传输数据对client和服务端而言都是一个双工通信通道,client和服务端来回传递的数据称之为“消息”。

client通过WebSocket URI发起WebSocket连接,WebSocket URIs模式定义例如以下:

 

ws-URI = "ws:" "//" host [ ":" port ] path [ "?" query ]
wss-URI = "wss:" "//" host [ ":" port ] path [ "?" query ]


 

        ws是普通的WebSocket通信协议,而wss是安全的WebSocket通信协议(就像HTTPHTTPS之间的差异一样)。在缺省情况下,ws的port是80wss的port是443

        关于WebSocke协议规范的完整详尽说明,请參考RFC 6455

2.      Tomcat7提供的WebSocket包简单介绍

        Tomcat7提供的与WebSocket相关的类均位于包org.apache.catalina.websocket之中(包org.apache.catalina.websocket的实现包括于文件catalina.jar之中),它包括有类Constants、MessageInbound、StreamInbound、WebSocketServlet、WsFrame、WsHttpServletRequestWrapper、WsInputStream、WsOutbound。这些类的关系如图 1所看到的。

 

WebSocket初探

                                                                 图1

        包org.apache.catalina.websocket中的这些类为WebSocket开发服务端提供了支持,这些类的主要功能简述例如以下:

        Constants:包org.apache.catalina.websocket中用到的常数定义在这个类中,它仅仅包括静态常数定义,无不论什么逻辑实现。

        MessageInbound:基于消息的WebSocket实现类(带内消息),应用程序应当扩展这个类并实现其抽象方法onBinaryMessageonTextMessage

       StreamInbound:基于流的WebSocket实现类(带内流),应用程序应当扩展这个类并实现其抽象方法onBinaryDataonTextData

       WebSocketServlet:提供遵循RFC6455WebSocket连接的Servlet基本实现。client使用WebSocket连接服务端时,须要将WebSocketServlet的子类作为连接入口。同一时候,该子类应当实现WebSocketServlet的抽象方法createWebSocketInbound,以便创建一个inbound实例(MessageInboundStreamInbound)

       WsFrame:代表完整的WebSocket框架。

       WsHttpServletRequestWrapper:包装过的HttpServletRequest对象。

       WsInputStream:基于WebSocket框架底层的socket的输入流。

       WsOutbound:提供发送消息到client的功能。它提供的全部向client的写方法都是同步的,能够防止多线程同一时候向client写入数据。

三、            基于Tomcat7WebSocket例程

        利用当前HTML5Tomcat7WebSocket提供的支持,基本仅仅须要编写简单的代码对不同的事件做对应的逻辑处理就能够实现利用WebSocket进行实时通信了。

        Tomcat7WebSocket提供了3个例程(echochatsnake),下面就当中的echochat分别做一简要解析。

1.      echo例程

        echo例程主要演示下面功能:client连接服务端、client向服务端发送消息、服务端收到client发送的消息后将其原样返回给client、client收到消息后将其显示在网页之上。

       在client页面选择streamsmessages作为“Connectusing”,然后点击“Connect”button,能够在右側窗体看到WebSocket连接打开的消息。随后点击“Echo message”button,client将向服务端发送一条消息,在右側窗体,能够看到,消息发出的后的瞬间,client已经收到了服务端原样返回的消息。

        client页面及执行效果截图如图 2所看到的。

WebSocket初探

                                                                                                    图2

       client实现上述功能的核心脚本例如以下,其关键点通过凝视的形式加以说明:

<script type="text/javascript">
        var ws = null;
        // 界面元素可用性控制
        function setConnected(connected) {
            document.getElementById('connect').disabled = connected;
            document.getElementById('disconnect').disabled = !connected;
            document.getElementById('echo').disabled = !connected;
        }

        function connect() {
            // 取得WebSocket连接入口(WebSocket URI)
            var target = document.getElementById('target').value;
            if (target == '') {
                alert('Please select server side connection implementation.');
                return;
            }
            // 创建WebSocket
            if ('WebSocket' in window) {
                ws = new WebSocket(target);
            } else if ('MozWebSocket' in window) {
                ws = new MozWebSocket(target);
            } else {
                alert('WebSocket is not supported by this browser.');
                return;
            }
            // 定义Open事件处理函数
            ws.onopen = function () {
                setConnected(true);
                log('Info: WebSocket connection opened.');
            };
            // 定义Message事件处理函数(收取服务端消息并处理)
            ws.onmessage = function (event) {
                log('Received: ' + event.data);
            };
            // 定义Close事件处理函数
            ws.onclose = function () {
                setConnected(false);
                log('Info: WebSocket connection closed.');
            };
        }
        // 关闭WebSocket连接
        function disconnect() {
            if (ws != null) {
                ws.close();
                ws = null;
            }
            setConnected(false);
        }

        function echo() {
            if (ws != null) {
                var message = document.getElementById('message').value;
                log('Sent: ' + message);
                // 向服务端发送消息
                ws.send(message);
            } else {
                alert('WebSocket connection not established, please connect.');
            }
        }
	    // 生成WebSocket URI 
        function updateTarget(target) {
            if (window.location.protocol == 'http:') {
                document.getElementById('target').value = 
'ws://' + window.location.host + target;
            } else {
                document.getElementById('target').value = 
'wss://' + window.location.host + target;
            }
        }
        // 在界面显示log及消息
        function log(message) {
            var console = document.getElementById('console');
            var p = document.createElement('p');
            p.style.wordWrap = 'break-word';
            p.appendChild(document.createTextNode(message));
            console.appendChild(p);
            while (console.childNodes.length > 25) {
                console.removeChild(console.firstChild);
            }
            console.scrollTop = console.scrollHeight;
        }
    </script>

       注:完整代码參见apache-tomcat-7.0.42\webapps\examples\websocket\echo.html

       以上client能够依据“Connectusing”的不同选择连接不同的服务端WebSocket。比如messages选项相应的服务端代码例如以下,其核心逻辑是在收到client发来的消息后马上将其发回client。

public class EchoMessage extends WebSocketServlet {
    private static final long serialVersionUID = 1L;
    private volatile int byteBufSize;
    private volatile int charBufSize;

    @Override
    public void init() throws ServletException {
        super.init();
        byteBufSize = getInitParameterIntValue("byteBufferMaxSize", 2097152);
        charBufSize = getInitParameterIntValue("charBufferMaxSize", 2097152);
    }

    public int getInitParameterIntValue(String name, int defaultValue) {
        String val = this.getInitParameter(name);
        int result;
        if(null != val) {
            try {
                result = Integer.parseInt(val);
            }catch (Exception x) {
                result = defaultValue;
            }
        } else {
            result = defaultValue;
        }

        return result;
    }

    // 创建Inbound实例,WebSocketServlet子类必须实现的方法
    @Override
    protected StreamInbound createWebSocketInbound(String subProtocol,
            HttpServletRequest request) {
        return new EchoMessageInbound(byteBufSize,charBufSize);
    }
    // MessageInbound子类,完毕收到WebSocket消息后的逻辑处理
    private static final class EchoMessageInbound extends MessageInbound {
        public EchoMessageInbound(int byteBufferMaxSize, int charBufferMaxSize) {
            super();
            setByteBufferMaxSize(byteBufferMaxSize);
            setCharBufferMaxSize(charBufferMaxSize);
        }
        //  二进制消息响应
        @Override
        protected void onBinaryMessage(ByteBuffer message) throws IOException {
            getWsOutbound().writeBinaryMessage(message);
        }
        // 文本消息响应
        @Override
        protected void onTextMessage(CharBuffer message) throws IOException {
            // 将收到的消息发回client
            getWsOutbound().writeTextMessage(message);
        }
    }
}

        注:完整代码參见apache-tomcat-7.0.42\webapps\examples\WEB-INF\classes\websocket\echo\EchoMessage.java。

2.      chat例程

        chat例程实现了通过网页进行群聊的功能。每一个打开的聊天网页都能够收到全部在线者发出的消息,同一时候,每一个在线者也都能够(也仅仅能够)向其他全部人发送消息。也就是说,chat实例演示了怎样通过WebSocket实现对全部在线client的广播。

WebSocket初探WebSocket初探

                                                                  图3

        chat例程client核心代码例如以下,能够看到事实上现方式与echo例程形式上稍有变化,本质依然是对WebSocket事件进行响应与处理。

<script type="text/javascript">
        var Chat = {};

        Chat.socket = null;

        Chat.connect = (function(host) {
            // 创建WebSocket
            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;
            }
            // 定义Open事件处理函数
            Chat.socket.onopen = function () {
                Console.log('Info: WebSocket connection opened.');
                document.getElementById('chat').onkeydown = function(event) {
                    if (event.keyCode == 13) {
                        Chat.sendMessage();
                    }
                };
            };
            // 定义Close事件处理函数
            Chat.socket.onclose = function () {
                document.getElementById('chat').onkeydown = null;
                Console.log('Info: WebSocket closed.');
            };
            // 定义Message事件处理函数
            Chat.socket.onmessage = function (message) {
                Console.log(message.data);
            };
        });

        Chat.initialize = function() {
            if (window.location.protocol == 'http:') {
                Chat.connect('ws://' + 
window.location.host + '/examples/websocket/chat');
            } else {
                Chat.connect('wss://' + 
window.location.host + '/examples/websocket/chat');
            }
        };
		// 发送消息至服务端
        Chat.sendMessage = (function() {
            var message = document.getElementById('chat').value;
            if (message != '') {
                Chat.socket.send(message);
                document.getElementById('chat').value = '';
            }
        });

        var Console = {};

        Console.log = (function(message) {
            var console = document.getElementById('console');
            var p = document.createElement('p');
            p.style.wordWrap = 'break-word';
            p.innerHTML = message;
            console.appendChild(p);
            while (console.childNodes.length > 25) {
                console.removeChild(console.firstChild);
            }
            console.scrollTop = console.scrollHeight;
        });

        Chat.initialize();

    </script>


        注:完整代码參见apache-tomcat-7.0.42\webapps\examples\websocket\chat.html

 
        chat例程服务端代码例如以下:
public class ChatWebSocketServlet extends WebSocketServlet {    private static final long serialVersionUID = 1L;    private static final String GUEST_PREFIX = "Guest";    private final AtomicInteger connectionIds = new AtomicInteger(0);    private final Set<ChatMessageInbound> connections =            new CopyOnWriteArraySet<ChatMessageInbound>();    // 创建Inbound实例,WebSocketServlet子类必须实现的方法    @Override    protected StreamInbound createWebSocketInbound(String subProtocol,            HttpServletRequest request) {        return new ChatMessageInbound(connectionIds.incrementAndGet());    }    // MessageInbound子类,完毕收到WebSocket消息后的逻辑处理    private final class ChatMessageInbound extends MessageInbound {        private final String nickname;        private ChatMessageInbound(int id) {            this.nickname = GUEST_PREFIX + id;        }        // Open事件        @Override        protected void onOpen(WsOutbound outbound) {            connections.add(this);            String message = String.format("* %s %s",                    nickname, "has joined.");            broadcast(message);        }        // Close事件        @Override        protected void onClose(int status) {            connections.remove(this);            String message = String.format("* %s %s",                    nickname, "has disconnected.");            broadcast(message);        }		// 二进制消息事件        @Override        protected void onBinaryMessage(ByteBuffer message) throws IOException {            throw new UnsupportedOperationException(                    "Binary message not supported.");        }		// 文本消息事件        @Override        protected void onTextMessage(CharBuffer message) throws IOException {            // Never trust the client            String filteredMessage = String.format("%s: %s",                    nickname, HTMLFilter.filter(message.toString()));            broadcast(filteredMessage);        }		// 向全部已连接的客户端发送文本消息(广播)        private void broadcast(String message) {            for (ChatMessageInbound connection : connections) {                try {                    CharBuffer buffer = CharBuffer.wrap(message);                    connection.getWsOutbound().writeTextMessage(buffer);                } catch (IOException ignore) {                    // Ignore                }            }        }    }


        注:完整代码參见apache-tomcat-7.0.42\webapps\examples\WEB-INF\classes\websocket\echo\ChatWebSocketServlet.java。

通过上述例程能够看到WebSocket广播实际上是通过遍历全部连接并通过每一个连接向对应的client发送消息实现的。

四、            WebSocket实战

        实时向在线用户推送通知是一个WebSocket应用的简单场景,后台提交通知信息以后,所在在线用户均应非常快收到这个通知。通过上述例程了解WebSocket后,能够尝试编写一个实现这个需求的WebSocket应用。

首先编写一个用户的Sample页面,该页面没有实质的内容,可是在收到后台发出的通知时要在右下角通过弹窗显示通知的内容。其代码例如以下:

<!DOCTYPE html><html><head>    <title>Receive Message</title>    <style type="text/css">        #winpop { width:200px; height:0px;             position:absolute;             right:0; bottom:0;             border:1px solid #999999;             margin:0;             padding:1px;             overflow:hidden;             display:none;             background:#FFFFFF}        #winpop .con { width:100%; height:80px;             line-height:80px;             font-weight:bold;             font-size:12px;             color:#FF0000;             text-align:center}    </style>    <script type="text/javascript">        // 弹窗相关        function tips_pop(){            var MsgPop=document.getElementById("winpop");            var popH=parseInt(MsgPop.style.height);                        if(isNaN(popH)) {                popH = 0;            }                        if (popH==0){                MsgPop.style.display="block";                show=setInterval("changeH('up')",100);            }            else {                hide=setInterval("changeH('down')",100);            }        }        function changeH(str) {            var MsgPop=document.getElementById("winpop");            var popH=parseInt(MsgPop.style.height);                        if(isNaN(popH)) {                popH = 0;            }                        if(str=="up"){                   if (popH<=100){                        MsgPop.style.height=(popH+4).toString()+"px";                }                else{                      clearInterval(show);                    setTimeout("tips_pop()", 5000);                }            }            if(str=="down"){                 if (popH>=4){                          MsgPop.style.height=(popH-4).toString()+"px";                }                else{                            clearInterval(hide);                        MsgPop.style.display="none";                  }            }        }                // WebSocket相关        var ws = null;                function connect() {            var target = 'ws://' + window.location.host                 + "/test/NotifyWebSocketServlet";                        if ('WebSocket' in window) {                ws = new WebSocket(target);            } else if ('MozWebSocket' in window) {                ws = new MozWebSocket(target);            } else {                alert('WebSocket is not supported by this browser.');                return;            }                        ws.onopen = function () {                 document.getElementById('msg').innerHTML =                     "WebSocket has opened, Waiting message.......";            };                        ws.onmessage = function (event) {                document.getElementById('infomsg').innerHTML = event.data;                tips_pop();            };                        ws.onclose = function () {                document.getElementById('msg').innerHTML = "WebSocket has closed";            };        }        function disconnect() {            if (ws != null) {                ws.close();                ws = null;            }        }                       connect();            </script></head><body>    <h1 align="center" id="msg">Try to connect websocket.</h1>    <div id="winpop">        <div class="con" id="infomsg"></div>    </div></body></html>


        当用户界面打开时,它会尝试通过/test/NotifyWebSocketServlet建立与server的WebSocket连接,而NotifyWebSocketServlet的实现代码则例如以下:

package net.yanzhijun.example;import javax.servlet.ServletContext;import javax.servlet.http.HttpServletRequest;import org.apache.catalina.websocket.StreamInbound;import org.apache.catalina.websocket.WebSocketServlet;public class NotifyWebSocketServlet extends WebSocketServlet {    private static final long serialVersionUID = 1L;        @Override    protected StreamInbound createWebSocketInbound(String subProtocol,            HttpServletRequest request) {        ServletContext application = this.getServletContext();        return new NofityMessageInbound(application);    }    }

        与Tomcat给出的演示样例代码不同的是,在NotifyWebSocketServlet中并未将继承于MessageInboundNofityMessageInbound作为一个内嵌类。前述演示样例代码中发送消息和接收消息都是在同一组client页面和服务端响应Servlet间进行的,而当前须要实现是在一个页面中提交通知,而在其他用户的页面上显示通知信息,因此须要将全部client与服务端的连接存储一个全局域中,故而NofityMessageInbound将不仅仅在当前Servlet中被使用,所以有必要将其独立出来。

        NofityMessageInbound的完整代码例如以下:

package net.yanzhijun.example;import java.nio.CharBuffer;import java.nio.ByteBuffer;import java.io.IOException;import java.util.Set;import java.util.concurrent.CopyOnWriteArraySet;import javax.servlet.ServletContext;import org.apache.catalina.websocket.WsOutbound;import org.apache.catalina.websocket.MessageInbound;public class NofityMessageInbound extends MessageInbound {    private ServletContext application;    private Set<NofityMessageInbound> connections = null;        public NofityMessageInbound(ServletContext application) {        this.application = application;        connections =             (Set<NofityMessageInbound>)application.getAttribute("connections");        if(connections == null) {            connections =                new CopyOnWriteArraySet<NofityMessageInbound>();        }    }        @Override    protected void onOpen(WsOutbound outbound) {        connections.add(this);            application.setAttribute("connections", connections);    }    @Override    protected void onClose(int status) {        connections.remove(this);        application.setAttribute("connections", connections);    }    @Override    protected void onBinaryMessage(ByteBuffer message) throws IOException {        throw new UnsupportedOperationException(                "message not supported.");    }    @Override    protected void onTextMessage(CharBuffer message) throws IOException {        throw new UnsupportedOperationException(                "message not supported.");    }}


        后台发送通知的页面实现的相当简单,仅仅是一个表单提交一条通知信息。

<%@ page contentType="text/html;charset=UTF-8" language="java" %><html>    <head>        <title>PushMessage</title>    </head>    <body>        <h1 align="Center">Online Broadcast</h1>        <form method="post" action="PushMessageServlet">            <p>Message:<br/>                <textarea name="message" rows="5" cols="30"></textarea>            </p>            <p><input type="submit" value="Send">                  <input type="reset" value="Reset">            </p>        </form>    </body></html>


       接收提交通知的Servlet是PushMessageServlet,它在收到后台提交的通知后,就通过全部用户的WebSocket连接将通知发送出去。

package net.yanzhijun.example;import java.io.PrintWriter;import java.nio.CharBuffer;import java.util.Set;import java.util.concurrent.CopyOnWriteArraySet;import java.io.IOException;import javax.servlet.ServletContext;import javax.servlet.ServletException;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;public class PushMessageServlet extends HttpServlet {     private static final long serialVersionUID = 1L;         @Override    public void doGet(HttpServletRequest request,                      HttpServletResponse response)        throws IOException, ServletException {            doPost(request, response);        }            @Override    public void doPost(HttpServletRequest request,                      HttpServletResponse response)        throws IOException, ServletException {                    request.setCharacterEncoding("UTF-8");        response.setContentType("text/html;charset=UTF-8");                PrintWriter out = response.getWriter();                String message = request.getParameter("message");                if(message == null || message.length() == 0) {                        out.println("The message is empty!");            return;        }                // 广播消息        broadcast(message);                out.println("Send success!");            }        // 将參数中的消息发送至全部在线client    private void broadcast(String message) {        ServletContext application=this.getServletContext();            Set<NofityMessageInbound> connections = (Set<NofityMessageInbound>)application.getAttribute("connections");        if(connections == null){            return;        }                for (NofityMessageInbound connection : connections) {            try {                CharBuffer buffer = CharBuffer.wrap(message);                connection.getWsOutbound().writeTextMessage(buffer);            } catch (IOException ignore) {                // Ignore            }        }    } }


        编译相关文件并完毕部署,尝试在后台发送消息,能够看到用户界面右下角出现的弹窗中显示了后台所提交的内容。

WebSocket初探WebSocket初探
                                                  图4

五、            WebSocket总结

    通过以上例程和实例能够看出,从开发角度使用WebSocket相当easy,基本仅仅须要创建WebSocket实例并对关心的事件进行处理就能够了;从应用角度WebSocket提供了优异的性能,图 5是来自websocket.org的性能測试图表(
http://www.websocket.org/quantum.html
),能够看到当并发和负载添加�时轮询与WebSocket的差异。

WebSocket初探
                                                                   图5
         (以上例程client在IE10.0和Chrom28.0下測试通过。)
 
         欢迎訪问梦断酒醒的博客http://blog.csdn.net/ishallwn
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/118409.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

  • Python-辨析type/dtype/astype用法

    Python-辨析type/dtype/astype用法Python中与数据类型相关函数及属性有如下三个:type/dtype/astype。名称描述type()返回参数的数据类型dtype返回数组中元素的数据类型astype()对数据类型进行转换type()用于获取数据类型#type用于获取数据类型importnumpyasnpa=[1,2,3]print(type(a))#&gt;&…

  • linux目录结构详解_linux系统配置文件目录

    linux目录结构详解_linux系统配置文件目录前言平常linux系统用的也不少,那么linux下的每个目录都是用来干什么的,小伙伴们有仔细研究过吗?让我们来了解下吧Linux系统目录结构登录系统后,在当前命令窗口下输入命令:[root@

  • CentOS7 安装以太坊 geth 客户端、创建私有区块链及挖矿

    CentOS7 安装以太坊 geth 客户端、创建私有区块链及挖矿安装以太坊源码,即安装GoEthereum(安装Geth)1、安装Golang可以直接使用yum这个包管理器安装Golangyuminstallgolang2、下载以太坊源码(GoEthereum)首先下载geth源码go-ethereum,这里以go-ethereum-1.9.7.tar.gz,直接在GitHub下载3、安装以太坊源码(安装Geth)接下来解压源码:tar-xzfgo-ethereum-1.9.7.tar.gz用下…

  • SpringBoot +DynamicDataSource切换多数据源记录方法

    SpringBoot +DynamicDataSource切换多数据源记录方法

  • Oracle Sequence Cache 参数说明[通俗易懂]

    Oracle Sequence Cache 参数说明[通俗易懂]之前整理的一篇文章:   ORACLESEQUENCE介绍http://blog.csdn.net/tianlesoftware/archive/2009/10/30/4745039.aspx 之前整理的一篇文章。那是还是写blog初期的作品。2009年10月份的。转眼一年,写Blog也比以前成熟了很多。 一.理论知识先看一个创建Sequence的语句:SQL>creates

  • vlan trunk对应的协议是_清楚怎么解释

    vlan trunk对应的协议是_清楚怎么解释什么是vlanvlan(VirtualLAN),翻译成中文是“虚拟局域网”。LAN可以是由少数几台家用计算机构成的网络,也可以是数以百计的计算机构成的企业网络。VLAN所指的LAN特指使用路由器分割的网络——也就是广播域。在此之前让我们先复习一下广播域的概念。广播域,指的是广播帧(目标MAC地址全部为1)所能传递到的范围,亦即能够直接通信的范围。严格地说,并不仅仅是广播帧,多播帧(MulticastFrame)和目标不明的单播帧(UnknownUnicastFrame)也能在同一个广播域中畅行无

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号