#255 free 输出解读

2018-06-12
  • total 内存总数
  • used 使用内存
  • free 未使用内存
  • shared Memory used (mostly) by tmpfs (Shmem in /proc/meminfo)
  • buffers Memory used by kernel buffers (Buffers in /proc/meminfo)
  • cached Memory used by the page cache and slabs (Cached and SReclaimable in /proc/meminfo)
  • available 可用内存

#254 短信长度到底是怎么规定的

2018-06-08

短信的通信协议从 2G 时代一直到现在基本没有什么变化。

  1. 支持 140 个字节(140 Byte = 140 * 8 bit = 1120 bit)
  2. 支持 GSM-7 编码,8bit 编码,UCS-2 编码
  3. GSM-7 每个字符 7 bit,支持英语和西欧语言,配合 National Language Shift Table 功能,能够支持一些其他字母文字的语言。
  4. UCS-2 每个字符 2 Byte(16 bit),传递一些 GSM-7 框架不支持的语言,比如中文(字符太多,7bit 方案反倒不合适)。
  5. 8bit 用来传递图片等二进制数据。
  6. 根据短信协议,如果长度超出范围,需要在短信前面加上一个二进制头部,叫做 UDH(User Data Header)。
  7. UDH 一共 6 个字节
  8. 包含一些 SMS 拓展字段
  9. 对于短信拆分来讲,里面有这一批短信的总条数,和当前这条短信的序号

所以,

  1. 如果是中文短信,用 UCS-2 编码,每个字符 2 Byte,一条短信最长支持 70 个中文字符(140 Byte / 2)。
  2. 注意:所有字符,包括 ASCII 中的英文、数字、半角的标点,全部需要转换成 UCS-2 的 2 Byte 编码。
  3. 如果长于 70 个字符,就需要切割成多条,UDH 头部需要 6 Byte,每条短信还剩 134 Byte,除以二,还能装 67 个字符。
  4. 例如:140 个中文字符的短信,需要切割成 67 + 67 + 6 三条短信。
  5. 如果是英文短信,或者部分西欧语言,可以使用 GSM-7 编码,每个字符 7 bit,一条短信最长支持 160 个字符(1120 bit / 7)。
  6. 如果长于 160 个字符,也是需要切割,除去 UDH 的 6 Byte = 6 _ 8 bit = 48 bit,还剩 1120 bit - 48 bit = 1072 bit,
    1072 bit = 7 bit _ 153 + 1 bit,也就是说最多发送 153 个字符(多的那 1 bit 置 0,不参与计费)。
  7. 例如,320 个英文字符,需要切割成 153 + 153 + 14 三条短信。
  8. 注意:GSM-7 编码中,9 个拓展字符 ^ ~ \ | { } [ ] € 需要算两个字符。非常重要
  9. 数据通信中都是使用 Byte 为单位,用 GSM-7 编码可能会除不尽,也就是说剩余 1 ~ 7 bit。比如:
    1. 发送 33 个字,33 * 7 bit = 231 bit,需要 29 Byte (232 bit) 来装,就多了 1 bit
    2. 同样的方法计算,发送 34 个字多了 2 bit,... 发送 39 个字剩余 7 bit,发送 40 个字正好 35 Byte,没有多余的 bit...
    3. 根据协议,多余的 bit 需要置零,除非是多出来 7 bit,7 个 0 会和 GSM-7 的 @ 字符冲突,这时候应该置为 0001101,也就是 GSM-7 中的 \r 回车字符。

最后:

  1. 关于 GSM-7 的详细信息,可以参考:2022/01/05,GSM-7 编码
  2. 关于 UDH 的详细信息,可以参考:2022/05/13,CMPP: UDHI 头
  3. https://en.wikipedia.org/wiki/Concatenated_SMS

补充:

  1. 部分通道采用 7 Byte 的 UDH (短信批次标识由 1 Byte 改成 2 Byte),所以短信切割长度是 152 个字符((140 - 7) * 8 / 7)。
  2. 如果短信采用 GSM-7 编码,UDH 需要按 7 bit 对齐,也就是说如果是 6B 的 UDH,最后需要占用 49 bit,UDH 后面的 1 bit 填 0。

#253 Python 打包上传到 PyPI

2018-05-26

首先你得有 PyPI 的账号,没有注册的话,不用搞了。
然后你肯定要先准备 setup.py 了,如果这个都没弄就不用搞了。

算了,先贴一个简单的样板吧。

setup.py

# -*- coding: utf-8 -*-

from setuptools import setup

