Netty服务端

1.引入依赖

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  3. xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  4. <modelVersion>4.0.0</modelVersion>
  5. <parent>
  6. <groupId>org.springframework.boot</groupId>
  7. <artifactId>spring-boot-starter-parent</artifactId>
  8. <version>1.5.9.RELEASE</version> <!-- 我这里用的1.5.9 -->
  9. <relativePath/> <!-- lookup parent from repository -->
  10. </parent>
  11. <groupId>com.blaze</groupId>
  12. <artifactId>netty-demo</artifactId>
  13. <version>0.0.1-SNAPSHOT</version>
  14. <name>netty-demo</name>
  15. <description>Demo project for Spring Boot</description>
  16.  
  17. <properties>
  18. <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  19. <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
  20. <java.version>1.8</java.version>
  21. </properties>
  22.  
  23. <dependencies>
  24. <dependency>
  25. <groupId>org.springframework.boot</groupId>
  26. <artifactId>spring-boot-starter-thymeleaf</artifactId>
  27. </dependency>
  28.  
  29. <dependency>
  30. <groupId>org.apache.commons</groupId>
  31. <artifactId>commons-lang3</artifactId>
  32. <version>3.4</version>
  33. </dependency>
  34.  
  35. <!--fastjson-->
  36. <dependency>
  37. <groupId>com.alibaba</groupId>
  38. <artifactId>fastjson</artifactId>
  39. <version>1.2.50</version>
  40. </dependency>
  41.  
  42. <!--netty依赖-->
  43. <dependency>
  44. <groupId>io.netty</groupId>
  45. <artifactId>netty-all</artifactId>
  46. <version>4.1.43.Final</version>
  47. </dependency>
  48.  
  49. </dependencies>
  50.  
  51. <build>
  52. <plugins>
  53. <plugin>
  54. <groupId>org.springframework.boot</groupId>
  55. <artifactId>spring-boot-maven-plugin</artifactId>
  56. <configuration>
  57. <mainClass>com.blaze.nettydemo.server.WebSocketServer</mainClass>
  58. </configuration>
  59. </plugin>
  60. </plugins>
  61. <finalName>netty-server</finalName>
  62. </build>
  63.  
  64. </project>

2.服务端

WebSocketServer

  1. import io.netty.bootstrap.ServerBootstrap;
  2. import io.netty.channel.Channel;
  3. import io.netty.channel.EventLoopGroup;
  4. import io.netty.channel.nio.NioEventLoopGroup;
  5. import io.netty.channel.socket.nio.NioServerSocketChannel;
  6. import io.netty.handler.logging.LogLevel;
  7. import io.netty.handler.logging.LoggingHandler;
  8. import io.netty.handler.ssl.SslContext;
  9. import io.netty.handler.ssl.SslContextBuilder;
  10. import io.netty.handler.ssl.util.SelfSignedCertificate;
  11.  
  12. /**
  13. * create by zy 2019/11/28 14:50
  14. * TODO
  15. */
  16. public final class WebSocketServer {
  17.  
  18. //static final boolean SSL = System.getProperty("ssl") != null;
  19. //static final int PORT = Integer.parseInt(System.getProperty("port", SSL ? "8443" : "8888"));
  20. static final boolean SSL = false;
  21. static final int PORT = 8888;
  22.  
  23. public static void main(String[] args) throws Exception {
  24. // Configure SSL. 配置 SSL
  25. final SslContext sslCtx;
  26. if (SSL) {
  27. SelfSignedCertificate ssc = new SelfSignedCertificate();
  28. sslCtx = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey()).build();
  29. } else {
  30. sslCtx = null;
  31. }
  32. /**
  33. * interface EventLoopGroup extends EventExecutorGroup extends ScheduledExecutorService extends ExecutorService
  34. * 配置服务端的 NIO 线程池,用于网络事件处理,实质上他们就是 Reactor 线程组
  35. * bossGroup 用于服务端接受客户端连接,workerGroup 用于进行 SocketChannel 网络读写
  36. */
  37. EventLoopGroup bossGroup = new NioEventLoopGroup(1);
  38. EventLoopGroup workerGroup = new NioEventLoopGroup();
  39. try {
  40. // ServerBootstrap 是 Netty 用于启动 NIO 服务端的辅助启动类,用于降低开发难度
  41. ServerBootstrap b = new ServerBootstrap();
  42. b.group(bossGroup, workerGroup)
  43. .channel(NioServerSocketChannel.class)
  44. .handler(new LoggingHandler(LogLevel.INFO))
  45. .childHandler(new WebSocketServerInitializer(sslCtx));
  46.  
  47. //服务器启动辅助类配置完成后,调用 bind 方法绑定监听端口,调用 sync 方法同步等待绑定操作完成,服务开启
  48. Channel ch = b.bind(PORT).sync().channel();
  49. System.out.println("服务已开启,等待客户端连接......");
  50.  
  51. //下面会进行阻塞,等待服务器连接关闭之后 main 方法退出,程序结束
  52. ch.closeFuture().sync();
  53. } catch (InterruptedException e) {
  54. e.printStackTrace();
  55. } finally {
  56. //退出 释放资源
  57. bossGroup.shutdownGracefully();
  58. workerGroup.shutdownGracefully();
  59. }
  60. }
  61. }

