分类 编程 中的文章

Blog | 代码显示/隐藏

缘由

最近在写技术博客时,引入的代码太多,影响观感。 在尽力减少代码量,还是有些不可或缺的代码,代码量很惊人,已经占满整个屏幕.

因为个人博客是使用hugo生成的静态博客,我刚开始想的使用原生的js和css来实现,奈何技术不足,我是做后端的,一度想放弃 后面还是在网上发现h5有个新的标签<details><summary></summary></details>可以实现显示/隐藏的功能。

……

阅读全文

Nginx的一些配置

静态网站

应用场景

  • 文件服务
  • 静态网站

静态网站的配置

配置如下:

nginx.conf
worker_processes  1;
pid        nginx.pid;

events {
    worker_connections  1024;
    multi_accept on;
}


http {
    default_type  application/octet-stream;


    sendfile        on;
    keepalive_timeout  100;

     server {
        listen 80;
        charset utf-8,gbk;
        server_name localhost;

        location /{
            root /opt/;
		    autoindex on;
		    autoindex_localtime on;
	    }

	    index index.html; # 显示首页

        #静态文件访问
	    location ~* ^.+\.(jpg|jpeg|gif|png|ico|css|js|pdf|txt) {
            root /opt;
        }
    }
}

  • root: 表示网站根目录
  • autoindex: on 显示目录, off 关闭目录展示

同一个IP:PORT指向不同的地址

应用场景

  • 移动端+PC端两套网站,部署在同一台服务器上

配置

配置如下:

……

阅读全文

A*算法 | Lua版本

A*算法思路

寻路步骤

  1. 从起点A开始, 把它作为待处理的方格存入一个"开启列表", 开启列表就是一个等待检查方格的列表.
  2. 寻找起点A周围可以到达的方格, 将它们放入"开启列表", 并设置它们的"父方格"为A.
  3. 从"开启列表"中删除起点 A, 并将起点 A 加入"关闭列表", “关闭列表"中存放的都是不需要再次检查的方格
  4. 从 “开启列表” 中选择 F 值最低的方格 C (绿色起始方块 A 右边的方块), 把它从 “开启列表” 中删除, 并放到 “关闭列表” 中.
  5. 检查它所有相邻并且可以到达 (障碍物和 “关闭列表” 的方格都不考虑) 的方格. 如果这些方格还不在 “开启列表” 里的话, 将它们加入 “开启列表”, 计算这些方格的 G, H 和 F 值各是多少, 并设置它们的 “父方格” 为 C.
  6. 如果某个相邻方格 D 已经在 “开启列表” 里了, 检查如果用新的路径 (就是经过C 的路径) 到达它的话, G值是否会更低一些, 如果新的G值更低, 那就把它的 “父方格” 改为目前选中的方格 C, 然后重新计算它的 F 值和 G 值 (H 值不需要重新计算, 因为对于每个方块, H 值是不变的). 如果新的 G 值比较高, 就说明经过 C 再到达 D 不是一个明智的选择, 因为它需要更远的路, 这时我们什么也不做.
  7. 当我们发现 “开始列表” 里出现了目标终点方块的时候, 说明路径已经被找到.

对每个格子怎么走,往哪个方向走有一个很重要的函数-估价函数, 公式如下:

……

阅读全文

Blog | 音乐播放器

概述

今天给我的个人博客加了个暗模式,这样可以在晚上阅读博客的时候不伤害眼睛。突然想到如果在阅读博客的时候可以听听音乐就更好了。 于是在github上找到了一个开源的项目MetingJS,看最后一次commit信息是3年前,但是看着README.md对网站的描述信息,是我要的音乐播放器无疑了。

MetingJS快速使用

把下面这段代码放到html页面中。

……

阅读全文

Openresty三 | 上传文件

上传文件接口

nginx.conf 的server 增加一个location:

location /upload
{
   resolver 8.8.8.8 ipv6=off;
   content_by_lua_file lua/upload.lua;
}

upload.lua

lua
-- upload.lua
--==========================================
-- 文件上传
--==========================================
local chunk_size = 4096
local form, err = upload:new(chunk_size)
if not form then
    ngx.log(ngx.ERR, "failed to new upload: ", err)
    ngx.exit(ngx.HTTP_INTERNAL_SERVER_ERROR)
end
form:set_timeout(1000)