setup(
    name='PageageName',
    version='0.0.1',
    author='YourName',
    author_email='YourEmail',
    url='PackageSite (e.g. GitHub)',
    description='....',
    long_description=open('README.md').read(),
    long_description_content_type='text/markdown',
    packages=['PackageDir'],
    install_requires=[
        'requests>=2.18.4',
        'balabala',
    ],
    extras_require={  # 可选
        'dev': [
            'balabala...',
        ],
        'test': [
            'balabala...',
        ],
    },
    classifiers=[
        'Development Status :: 3 - Alpha',
        'Programming Language :: Python',
        'Programming Language :: Python :: 2',
        'Programming Language :: Python :: 2.7',
        'Programming Language :: Python :: 3',
        'Programming Language :: Python :: 3.6',
        'Programming Language :: Python :: 3.7',
        'Programming Language :: Python :: 3.8',
        'License :: OSI Approved :: MIT License',
    ],
)

流程

python3 -m pip install twine

rm -rf dist

python3 setup.py sdist bdist_wheel
python2 setup.py sdist bdist_wheel

twine upload dist/*

twine 会询问账号密码,如果不想每次这么麻烦,可以创建 ~/.pypirc 文件。

.pypirc 文件

[distutils]
index-servers =
    pypi

[pypi]
repository = https://upload.pypi.org/legacy/
username = your_username
password = your_password

参考资料

#252 SMTP 认证

2018-05-18

最常见的三种 SMTP 认证方法:

  • PLAIN
  • LOGIN
  • CRAM-MD5

#251 Python 应用: 简易 SMTP 服务器

2018-05-07

Python2

python2 -m smtpd -h
# An RFC 2821 smtp proxy.
# Usage: /usr/lib/python2.7/smtpd.py [options] [localhost:localport [remotehost:remoteport]]
#     --nosetuid, -n
#     --version, -V
#     --class classname, -c classname
#     --debug, -d
#     --help, -h

Python3

python -m smtpd -h
# An RFC 5321 smtp proxy with optional RFC 1870 and RFC 6531 extensions.
# Usage: /usr/lib/python3.9/smtpd.py [options] [localhost:localport [remotehost:remoteport]]
#     --nosetuid, -n  默认会设置用户为 nobody,如果不是 root 会因权限不足失败
#     --version, -V
#     --class classname, -c classname 默认: PureProxy
#     --size limit, -s limit 消息大小限制(RFC 1870 SIZE extension),默认是 33554432 字节,即 32MB
#     --smtputf8, -u 启用 SMTPUTF8 扩展(RFC 6531)
#     --debug, -d
#     --help, -h

# 如果不指定主机,就使用 localhost
# 如果主机是 localhost,端口使用 8025
# 如果是其他主机,端口使用 25
python3 -m smtpd -n

# 默认的 PureProxy 会给转信出去,正常情况会被服务器拒绝
python3 -m smtpd -n -c smtpd.DebuggingServer

Python 3.9 的 PureProxy 有 BUG,会报 process_message() got an unexpected keyword argument 'mail_options'

自定义黑洞服务器

blackhole.py

import smtpd
import time
class BlackHoleServer(smtpd.SMTPServer):
    def process_message(self, peer, mailfrom, rcpttos, data, **kwargs):
        print('%s %s %s -> %s' % (time.strftime('%Y-%m-%d %H:%M:%S'), peer, mailfrom, rcpttos))

setup.py

import setuptools
setuptools.setup(name="blackhole", py_modules=["blackhole"])

附件下载:blackhole.zip

python setup.py install --user
python -m smtpd -n -c blackhole.BlackHoleServer

测试

import smtplib
smtp = smtplib.SMTP('localhost', 8025)
from_addr = 'admin@markjour.com'
to_addr = 'you@markjour.com'
smtp.sendmail(from_addr, to_addr,
    f"""From: {from_addr}\nTo: {to_addr}\nSubject: just4fun\n\nhello, world!""")

#249 Python 应用: 简易 HTTP 服务器

2018-05-07

Python2

在当前目录起 HTTP 服务,可以用于测试和临时性的文件下载服务。

# Default bind to 0.0.0.0:8000
python -m SimpleHTTPServer

# Maybe you want to use port 8080
python -m SimpleHTTPServer 8080

Python3

除了可以指定端口,还可以指定绑定地址、工作目录。

# Also bind to 0.0.0.0:8000
python -m http.server

python -m http.server -h
# usage: server.py [-h] [--cgi] [--bind ADDRESS] [--directory DIRECTORY] [port]
#
# positional arguments:
#   port                  Specify alternate port [default: 8000]
#
# optional arguments:
#   -h, --help            show this help message and exit
#   --cgi                 Run as CGI Server
#   --bind ADDRESS, -b ADDRESS
#                         Specify alternate bind address [default: all interfaces]
#   --directory DIRECTORY, -d DIRECTORY
#                         Specify alternative directory [default:current directory]

python -m http.server 9999
python -m http.server --bind=127.0.0.1
python -m http.server --bind=127.0.0.1 9999
python -m http.server -d ~/Pictures

#248 Python Redis Debug

2018-05-06

如果可以的话,使用 redis-cli monitor 命令来输出所有 Redis 命令也很方便。

有时,条件不允许,或者 Redis 需要处理其他的连接,我希望将自己代码调用的 Redis 命令输出到日志中,方便调试。

#247 Logrotate

2018-05-03

配置

# cat /etc/logrotate.conf
weekly
su root adm
rotate 4
create
#dateext
#compress
include /etc/logrotate.d

以 Nginx 配置为例:

# cat /etc/logrotate.d/nginx
/var/log/nginx/*.log {
        daily       # 按日切割
        missingok   # 如果文件不存在,则不创建
        rotate 14   # 最多保留 14 个日志文件
        compress    # 压缩
        delaycompress   # 延迟压缩
        notifempty      # 如果文件为空,则不创建
        create 0640 www-data adm

        # 可能一次切割多个日志,
        # 但是后面遇到的每个脚本都只执行一次,
        # 在所有日志切割之前或之后
        sharedscripts

        prerotate
                if [ -d /etc/logrotate.d/httpd-prerotate ]; then \
                        run-parts /etc/logrotate.d/httpd-prerotate; \
                fi \
        endscript

        postrotate
                invoke-rc.d nginx rotate >/dev/null 2>&1
        endscript
}

其他常用选项:

  • dateext 部分日志需要添加日期后缀
  • lastaction/endscript 最后执行的指令,很有用,比如最后将日志备份到某些地方

比如:

rsync -au *.gz 192.168.64.234:/backup/nginx-logs/`hostname -s`/

参考资料与拓展阅读

#246 关于 DNS(历史和未来)

2018-05-02

历史

从阿帕网 (ARPANET) 时代一直到互联网的早期,网络节点比较少,都是通过本地 hosts 文件来实现主机名到 IP 地址的映射。
根据维基百科的信息,斯坦福研究所负责维护了一个公共 hosts 文件,大家会找他同步 (rfc606, rfc608)。
PS: 这个时候如果有主机名重复了谁来管?打电话过去让他们改名?

这套机制一直运行了十几年,公共 hosts 文件已经变的很大了,变化也很频繁(IP 可能已经不再那么固定了),需要经常同步。这个时候,斯坦福研究所的网络压力也越来越大了。

后来人们开始设计域名和域名相关的公共设施 (rfc805, rfc830)。最后,在 1983 年,形成了下面两个 RFC 文档:

  • RFC 882, DOMAIN NAMES - CONCEPTS and FACILITIES
  • RFC 883, DOMAIN NAMES - IMPLEMENTATION and SPECIFICATION

几年后(1987),正式的 DNS 标准 RFC 1034 和 RFC 1035 推出。
这套标准一直运行到现在,可能有对其进行拓展(比如 DNS 记录类型不断添加,Unicode 字符引入),但是基本技术设计没有改变。

DNS 的管理权问题

https://zhidao.baidu.com/question/1386069665602139980.html

基本流程

比如本站域名 www.markjour.com, 其完整形式应该是 www.markjour.com. (后面多一个小数点)

DNS 软件

  • BIND
  • PowerDNS
  • dnsmasq
  • Unbound
  • CoreDNS
  • SmartDNS

Cache-Only DNS Server

新的发展

  1. 标准的 DNS 是运行在 UDP 53 端口上的。后来的 RFC 1123 增加了 TCP 的支持, 这个方案叫做 DNS over TCP, 还是在 53 端口。
  2. DNSCrypt, 2011 年设计的, 实现 DNS 的加密和验证,运行于 443 端口。注意:存在于 IETF 框架之外,但是好像有很多服务器支持。
  3. DNS over TLS (DoT), 2016 年 5 月成为规范。
    RFC 7858 Specification for DNS over Transport Layer Security (TLS)
    主要作用是加密传输,防止窃听。
  4. DNS over HTTPS (DoH), 2018 年 10 月成为规范。
    RFC 8484 DNS Queries over HTTPS (DoH)
    作用和 DoT 一样。
  5. DNS over TOR, 2019 年。
  6. Oblivious DNS-over-HTTPS (ODoH), 透过代理的方式,让 DoH 服务器无法获取客户端的真实 IP。同时代理无法获取 DNS 请求的内容。

参考资料与拓展阅读