WebSocketServerInitializer

  1. import io.netty.channel.ChannelInitializer;
  2. import io.netty.channel.ChannelPipeline;
  3. import io.netty.channel.socket.SocketChannel;
  4. import io.netty.handler.codec.http.HttpObjectAggregator;
  5. import io.netty.handler.codec.http.HttpServerCodec;
  6. import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler;
  7. import io.netty.handler.codec.http.websocketx.extensions.compression.WebSocketServerCompressionHandler;
  8. import io.netty.handler.ssl.SslContext;
  9.  
  10. /**
  11. * create by zy 2019/11/28 14:53
  12. * TODO
  13. */
  14. public class WebSocketServerInitializer extends ChannelInitializer<SocketChannel> {
  15.  
  16. private static final String WEBSOCKET_PATH = "/";
  17.  
  18. private final SslContext sslCtx;
  19.  
  20. public WebSocketServerInitializer(SslContext sslCtx) {
  21. this.sslCtx = sslCtx;
  22. }
  23.  
  24. @Override
  25. public void initChannel(SocketChannel ch) {
  26. ChannelPipeline pipeline = ch.pipeline();
  27. if (sslCtx != null) {
  28. pipeline.addLast(sslCtx.newHandler(ch.alloc())); // 设置 https 相关
  29. }
  30. pipeline.addLast(new HttpServerCodec()); // http 编码
  31. pipeline.addLast(new HttpObjectAggregator(65536)); // http 消息聚合器
  32. pipeline.addLast(new WebSocketServerCompressionHandler()); // 压缩 可以不设置
  33. pipeline.addLast(new WebSocketServerProtocolHandler(WEBSOCKET_PATH, null, true)); // 协议
  34. pipeline.addLast(new WebSocketFrameHandler()); // 处理WebSocketFrame
  35. }
  36. }

WebSocketFrameHandler

  1. import com.alibaba.fastjson.JSON;
  2. import com.rising.netty.model.RequestModel;
  3. import com.rising.netty.model.ResultModel;
  4. import com.rising.netty.util.MQUtil;
  5. import io.netty.buffer.ByteBuf;
  6. import io.netty.buffer.Unpooled;
  7. import io.netty.channel.ChannelHandlerContext;
  8. import io.netty.channel.SimpleChannelInboundHandler;
  9. import io.netty.handler.codec.http.websocketx.BinaryWebSocketFrame;
  10. import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
  11. import io.netty.handler.codec.http.websocketx.WebSocketFrame;
  12.  
  13. /**
  14. * create by zy 2019/11/28 14:57
  15. * TODO
  16. */
  17. public class WebSocketFrameHandler extends SimpleChannelInboundHandler<WebSocketFrame> {
  18.  
  19. @Override
  20. protected void channelRead0(ChannelHandlerContext ctx, WebSocketFrame frame) throws Exception {
  21. if (frame instanceof TextWebSocketFrame) {
  22. String request = ((TextWebSocketFrame) frame).text();
  23. System.out.println("接收消息:" + request);
  24.  
  25. String msg = "接收成功";
  26. //返回信息
  27. ctx.channel().writeAndFlush(new TextWebSocketFrame(msg));
  28. } else if (frame instanceof BinaryWebSocketFrame) {
  29. //二进制
  30. ByteBuf content = frame.content();
  31. byte[] reg = new byte[content.readableBytes()];
  32. content.readBytes(reg);
  33. String request = new String(reg, "UTF-8");
  34. System.out.println("接收消息:" + request);
  35.  
  36. String msg = "接收成功";
  37. //返回信息
  38. ByteBuf respByteBuf = Unpooled.copiedBuffer(msg.getBytes());
  39. ctx.channel().writeAndFlush(new BinaryWebSocketFrame(respByteBuf));
  40. } else {
  41. String message = "unsupported frame type: " + frame.getClass().getName();
  42. throw new UnsupportedOperationException(message);
  43. }
  44. }
  45. }

