从今天开始从《Beginning Python》中翻译相关的文章进行学习,主要是关于网络编程方面。今天开始第一篇。
当你要求 imaplib 或者 smtplib 连接到某台主机的某个端口里,在这后面 Python 打开一个连接到那个主机和端口。连接一旦建立,服务端就打开一个相应的连接到你的电脑。Python的 “Sockets” 对象将进入和出去的连接隐藏在一个界面底下。一个 Socket 就像一个文件,你可以在同一时间对它进行读写。
为实现一个基于 TCP/IP 协议的客户端,你要打开一个 Sockets 给相应的服务端。通过向 Sockets 写入你可以将数据发送给服务端,同时你也可以通过读取 Socket 来获取服务端发送给你的数据。服务端的实现正好相反:你绑定一个 Socket 到一个主机名和端口,然后等待一个客户端的连接。一旦你有一个客户端连接上,你就可以像客户端一样通过对 Socket 的读写来接收和发送数据。
在网络上发送 1 byte 的数据要做相当大量的工作,但是通过 TCP/IP 和 socket 库,你可以跳过其中大量的工作。你不必费劲去计算该如何让你的数据通过全球到达目的,因为这些工作 TCP/IP 都会为你处理。你也不必担心该如何对数据进行 TCP/IP 包装, 因为这些 socket 库都会替你处理。
作为第一个 socket 例子,下面是一个超级简单的 socket 服务端,SuperSimpleSocketServer.py:
SuperSimpleSocketServer#!/usr/bin/python import socket import sys if len(sys.argv) < 3: print ‘Usage: %s [hostname] [port number]’ % sys.argv[0] sys.exit(1) hostname = sys.argv[1] port = int(sys.argv[2]) #Set up a standard Internet socket. The setsockopt call lets this #server use the given port even if it was recently used by another #server (for instance, an earlier incarnation of #SuperSimpleSocketServer). sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) #Bind the socket to a port, and bid it listen for connections. sock.bind((hostname, port)) sock.listen(1) print “Waiting for a request.” #Handle a single request. request, clientAddress = sock.accept() print “Received request from”, clientAddress request.send(‘-=SuperSimpleSocketServer 3000=-\n’) request.send(‘Go away!\n’) request.shutdown(2) #Stop the client from reading or writing anything. print “Have handled request, stopping server.” sock.close()
这个服务端只能处理单个请求。一旦任何客户端连接到它绑定的端口,它将告诉客户端走开,然后关闭连接,停止响应请求,并退出。
试一下:用 Telnet 连接 SuperSimpleSocketServer
Telnet 是一个很简单的 TCP/IP 客户端。你以主机名和端口为参数调用它,它将连接到指定的端口,然后你就可以不管了。你输入的任何东西都会通过 socket 发送到服务端,而服务端通过 socket 发送的任何东西也会显示到你的终端。Telnet 作为一个命令行程序包含在 Windows, Mac OS X 和 Unix 的安装包里, 因此你不用满地去找它。
其实这个 socket 服务端什么事都没有做,因此没有必要专门为它写一个客户端。为了测试它,只要启动服务端:
启动服务端$ python SuperSimpleSocketServer.py localhost 2000 Waiting for a request.
然后,在另外一个终端里,telnet 到服务端:
Telnet 到服务端$ telnet localhost 2000 Trying 127.0.0.1... Connected to rubberfish. Escape character is ‘^]’. -=SuperSimpleSocketServer 3000=- Go away! Connection closed by foreign host.
返回到服务端运行的终端,我应该可以看到类似下面的输入:
OutputReceived request from (‘127.0.0.1’, 32958) Have handled request, stopping server.
代码是怎么运行的
当你启动SuperSimpleSocketServer, 你将程序绑定到一个主机名为 "local-host” 的主机的端口2000 。当脚本调用 socket.accept 时, 服务端停止运行并“阻止”在 socket 输入,等待某个客户端连接到服务端。
当你运行 telnet 命运打开一个 TCP/IP 连接到 SuperSimpleSocketServer,
socket.accept 从等待中返回。最后,某个客户端连接到了服务端。socket.accept 的返回值提供给服务端与客户端通信的必备工具:一个 socket 对象和一个记录客户端网络地址的数组。服务端通过 socket 发送数据给客户端,然后关闭。并不再接受其它的 socket 连接。
比较难明白的是客户端的地址数组:('127.0.0.1’, 32958) 。你已经看到 127.0.0.1——这是一个特殊的 IP 地址,它指向本台电脑,即这个 IP 地址与 "local-host” 等同。一个从 127.0.0.1 的连接意味着客户端与服务端运行在同一台电脑上。如果你从另外一台电脑登录,显示的将被那一台电脑的 IP 地址替代。
32958 是客户端的一个临时或“短暂”的端口号码。看起来虽然像是一个,但实际上双向 "socket” 对象包含着两个单方向的连接:一个从客户端到服务端和一个从服务端到客户端。服务端启动时绑定的本机端口2000是所有客户端数据的目的(并不是说这个客户端有机会发送数据)。服务端发送的数据也需要有一个目的主机名和端口,但并不需要预先定义。一个服务端的端口经常是由控制服务端的人选择的,而临时端口是由客户端的操作系统决定的。多次运行这个程序,你将看到每个独立的 TCP/IP 连接将会被分配到不同的临时端口号。
第一次进行翻译,以前都是直接看英文文章只要自己看懂就可以,觉得翻译很简单。没想到真正要自己翻译的时候真的是很难的,看来以前是眼高手低了。以后要好好地练习提高。☺