/images/avatar.jpg

派大星的独白

Go 数据结构: map 的键类型约束

结论

键类型不能是以下几种:

  • function

  • map

  • slice

  • 实际类型为functionmapsliceinterface{}

  • 元素类型为functionmapslicearray

  • 元素类型包含functionmapslicestruct

原因

在 Go 语言规范的Map Types部分(原文链接)中规定,在键的值之间必须可以使用操作符==!=,即键的值之间可进行判等操作。

  • function, map, slice这三种类型无法进行判等操作

  • interface{}类型的实际类型如果为上述三者也无法判等

  • array类型若要支持相互比较则要求它的元素类型可相互比较

  • struct类型同理

深层原因

为什么 Go 语言规范要求map的键的值之间可进行判等操作呢?

Go 语言的map类型是一个哈希表的特定实现,每新增一个键-元素对,哈希表便会对键的值进行哈希,而后将键的哈希值真正的键-元素对进行存储。

而当进行查找时,Go 语言会用被查找键的哈希值与已有哈希值逐个对比,如果没有相等的,就会立刻返回结果。如果有相等的,那就需要再用键值本身去对比一次。

为什么还要对比?

因为再厉害的哈希算法,都存在哈希碰撞的可能。所以即使哈希值一样,键值也不一定一样。

如果键类型的值之间无法判断相等,那么当发生哈希碰撞时则无法查找到真正所需的键-元素对了。

只有键的哈希值键值都相等,才能说明找到了所需的键-元素对。


博客更新地址

Ubuntu 忘记密码后进入单用户模式强制修改密码

Ubuntu 忘记密码后进入单用户模式强制修改密码

环境:Ubuntu 16.04

昨晚心血来潮,看到 Ubuntu 终端里面的用户名,觉得有点长,就试着想改下,于是Google了下,并点开了第一篇文章,成功掉坑,重启 Ubuntu 后登陆页面输入密码一直显示输入错误,后来花了几个小时去搜索解决方案,良莠不齐,综合了一下,成功解决,现分享如下:

系统版本:Ubuntu 16.04,其它版本类似。

  • 第一步 重启 Ubuntu ,并长按shift键,进入grub菜单,上下键选择Ubuntu高级选项

http://qn.songmingyao.com/img/image-20211128204129966.png

  • 第二步 上下键选择recovery mode,不要按回车,按’e’键来编辑启动项

http://qn.songmingyao.com/img/image-20211128204139650.png

  • 第三步 执行完第二步之后,你会见到以下界面

http://qn.songmingyao.com/img/image-20211128204235380.png

使用上下键,拖到最下面,找到图上红框的部分,即ro recovery nomodeset,将之替换为rw single init=/bin/bash,如果ro recovery nomodeset后面还有内容,全部删掉(删到行尾,下面两行别删),改完效果如下:

http://qn.songmingyao.com/img/image-20211128204303362.png

  • 第四步 修改完成后,按Ctrl+xF10进行引导,引导一会后会进入单用户模式,如下图:

http://qn.songmingyao.com/img/image-20211128204320891.png

git常见问题

git常见问题

环境:Ubuntu 16.04

使用Git经常会遇到奇奇怪怪的问题,现将我遇到的一些问题汇总如下,不定期更新。

· 无法推送引用

提示信息:

1
2
3
4
5
error: 无法推送一些引用到 'git@github.com:XXXXXX'
提示:更新被拒绝,因为远程仓库包含您本地尚不存在的提交。这通常是因为另外
提示:一个仓库已向该引用进行了推送。再次推送前,您可能需要先整合远程变更
提示:(如 'git pull ...')。
提示:详见 'git push --help' 中的 'Note about fast-forwards' 小节。

解决方案:

执行以下代码,强制更新

1
git push -u origin +master​

· 每次push时都提示输入github用户名和密码

提示信息

1
2
Username for https://XXXXXX
Password for https://XXXXXX

解决方案

因为关联远程库的时候,选择的是https协议,换成ssh方式就不会出现这个问题了,具体代码如下:

1
2
3
git remote rm origin
git remote add origin git@github.com:XXXXXX
git push -u origin master

git@github.com:XXXXXX就是你的仓库地址

第一次提交会出现验证的警告,输入yes回车就行

