本教程将介绍Python中的套接字以及如何使用套接字模块在Python中构建HTTP服务器和客户端。它还将介绍Tornado,一个Python网络库,它是长轮询、WebSockets和其他需要与每个用户建立长期连接的应用的理想选择。
什么是套接字?
套接字是两个应用程序之间的链接,它们可以相互通信(可以是在一台机器上的本地通信,也可以是在不同地点的两台机器之间的远程通信)。
基本上,套接字作为两个实体之间的通信链接,即服务器和客户端。服务器将发出被客户要求的信息。例如,当你访问这个页面时,浏览器创建了一个套接字并连接到服务器。
套接字模块
为了创建一个套接字,你使用socket.socket() 函数,其语法就像这样简单:
import socket
s= socket.socket (socket_family, socket_type, protocol=0)
下面是参数的描述:
- socket_family:代表地址(和协议)家族。它可以是AF_UNIX或AF_INET。
- socket_type:代表套接字类型,可以是SOCK_STREAM或SOCK_DGRAM。
- 协议:这是一个可选的参数,它通常默认为0。
在获得你的套接字对象后,你可以根据需要使用套接字模块中的方法创建一个服务器或客户端。
创建一个简单的客户端
在我们开始之前,让我们看看 Python 中可用的客户端套接字方法:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)s.connect():启动一个TCP服务器连接。
要创建一个新的套接字,首先要导入套接字类的套接字方法:
import socket
接下来,我们将创建一个流(TCP)套接字,如下所示:
stream_socket = socket.socket( socket.AF_INET, socket.SOCK_STREAM )
AF_INET 参数表示你正在请求一个互联网协议(IP)套接字,特别是IPv4。第二个参数是TCP套接字的传输协议类型SOCK_STREAM 。此外,你也可以通过指定套接字AF_INET6 参数来创建一个IPv6套接字。
指定服务器:
server = "localhost"
指定我们要通信的端口:
port =80
将套接字连接到服务器正在监听的端口:
server_address = ((host, port))
stream_socket.connect(server_address)
值得注意的是,主机和端口必须是一个元组。
向服务器发送一个数据请求:
message = 'message'
stream_socket.sendall(message)
从服务器获取响应:
data = sock.recv(10)
print(data)
要关闭一个连接的套接字,你使用close方法:
stream_socket.close()
下面是客户端/服务器的完整代码:
import socket
# Create a TCP/IP socket
stream_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# Define host
host = 'localhost'
# define the communication port
port = 8080
# Connect the socket to the port where the server is listening
server_address = ((host, port))
print("connecting")
stream_socket.connect(server_address)
# Send data
message = 'message'
stream_socket.sendall(message.encode())
# response
data = stream_socket.recv(10)
print(data)
print('socket closed')
建立一个简单的服务器
现在让我们来看看一个简单的Python服务器。下面是Python中可用的套接字服务器方法:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)s.bind():将地址(主机名,端口号)与套接字绑定。s.listen():设置并启动TCP监听器。s.accept():接受TCP客户端连接。
我们将遵循以下步骤:
- 创建一个套接字。
- 将套接字绑定到一个端口。
- 开始接受该套接字的连接。
这里是服务器程序:
import socket
# Create a TCP/IP socket
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# Define host
host = 'localhost'
# define the communication port
port = 8080
# Bind the socket to the port
sock.bind((host, port))
# Listen for incoming connections
sock.listen(1)
# Wait for a connection
print('waiting for a connection')
connection, client = sock.accept()
print(client, 'connected')
# Receive the data in small chunks and retransmit it
data = connection.recv(16)
print('received "%s"' % data)
if data:
connection.sendall(data)
else:
print('no data from', client)
# Close the connection
connection.close()
现在服务器已经准备好接收连接了。
现在在不同的终端窗口中运行客户端和服务器程序,这样它们就可以互相通信了。
服务器输出
$ python server.py
waiting for a connection
('127.0.0.1', 45504) connected
received "b'message'
客户端输出
$ python client.py
connecting
b'message'
socket closed
要查看哪些端口目前正在使用,你可以使用nestat -ntlp命令,如下图所示
龙卷风框架
Tornado框架是Python中可用于网络编程的库之一。在本节中,我们将讨论这个库,并展示如何使用它来构建WebSockets。
Tornado是一个Python网络框架和异步网络库。Tornado使用非阻塞的网络I/O,因此能够扩展到数以万计的开放连接。这一特性使其成为长轮询、WebSockets以及其他需要与每个用户建立长期连接的应用程序的理想选择。
让我们创建一个简单的Tornado WebSocket:
import tornado.ioloop
import tornado.web
class ApplicationHandler(tornado.web.RequestHandler):
def get(self):
self.message = message = """<html>
<head>
<title>Tornado Framework</title>
</head>
<body
<h2>Welcome to the Tornado framework</h2>
</body>
</html>"""
self.write(message)
if __name__ == "__main__":
application = tornado.web.Application([
(r"/", ApplicationHandler),
])
application.listen(5001)
tornado.ioloop.IOLoop.instance().start()
在上面的代码中:
- 我们定义了
ApplicationHandler,该类作为请求的处理程序,并使用write()方法返回响应。 main方法是该程序的入口。tornado.web.Application创建了一个网络应用程序的基础,并接受一个处理程序的集合,即ApplicationHandler。- 应用程序在5000端口监听,客户端可以使用相同的端口与该应用程序通信。
tornado.ioloop.IOLoop.instance().start()为一个应用程序创建一个非阻塞的线程。
如果我们运行该应用程序,我们将得到如下截图所示的结果:
Tornado还与asyncio模块集成,允许你在同一个事件循环中使用这两个库。Asyncio是一个Python库,它将使你能够用 async/await 语法编写coroutines 。
同步与异步编程
同步编程是指任务是有顺序的。所有的任务都遵循一个特定的顺序,这意味着每个任务在下一个任务开始之前都要从头到尾完成。
同步编程的缺点是,如果一个特定的任务需要相当长的时间来执行,前面的任务必须等待该任务完成;这将导致延迟。
然而,异步编程意味着任务可以同时进行,无需等待其他工作完成执行。
异步通信主要用于聊天应用程序,以及显示实时数据的应用程序。
当从API中获取数据时,异步编程拯救了这一天。从API中获取数据或进行HTTP请求可能需要比预期更长的时间;与其等待http请求完成,不如在HTTP调用上应用异步函数。
异步调用释放了程序,使其在其他领域继续执行。
异步编程的另一个优点是,如果你正在进行多个请求,其中一个请求突然停止,这不会影响到其他的请求,因为程序被允许转移到下一个任务。
假设你有一个应用程序,多次向外部API服务器请求。通过同步编程,应用程序将不得不等待,直到进行中的API完成HTTP请求并返回响应,使你的应用程序变得缓慢。
例如,假设我们想请求一个外部API,如天气API。让我们用Tornado执行同步和异步调用,以获取伦敦市的天气信息:
# synchronous function
from tornado.httpclient import HTTPClient
key = 'my_secret_key'
def get_weather():
city = 'london'
url = 'https://api.weatherapi.com/v1/current.json?key='+key+'&q='+city+'&aqi=no'
print(url)
http_client = HTTPClient()
response = http_client.fetch(url)
return (response.body)
# Asynchronous function
from tornado.httpclient import AsyncHTTPClient
secret_key = 'my_secret_key'
async def get_weather():
city = 'london'
url = 'http://api.weatherapi.com/v1/current.json?key='+key+'&q='+city+'&aqi=no'
print(url)
http_client = AsyncHTTPClient()
response = await http_client.fetch(url)
return (response.body)
总结
现在你一定已经掌握了Python中套接字编程的基础知识,以及如何建立一个简单的服务器和客户端。请自由地通过建立你自己的聊天客户端进行试验。更多信息,请访问官方Python文档。