3.客户端

WebSocketClient

  1. import io.netty.bootstrap.Bootstrap;
  2. import io.netty.buffer.Unpooled;
  3. import io.netty.channel.Channel;
  4. import io.netty.channel.ChannelInitializer;
  5. import io.netty.channel.ChannelPipeline;
  6. import io.netty.channel.EventLoopGroup;
  7. import io.netty.channel.nio.NioEventLoopGroup;
  8. import io.netty.channel.socket.SocketChannel;
  9. import io.netty.channel.socket.nio.NioSocketChannel;
  10. import io.netty.handler.codec.http.DefaultHttpHeaders;
  11. import io.netty.handler.codec.http.HttpClientCodec;
  12. import io.netty.handler.codec.http.HttpObjectAggregator;
  13. import io.netty.handler.codec.http.websocketx.*;
  14. import io.netty.handler.codec.http.websocketx.extensions.compression.WebSocketClientCompressionHandler;
  15. import io.netty.handler.ssl.SslContext;
  16. import io.netty.handler.ssl.SslContextBuilder;
  17. import io.netty.handler.ssl.util.InsecureTrustManagerFactory;
  18.  
  19. import java.io.BufferedReader;
  20. import java.io.InputStreamReader;
  21. import java.net.URI;
  22.  
  23. /**
  24. * create by zy 2019/11/28 14:57
  25. * TODO
  26. */
  27. public final class WebSocketClient {
  28.  
  29. static final String URL = System.getProperty("url", "ws://127.0.0.1:8888/");
  30.  
  31. public static void main(String[] args) throws Exception {
  32. URI uri = new URI(URL);
  33. String scheme = uri.getScheme() == null ? "ws" : uri.getScheme();
  34.  
  35. final String host = uri.getHost() == null ? "127.0.0.1" : uri.getHost();
  36. final int port;
  37. if (uri.getPort() == -1) {
  38. if ("ws".equalsIgnoreCase(scheme)) {
  39. port = 80;
  40. } else if ("wss".equalsIgnoreCase(scheme)) {
  41. port = 443;
  42. } else {
  43. port = -1;
  44. }
  45. } else {
  46. port = uri.getPort();
  47. }
  48.  
  49. if (!"ws".equalsIgnoreCase(scheme) && !"wss".equalsIgnoreCase(scheme)) {
  50. System.err.println("Only WS(S) is supported.");
  51. return;
  52. }
  53.  
  54. final boolean ssl = "wss".equalsIgnoreCase(scheme);
  55. final SslContext sslCtx;
  56. if (ssl) {
  57. sslCtx = SslContextBuilder.forClient()
  58. .trustManager(InsecureTrustManagerFactory.INSTANCE).build();
  59. } else {
  60. sslCtx = null;
  61. }
  62.  
  63. //配置客户端 NIO 线程组/池
  64. EventLoopGroup group = new NioEventLoopGroup();
  65. try {
  66. // Connect with V13 (RFC 6455 aka HyBi-17). You can change it to V08 or V00.
  67. // If you change it to V00, ping is not supported and remember to change
  68. // HttpResponseDecoder to WebSocketHttpResponseDecoder in the pipeline.
  69. final WebSocketClientHandler handler = new WebSocketClientHandler(
  70. WebSocketClientHandshakerFactory.newHandshaker(
  71. uri, WebSocketVersion.V13, null, true, new DefaultHttpHeaders()));
  72.  
  73. /**
  74. * Bootstrap 与 ServerBootstrap 都继承(extends)于 AbstractBootstrap
  75. * 创建客户端辅助启动类,并对其配置,与服务器稍微不同,这里的 Channel 设置为 NioSocketChannel
  76. * 然后为其添加 Handler,这里直接使用匿名内部类,实现 initChannel 方法
  77. * 作用是当创建 NioSocketChannel 成功后,在进行初始化时,将它的ChannelHandler设置到ChannelPipeline中,用于处理网络I/O事件
  78. */
  79. Bootstrap b = new Bootstrap();
  80. b.group(group)
  81. .channel(NioSocketChannel.class)
  82. .handler(new ChannelInitializer<SocketChannel>() {
  83. @Override
  84. protected void initChannel(SocketChannel ch) {
  85. ChannelPipeline p = ch.pipeline();
  86. if (sslCtx != null) {
  87. p.addLast(sslCtx.newHandler(ch.alloc(), host, port));
  88. }
  89. p.addLast(
  90. new HttpClientCodec(),
  91. new HttpObjectAggregator(8192),
  92. WebSocketClientCompressionHandler.INSTANCE,
  93. handler);
  94. }
  95. });
  96.  
  97. //客户端与服务端建立连接
  98. Channel ch = b.connect(uri.getHost(), port).sync().channel();
  99. handler.handshakeFuture().sync();
  100.  
  101. /**
  102. * 将输入信息传输到 server 端
  103. */
  104. BufferedReader console = new BufferedReader(new InputStreamReader(System.in));
  105. while (true) {
  106. String msg = console.readLine();
  107. if (msg == null) {
  108. break;
  109. } else if ("bye".equals(msg.toLowerCase())) {
  110. //输入bye 断开连接
  111. ch.writeAndFlush(new CloseWebSocketFrame());
  112. ch.closeFuture().sync();
  113. break;
  114. } else if ("ping".equals(msg.toLowerCase())) {
  115. WebSocketFrame frame = new PingWebSocketFrame(Unpooled.wrappedBuffer(new byte[]{8, 1, 8, 1}));
  116. ch.writeAndFlush(frame);
  117. } else {
  118. WebSocketFrame frame = new TextWebSocketFrame(msg);
  119. ch.writeAndFlush(frame);
  120. }
  121. }
  122. } finally {
  123. group.shutdownGracefully();
  124. }
  125. }
  126. }

