之前有位同事,需要在两台电脑间传输一个很大的文件,前提是只开放了 80 端口。我以为他会配个 Apache 之类的 HTTP 服务或者 WebDAV 什么的。
他只用了一条命令:python -m http.server 80
。
后来我想也是,有时候面对问题,一个很简单的且还算完美的方案,即便不是通用的或者完备的,就解决问题而言,未尝不是一个好的选择。
http.server
http.server
) 是 Python3 的一个内置模块,源代码在 Lib/http/server.py
中,它定义了用来实现 HTTP 服务的类。
其中的 SimpleHTTPRequestHandler 类可以用来在当前目录下创建一个基本的 HTTP 服务:1
2
3
4
5
6
7
8
9
10import http.server
import socketserver
PORT = 8000
Handler = http.server.SimpleHTTPRequestHandler
with socketserver.TCPServer(("", PORT), Handler) as httpd:
print("serving at port", PORT)
httpd.serve_forever()
运行效果如下:1
2
3
4
5$ python test.py
serving at port 8000
127.0.0.1 - - [06/Apr/2019 22:55:26] "GET / HTTP/1.1" 200 -
127.0.0.1 - - [06/Apr/2019 22:55:41] "GET /pyqt/ HTTP/1.1" 200 -
127.0.0.1 - - [06/Apr/2019 22:55:56] "GET /test.py HTTP/1.1" 200 -
因为当前目录下没有 index.html 文件,所以显示的是类似 FTP 站点上的那种文件列表。
当然,http.server 也可以通过 Python 的 -m
选项在命令行中直接调用:$ python -m http.server
默认会在 8000 端口打开 HTTP 服务,也可以手动指定端口:$ python -m http.server 8080
同时,该服务会绑定本机的所有网络接口(0.0.0.0
),也可以手动指定:$ python -m http.server 8000 -b 127.0.0.1
其他支持的命令行选项可以参考帮助信息:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15$ 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]
pyftpdlib
pyftpdlib 是一个非常高效的异步的 FTP 库,可以为 Python 编写 FTP 服务提供高级的可移植的编程接口。
pyftpdlib 是 Python 的第三方库,需要使用包管理器安装:pip install pyftpdlib
安装成功以后,一个最基本的 FTP 服务器可以通过以下代码实现: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
40import os
from pyftpdlib.authorizers import DummyAuthorizer
from pyftpdlib.handlers import FTPHandler
from pyftpdlib.servers import FTPServer
def main():
# 初始化验证器
authorizer = DummyAuthorizer()
# 创建有读写权限的用户和只读权限的匿名用户
authorizer.add_user('user', '12345', '.', perm='elradfmwMT')
authorizer.add_anonymous(os.getcwd())
# 实例化 FTPHandler 类
handler = FTPHandler
handler.authorizer = authorizer
# 自定义提示信息
handler.banner = "pyftpdlib based ftpd ready."
# Specify a masquerade address and the range of ports to use for
# passive connections. Decomment in case you're behind a NAT.
#handler.masquerade_address = '151.25.42.11'
#handler.passive_ports = range(60000, 65535)
# FTP 服务监听于 0.0.0.0:2121
address = ('', 2121)
server = FTPServer(address, handler)
# 连接限制
server.max_cons = 256
server.max_cons_per_ip = 5
# 开启 FTP 服务
server.serve_forever()
if __name__ == '__main__':
main()
效果如下:1
2
3
4
5
6
7
8
9$ python ftp.py
[I 2019-04-07 15:08:37] >>> starting FTP server on :::2121, pid=9952 <<<
[I 2019-04-07 15:08:37] concurrency model: async
[I 2019-04-07 15:08:37] masquerade (NAT) address: None
[I 2019-04-07 15:08:37] passive ports: None
[I 2019-04-07 15:08:44] ::1:63265-[] FTP session opened (connect)
[I 2019-04-07 15:08:44] ::1:63265-[anonymous] USER 'anonymous' logged in.
[I 2019-04-07 15:08:44] ::1:63265-[anonymous] CWD D:\Program\python 250
[I 2019-04-07 15:08:44] ::1:63265-[anonymous] FTP session closed (disconnect).
pyftpdlib 从 0.6.0 版本开始是完全支持 FTPS (FTP over TLS/SSL) 的,需要提前安装好 PyOpenSSL 模块:pip install pyopenssl
示例代码如下: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"""
An RFC-4217 asynchronous FTPS server supporting both SSL and TLS.
Requires PyOpenSSL module (http://pypi.python.org/pypi/pyOpenSSL).
"""
from pyftpdlib.servers import FTPServer
from pyftpdlib.authorizers import DummyAuthorizer
from pyftpdlib.handlers import TLS_FTPHandler
def main():
authorizer = DummyAuthorizer()
authorizer.add_user('user', '12345', '.', perm='elradfmwMT')
authorizer.add_anonymous('.')
handler = TLS_FTPHandler
handler.certfile = 'keycert.pem'
handler.authorizer = authorizer
# requires SSL for both control and data channel
#handler.tls_control_required = True
#handler.tls_data_required = True
server = FTPServer(('', 21), handler)
server.serve_forever()
if __name__ == '__main__':
main()
TLS_FTPHandler 类需要至少一个 certfile
和一个可选的 keyfile
。可以参考 Apache FAQs 自行生成证书文件,也可以直接下载 pyftpdlib 提供的示例文件 keycert.pem。
FTP 客户端可以使用 WinSCP,截图如下:
运行效果:1
2
3
4
5
6
7
8$ python ftps.py
[I 2019-04-07 17:20:24] >>> starting FTP+SSL server on :::21, pid=5028 <<<
[I 2019-04-07 17:20:24] concurrency model: async
[I 2019-04-07 17:20:24] masquerade (NAT) address: None
[I 2019-04-07 17:20:24] passive ports: None
[I 2019-04-07 17:20:36] ::1:64934-[] FTP session opened (connect)
[I 2019-04-07 17:20:36] ::1:64934-[user] USER 'user' logged in.
[I 2019-04-07 17:20:36] ::1:64934-[user] CWD D:\Program\python 250
同 http.server 类似,pyftpdlib 也可以在命令行中通过 python -m
直接调用:1
2
3
4
5$ python -m pyftpdlib
[I 2019-04-07 17:27:51] >>> starting FTP server on 0.0.0.0:2121, pid=1100 <<<
[I 2019-04-07 17:27:51] concurrency model: async
[I 2019-04-07 17:27:51] masquerade (NAT) address: None
[I 2019-04-07 17:27:51] passive ports: None
匿名用户有写权限:$ python -m pyftpdlib -w
设置特定的监听地址、端口号和 home 目录:$ python -m pyftpdlib -i localhost -p 8021 -d /home/someone
设置登录用户和密码(匿名用户则会被禁用)$ python -m pyftpdlib -u username -P password
获取帮助信息:$ python -m pyftpdlib -h