· 删除Github上的某个文件夹

提示信息

Ubuntu 修改用户的默认目录

Ubuntu 修改用户的默认目录

环境:Ubuntu 16.04

Ubuntu 系统中,每次打开终端,就会进入系统默认的用户目录/home/username,有时候我们想要修改用户默认目录,以更快速地进入项目进行操作,这时候我们就需要通过以下方法来修改用户默认目录。

  • 第一步 执行以下代码,修改passwd配置文件
1
sudo vi /etc/passwd
  • 第二步 找到用户所在行,vim是用:/你的用户名来快速查找,找到所在行类似下面这种:
1
smy:x:1000:1000:Shelming.Song:/home/smy:/bin/bash

将其中的/home/smy改成你想要修改成的目录(采用绝对路径),如/home/smy/Desktop/python,其余内容都别改,保存后关闭。

  • 第三步 关闭终端,重新打开,就自动进入你修改的目录了。

博客更新地址

依据TFTP协议的服务端和客户端

依据TFTP协议的服务端和客户端

今天写了下依据TFTP协议的服务端和客户端,端口号设置为2048。

实现功能:

  • 可让服务端客户端搭配使用实现上传下载功能
  • 可在服务端记录log日志
  • 客户端可单独与Windows上的TFTP程序完成文件传输

待完善:

  • 服务端无退出功能,不退出的话端口不能释放
  • 代码均尚未捕获异常
  • 服务端文件列表未实时更新
  • 服务端log日志未设保护
  • 未按照MD5校验值来判断文件

服务端代码

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
from socket import *
import struct
import os
import time

def send_file():
    global log
    '发送文件'
    if file_name in file_list: # 检测服务端是否存在客户端要下载的文件
        f = open('./%s'%file_name, 'rb')
        i = 1
        times = 0
        while True:
            content = f.read(512)
            con_len = len(content)
            pack_content = struct.pack('!HH%ds'%con_len, 3, i, content)
            tftp_socket.sendto(pack_content, addr)
            echo_msg = tftp_socket.recvfrom(1024) # 接收客户端返回值
            echo_op = struct.unpack('!HH', echo_msg[0][:4]) # 读取客户端ACK
            if echo_op == (4, i):
                times = 0 # 重置客户端无响应次数
                i += 1
                if i == 65536:
                    i = 0 # 重置块编号
            elif echo_op == (4, i-1):
                times += 1 # 客户端无响应次数统计
                f.seek(1, -512) # 调整文件读取位置
                if times > 6:
                    log = open('log.txt', 'a')
                    log.write('<time : %s>\t<ip : %s>\t<op : 请求下载文件%s,中途断开链接,下载失败!>\n'%(time.ctime(), addr[0], file_name))
                    log.close()
                    break
            if con_len < 512: # 数据长度判断
                log = open('log.txt', 'a')
                log.write('<time : %s>\t<ip : %s>\t<op : 请求下载文件%s,下载成功!>\n'%(time.ctime(), addr[0], file_name))
                log.close()
                break
    else:
        error_info = struct.pack('!HH21sb', 5, 1, 'cannot find this file'.encode('utf-8'), 0) # 返回文件未找到的错误信息
        tftp_socket.sendto(error_info, addr)
        log = open('log.txt', 'a')
        log.write('<time : %s>\t<ip : %s>\t<op : 请求下载文件%s,服务端无此文件,下载失败!>\n'%(time.ctime(), addr[0], file_name))
        log.close()