WebSocketClientHandler

  1. import io.netty.channel.*;
  2. import io.netty.handler.codec.http.FullHttpResponse;
  3. import io.netty.handler.codec.http.websocketx.*;
  4. import io.netty.util.CharsetUtil;
  5.  
  6. /**
  7. * create by zy 2019/11/28 14:58
  8. * TODO
  9. */
  10. public class WebSocketClientHandler extends SimpleChannelInboundHandler<Object> {
  11.  
  12. private final WebSocketClientHandshaker handshaker;
  13. private ChannelPromise handshakeFuture;
  14.  
  15. public WebSocketClientHandler(WebSocketClientHandshaker handshaker) {
  16. this.handshaker = handshaker;
  17. }
  18.  
  19. public ChannelFuture handshakeFuture() {
  20. return handshakeFuture;
  21. }
  22.  
  23. @Override
  24. public void handlerAdded(ChannelHandlerContext ctx) {
  25. handshakeFuture = ctx.newPromise();
  26. }
  27.  
  28. @Override
  29. public void channelActive(ChannelHandlerContext ctx) {
  30. handshaker.handshake(ctx.channel());
  31. }
  32.  
  33. @Override
  34. public void channelInactive(ChannelHandlerContext ctx) {
  35. System.out.println("WebSocket Client disconnected!");
  36. }
  37.  
  38. @Override
  39. public void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception {
  40. Channel ch = ctx.channel();
  41. if (!handshaker.isHandshakeComplete()) {
  42. try {
  43. //握手成功 建立连接
  44. handshaker.finishHandshake(ch, (FullHttpResponse) msg);
  45. System.out.println("WebSocket Client connected!");
  46. handshakeFuture.setSuccess();
  47. } catch (WebSocketHandshakeException e) {
  48. //握手失败
  49. System.out.println("WebSocket Client failed to connect");
  50. handshakeFuture.setFailure(e);
  51. }
  52. return;
  53. }
  54.  
  55. if (msg instanceof FullHttpResponse) {
  56. FullHttpResponse response = (FullHttpResponse) msg;
  57. throw new IllegalStateException(
  58. "Unexpected FullHttpResponse (getStatus=" + response.status() +
  59. ", content=" + response.content().toString(CharsetUtil.UTF_8) + ')');
  60. }
  61.  
  62. WebSocketFrame frame = (WebSocketFrame) msg;
  63. if (frame instanceof TextWebSocketFrame) {
  64. //接收客户端返回消息
  65. TextWebSocketFrame textFrame = (TextWebSocketFrame) frame;
  66. System.out.println("WebSocket Client received message: " + textFrame.text());
  67. } else if (frame instanceof PongWebSocketFrame) {
  68. System.out.println("WebSocket Client received pong");
  69. } else if (frame instanceof CloseWebSocketFrame) {
  70. System.out.println("WebSocket Client received closing");
  71. ch.close();
  72. }
  73. }
  74.  
  75. @Override
  76. public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
  77. cause.printStackTrace();
  78. if (!handshakeFuture.isDone()) {
  79. handshakeFuture.setFailure(cause);
  80. }
  81. ctx.close();
  82. }
  83. }