-- 文件保存的根路径
local saveRootPath = "./download/"
-- 保存的文件对象
local fileToSave
--文件是否成功保存
local ret_save = false
local filename = ""
while true do
    local typ, res, err = form:read()
    if not typ then
        ngx.say("failed to read: ", err)
        return
    end
    if typ == "header" then
        -- 开始读取 http header
        -- 解析出本次上传的文件名
        local key = res[1]
        local value = res[2]
        if key == "Content-Disposition" then
            -- 解析出本次上传的文件名
            -- form-data; name="testFileName"; filename="testfile.txt"
            local kvlist = string.split(value, ';')
            for _, kv in ipairs(kvlist) do
                local seg = string.trim(kv)
                if seg:find("filename") then
                    local kvfile = string.split(seg, "=")
                    filename = string.sub(kvfile[2], 2, -2)
                    if filename then
                        fileToSave = io.open(saveRootPath .. filename, "w+")
                        if not fileToSave then
                            ngx.say("failed to open file ", filename)
                            return
                        end
                        break
                    end
                end
            end
        end
    elseif typ == "body" then
        -- 开始读取 http body
        if fileToSave then
            fileToSave:write(res)
        end
    elseif typ == "part_end" then
        -- 文件写结束,关闭文件
        if fileToSave then
            fileToSave:close()
            fileToSave = nil
        end

        ret_save = true
    elseif typ == "eof" then
        -- 文件读取结束
        break
    else
        ngx.log(ngx.INFO, "do other things")
    end
end
if not ret_save then
    ngx.say("save file error")
    return
end

filename = saveRootPath..filename
ngx.say("save file success filename: " .. filename)

curl测试

$ curl -X POST -F 'data=@foo.txt' http://127.0.0.1:9001/upload
  • foo.txt: 本地文件名字

更改上传目录权限

更改目录所有者命令

……

阅读全文

Webrtc二 | 共享桌面

环境

  • Ubuntu18.04
  • webrtc M84

改造peerconnection例子,用来实现桌面共享

改造类CapturerTrackSource

  • 增加桌面的数据源类RcrtcDesktopCapturerTrackSource
  • 继承webrtc::DesktopCapturer::Callback, 用于桌面数据捕获与回调
  • 继承类rtc::VideoSourceInterface<webrtc::VideoFrame>, 用于rendered_track_->AddOrUpdateSink(this, rtc::VideoSinkWants()); 添加class VideoRenderer : public rtc::VideoSinkInterface<webrtc::VideoFrame> 的回调

详细代码:

……

阅读全文

Webrtc一 | peerconnection

环境

  • Ubuntu18.04
  • webrtc M84

国内镜像:https://webrtc.org.cn/mirror/

webrtc交互过程

通过peerconnection的例子来分析webrtc的交互流程

peer_connection 解析

  1. 为了便于管理,如编码,回显等,需要在应用层和底层一帧数据做一个桥接层,以隔离上下层。
  2. 桥接层的代码主要位于media/basemedia/engine,WebRTC中抽象了VideoSourceInterfaceVideoSinkInterface这两个模板类分别表示生产和吸收视频流。
  3. 如果能提供视频数据,需要实现 VideoSourceInterface,此接口类暴露了 AddOrUpdateSink 函数,可以将 Sink 注册给 Source
  4. 如果想要接收视频数据,需要实现 VideoSinkInterface,此接口类暴露了 OnFrame 函数。只要将 Sink 通过 AddOrUpdateSink 函数注册给 Source, 那么 Source 就会通过 OnFrame 接口将数据传给 Sink
  5. 对于WebRTC的PeerConnection例子,其VcmCapturer类继承了VideoSinkInterfaceTestVideoCapturer类继承类VideoSourceInterface<VideoFrame>

信令交互主要是PeerConnectionClient这个类 初始化socket连接跟 绑定回调

……

阅读全文

Go-libp2p四 | 穿透NAT

本文目的

上文Go-libp2p三 | relay例子已经分析过relay例子,并解释了两个节点如果通过中继服务进行通信。本文改造relay例子 并让这个服务做如下两件事:

  1. 交换两个节点的在交换机上的信息,帮助两个节点进行P2P打洞。
  2. 在打洞不成功的情况下能提供中继服务,转发两节点发送的数据。

我是看到源码中有hole punching协议,我是看了测试用例后才明白的。 利用libp2p的hole punching协议,NAT穿透打洞。打洞成功后进行p2p传输

……

阅读全文