def recv_file():
    '接收文件'
    if file_name in file_list:
        error_info = struct.pack('!HH19sb', 5, 2, 'file already exists'.encode('utf-8'), 0) # 返回文件未找到的错误信息
        tftp_socket.sendto(error_info, addr)
        log = open('log.txt', 'a')
        log.write('<time : %s>\t<ip : %s>\t<op : 请求上传文件%s,服务端已有此文件,上传失败!>\n'%(time.ctime(), addr[0], file_name))
        log.close()
    else:
        ack_info = struct.pack('!HH', 4, 0)
        tftp_socket.sendto(ack_info, addr)
        recv_data = tftp_socket.recvfrom(1024) # 接收服务端信息
        f = open('./%s'%file_name, 'wb')
        i = 0
        while True:
            recv_msg = recv_data[0][4:] # 读取接收信息
            recv_addr = recv_data[1] # 读取地址
            recv_id = struct.unpack('!H', recv_data[0][2:4])[0] #读取块编号

            tftp_socket.sendto(struct.pack('!HH', 4, recv_id), recv_addr) #发送ACK

            i += 1
            if i == 65536:
                i = 0

            if i == recv_id: # 防止丢包的时候也写入了文件
                f.write(recv_msg)

            if len(recv_data[0]) < 516:
                log = open('log.txt', 'a')
                log.write('<time : %s>\t<ip : %s>\t<op : 请求上传文件%s,上传成功!>\n'%(time.ctime(), addr[0], file_name))
                log.close()
                break
            recv_data = tftp_socket.recvfrom(1024) # 接收服务端信息

def main():
    global tftp_socket
    global file_name
    global file_list
    global addr
    global log

    tftp_socket = socket(AF_INET, SOCK_DGRAM)
    tftp_socket.bind(('', 2048))
    os.chdir('./server_files')

    while True:
        file_list = os.listdir('.') # 文件列表需要不断更新
        recv_msg = tftp_socket.recvfrom(1024) # 从客户端获取信息
        msg_len = len(recv_msg[0])-9 # 获取文件名长度
        file_name = struct.unpack('%ds'%msg_len, recv_msg[0][2:-7])[0].decode('utf-8') # 解码出文件名
        addr = recv_msg[1] # 获取客户端地址信息
        op = struct.unpack('!H', recv_msg[0][:2])[0] # 获取客户端请求操作码

        if op == 1: # 客户端请求下载
            send_file()

        elif op == 2: # 客户端请求上传
            recv_file()

    log.close()

if __name__ == '__main__':
    tftp_socket = None
    file_name = ''
    file_list = []
    addr = ()
    log = None
    main()

客户端代码

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
from socket import *
import struct
import os
import time

def send_requ(io):
    '发送请求'
    name_len = len(file_name)
    send_msg = struct.pack('!H%dsb5sb'%name_len, io, file_name.encode('utf-8'), 0, 'octet'.encode('utf-8'), 0 )
    tftp_socket.sendto(send_msg, (ip_addr, port_addr)) 

def recv_file():
    '接收文件' 
    recv_data = tftp_socket.recvfrom(1024) # 接收服务端信息
    if struct.unpack('!H', recv_data[0][:2])[0] == 5: # 读取操作码
        print('-----服务端无文件 %s!-----'%file_name) 

    elif struct.unpack('!H', recv_data[0][:2])[0] == 3: # 读取操作码
        f = open('./%s'%file_name, 'wb')
        i = 0
        while True:
            recv_msg = recv_data[0][4:] # 读取接收信息
            recv_addr = recv_data[1] # 读取地址
            recv_id = struct.unpack('!H', recv_data[0][2:4])[0] #读取块编号
            print(recv_id, end = ' ')

            tftp_socket.sendto(struct.pack('!HH', 4, recv_id), recv_addr) #发送ACK

            i += 1
            if i == 65536:
                i = 0

            if i == recv_id: # 防止丢包的时候也写入了文件
                f.write(recv_msg)

            if len(recv_data[0]) < 516:
                print('\n-----文件 %s下载完成!-----'%file_name)
                break
            recv_data = tftp_socket.recvfrom(1024) # 接收服务端信息

        f.close()

def send_file():
    '发送文件'
    echo_data = tftp_socket.recvfrom(1024) # 接收服务端ACK
    if struct.unpack('!H', echo_data[0][:2])[0] == 5: # 读取操作码
        print('服务端已有文件 %s,请勿重复上传!'%file_name)

    elif struct.unpack('!HH', echo_data[0]) == (4, 0): # 读取操作码
        f = open('./%s'%file_name, 'rb')
        i = 1
        times = 0
        while True:
            send_msg = f.read(512) # 每次发送512字节
            msg_len = len(send_msg) 
            send_addr = echo_data[1] # 读取地址
            pack_data = struct.pack('!HH%ds'%msg_len, 3, i, send_msg) # 打包发送信息
            tftp_socket.sendto(pack_data, send_addr)
            echo_data = tftp_socket.recvfrom(1024) # 接收客户端返回值
            echo_op = struct.unpack('!HH', echo_data[0][:4]) # 读取客户端ACK
            if echo_op == (4, i):
                times = 0 # 重置服务端无响应次数
                print(i,end = ' ')
                i += 1
                if i == 65536:
                    i = 0 # 重置块编号
            elif echo_op == (4, i-1):
                times += 1 # 客户端无响应次数统计
                f.seek(1, -512) # 调整文件读取位置
                if times > 6:
                    print('-----服务端无响应,传输失败!-----')
                    break
            if msg_len < 512: # 数据长度判断
                print('\n-----文件 %s上传完成-----'%file_name)
                break
        f.close()