4.web客户端

  1. <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1transitional.dtd">
  2. <html xmlns="http://www.w3.org/1999/xhtml">
  3. <head>
  4. <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
  5. <title>Netty-Websocket</title>
  6. <script type="text/javascript">
  7. var socket;
  8. if(!window.WebSocket){
  9. window.WebSocket = window.MozWebSocket;
  10. }
  11. if(window.WebSocket){
  12. socket = new WebSocket("ws://127.0.0.1:9898/");
  13. socket.onmessage = function(event){
  14. var ta = document.getElementById('responseText');
  15. ta.value += event.data+"\r\n";
  16. };
  17. socket.onopen = function(event){
  18. var ta = document.getElementById('responseText');
  19. ta.value = "Netty-WebSocket服务器。。。。。。连接 \r\n";
  20. login();
  21. };
  22. socket.onclose = function(event){
  23. var ta = document.getElementById('responseText');
  24. ta.value = "Netty-WebSocket服务器。。。。。。关闭 \r\n";
  25. };
  26. }else{
  27. alert("您的浏览器不支持WebSocket协议!");
  28. }
  29. function send(msg){
  30. if(!window.WebSocket){return;}
  31. if(socket.readyState == WebSocket.OPEN){
  32. socket.send(msg);
  33. }else{
  34. alert("WebSocket 连接没有建立成功!");
  35. }
  36. }
  37. function login(){
  38. if(!window.WebSocket){return;}
  39. if(socket.readyState == WebSocket.OPEN){
  40. socket.send("建立连接成功!");
  41. }else{
  42. alert("WebSocket 连接没有建立成功!");
  43. }
  44. }
  45. function closeSocket(){
  46. if(!window.WebSocket){return;}
  47. socket.close();
  48. }
  49. </script>
  50. </head>
  51. <body>
  52. <form onSubmit="return false;">
  53. <label>TEXT</label><input type="text" name="blaze" value="" /> <br />
  54. <br /> <input type="button" value="发送ws消息"
  55. onClick="send(this.form.blaze.value)" />
  56. <hr color="black" />
  57. <br /> <input type="button" value="断开连接"
  58. onClick="closeSocket()" />
  59. <hr color="black" />
  60. <h3>服务端返回的应答消息</h3>
  61. <textarea id="responseText" style="width: 1024px;height: 300px;"></textarea>
  62. </form>
  63. </body>
  64. </html>