def main():
    '主函数'
    global tftp_socket
    global file_name
    global ip_addr
    global port_addr

    print('-'*50)
    print('欢迎使用下载上传工具 by Shelming.Song')
    ip_addr = input('请输入服务器IP地址:')
    port_addr = int(input('请输入服务器端口:'))
    print('-'*50)

    tftp_socket = socket(AF_INET, SOCK_DGRAM)
    os.chdir('./client_files')

    while True:
        file_list = os.listdir('.') # 文件列表需要不断更新
        op = input('请选择您要进行的操作:\n1.下载\n2.上传\n3.退出\n')

        if op == '1': # 下载
            file_name = input('请输入您要下载的文件名(含后缀名):')
            if file_name in file_list:
                confirm = input('本地已有文件 %s,是否重新下载? y/n:'%file_name)
                if confirm.lower() == 'y':
                    send_requ(1)
                    recv_file()
                else:
                    continue
            else:
                send_requ(1)
                recv_file()

        elif op == '2': # 上传
            file_name = input('请输入您要上传的文件名(含后缀名):')
            if file_name not in file_list:
                print('本地无文件 %s!:'%file_name)
            else:
                send_requ(2)
                send_file()

        elif op == '3': # 退出
            print('程序即将退出,欢迎再次使用!')
            break

        else:
            print('您的输入有误,请重新输入!')
            break

if __name__ == '__main__':
    tftp_socket = None
    file_name = ''
    ip_addr = ''
    port_addr = 0
    main()  

博客更新地址

  • 微信公众号:派大星的独白

免费搭建个人技术博客 Github and Hexo

免费搭建个人技术博客 Github and Hexo

环境:Windows 10

1 环境安装

1.1 安装Node.js

Node.js下载地址:https://nodejs.org/en/,下载完成后安装

https://blog-1253888157.cos.ap-shanghai.myqcloud.com/img/image-20211128202916307.png

1.2 安装Cmder(with git)

Cmder是一款Windows下的命令行终端软件,完爆Windows自带的cmd,推荐下载!

**Cmder(with git)**下载地址:http://cmder.net/

直接下载包含git的完全版,可以不用再去单独安装git了。

https://blog-1253888157.cos.ap-shanghai.myqcloud.com/img/image-20211128202949235.png

1.3 安装Hexo

打开Cmder,输入以下命令使用npm安装Hexo

1
npm install -g hexo-cli

等待时间视网速而定,此时可以先去找找自己喜欢的主题:https://hexo.io/themes/,或是先按照1.4 配置Github来进行Github的配置

1.4 配置Github

1.4.1 创建New repository

Github网址:https://github.com

  • 注册完账号后(如之前没有的话),点击右上角的**+** ,而后选择New repository

  • Repository name内填入你的用户名.github.io,将来你的用户名.github.io就是你的博客域名,如我的博客域名就是’shelmingsong.github.io’

1.4.2 生成SSH key

  • 在Cmder内输入
1
ssh-keygen -t rsa -C '你的Github注册邮箱'
  • 一直enter下去就可以了,最后会提示:
1
Your public key has been saved in /c/Users/smy/.ssh/id_rsa.pub.
  • 找到这个文件,用sublime或是其它编辑器打开,复制文件内全部内容,而后打开https://github.com/settings/keys,点击new SSH key,Title随便写,建议写Blog,Key内粘贴刚才复制的内容。

2 Hexo博客本地发布

  • 在你认为合适的目录创建文件夹,将来博客的所有本地内容将存储在此目录