Netty搭建WebSocket服务端的更多相关文章

  1. Netty 搭建 WebSocket 服务端

    一.编码器.解码器 ... ... @Autowired private HttpRequestHandler httpRequestHandler; @Autowired private TextW ...

  2. 使用Netty做WebSocket服务端

    使用Netty搭建WebSocket服务器 1.WebSocketServer.java public class WebSocketServer { private final ChannelGro ...

  3. nodejs搭建简单的websocket服务端

    创建websocket服务端使用了nodejs-websocket ,首先要安装nodejs-websocket,在项目的目录下: npm install nodejs-websocket 1.搭建w ...

  4. 《用OpenResty搭建高性能服务端》笔记

    概要 <用OpenResty搭建高性能服务端>是OpenResty系列课程中的入门课程,主讲人:温铭老师.课程分为10个章节,侧重于OpenResty的基本概念和主要特点的介绍,包括它的指 ...

  5. asp.net网站作为websocket服务端的应用该如何写

    最近被websocket的一个问题困扰了很久,有一个需求是在web网站中搭建websocket服务.客户端通过网页与服务器建立连接,然后服务器根据ip给客户端网页发送信息. 其实,这个需求并不难,只是 ...

  6. C# WebSocket 服务端示例代码 + HTML5客户端示例代码

    WebSocket服务端 C#示例代码 using System; using System.Collections.Generic; using System.Linq; using System. ...

  7. contos7搭建syslog服务端与客户端

    搭建中心服务端1,编辑文件/etc/rsyslog.conf,找到以下内容,将前面的#注释符合去除#$ModLoad imtcp#$InputTCPServerRun 514 2,在/etc/rsys ...

  8. vue.js+koa2项目实战(四)搭建koa2服务端

    搭建koa2服务端 安装两个版本的koa 一.版本安装 1.安装 koa1 npm install koa -g 注:必须安装到全局 2.安装 koa2 npm install koa@2 -g 二. ...

  9. Centos6.9 搭建rsync服务端与客户端 案例:全网备份项目

    rsync的企业工作场景说明 1)定时备份 1.1生产场景集群架构服务器备份方案项目 借助cron+rsync把所有客户服务器数据同步到备份服务器 2)实时复制 本地数据传输模式(local-only ...

随机推荐

  1. 粒子系统与雨的效果 (DirectX11 with Windows SDK)

    前言 最近在学粒子系统,看这之前的<<3D图形编程基础 基于DirectX 11 >>是基于Direct SDK的,而DXSDK微软已经很久没有更新过了并且我学的DX11是用W ...

  2. Egret入门学习日记 --- 第十篇(书中 2.9~2.13节 内容)

    第十篇(书中 2.9~2.13节 内容) 好的 2.9节 开始! 总结一下重点: 1.之前通过 ImageLoader 类加载图片的方式,改成了 RES.getResByUrl 的方式. 跟着做: 重 ...

  3. w10环境下Hexo博客搭建

    w10使用hexo+github手把手搭建自己的第一个博客 对一个程序员来说,博客的重要性不言而喻,不但可以积累知识,还可以更好的给别人分享自己的心得.今天就以时下比较流行的hexo博客搭建属于自己的 ...

  4. C++中pair详解

    pair是定义在<utility>中的生成特定类型的模板,它的作用是把一组数据合并为一体,实际上是一个拥有两个成员变量的struct. 创建一个pair对象时,需要提供两个数据类型(不要求 ...

  5. golang json解析到map中

    package main import ( "fmt" "encoding/json" ) type ItemMessage struct { ItemType ...

  6. chapter01图像基本操作

    刚刚开始学习opencv,来记录一下自己的学习笔记,也向各位大牛虚心求教 一.图片的基本知识 只要是彩色的图片都有三个颜色通道,即RGB,用三个矩阵来表示. 三个矩阵的同一个坐标位置上的数值描述的是一 ...

  7. 050 Android 百度地图的使用

    1.初始化地图 //初始化地图 private void initMapView() { //1.获取地图控件引用 mMapView = findViewById(R.id.bmapView); mB ...

  8. Vue基础知识学习(后端)

    ### Vue学习(后端) Vue安装 -引入文件安装,直接在官网下载vue.js引入项目中 -直接引用CDN -NPM安装(构建大型应用使用,在这不用) -命令行工具(构建大型单页应用,在这不用) ...

  9. bs4解析

    介绍:将一个html文档转换成BeautifulSoup对象,然后通过对象的方法或属性查找指定的节点内容 转换本地文件: ​ soup = BeautifulSoup(fp,'lxml') fp为文档 ...

  10. 关于typecho发布文章后的错位

    今天发布了一篇文章,发布后发现,what?主页错位了,安装控制变量法知道,肯定是这篇文章有什么不可告人的秘密. 所以,顺便使用一下二分法查找一下为啥,最后找到是因为使用了---------->( ...