# Python 套接字教程
> 原文: [http://zetcode.com/python/socket/](http://zetcode.com/python/socket/)
Python 套接字教程展示了如何使用套接字进行 Python 网络编程。 套接字编程是低级的。 本教程的目的是介绍包括这些低级详细信息的网络编程。 有更高级的 Python API,例如 Twisted,可能更适合。
在编程中,套接字是网络上运行的两个程序之间通信的端点。 套接字用于在客户端程序和服务器程序之间创建连接。
Python 的`socket`模块提供了与 Berkeley 套接字 API 的接口。
> **注意**:在网络中,“套接字”一词具有不同的含义。 它用于 IP 地址和端口号的组合。
## 网络协议
TCP/IP 是设备用于在互联网和大多数本地网络上进行通信的一组协议。 TCP 更可靠,具有大量错误检查并需要更多资源。 HTTP,SMTP 或 FTP 等服务使用它。 UDP 的可靠性要差得多,错误检查的能力也有限,所需资源也更少。 VoIP 等服务使用它。
`socket.SOCK_STREAM`用于为 TCP 创建套接字,而`socket.SOCK_DGRAM`为 UDP 创建套接字。
## 地址族
创建套接字时,必须指定其地址族。 然后,我们只能在套接字中使用该类型的地址。
* `AF_UNIX`,`AF_LOCAL` - 本地通讯
* `AF_INET` - IPv4 互联网协议
* `AF_INET6` - IPv6 互联网协议
* `AF_IPX` - IPX-Novell 协议
* `AF_BLUETOOTH` - 无线蓝牙协议
* `AF_PACKET` - 底层数据包接口
对于`AF_INET`地址族,指定了一对(主机,端口)。 `host`是一个字符串,表示互联网域表示法中的主机名(如`example.com`)或 IPv4 地址(如`93.184.216.34`),并且`port`是整数。
## Python 获取 IP 地址
使用`gethostbyname()`,我们获得主机的 IP 地址。
`get_ip.py`
```py
#!/usr/bin/env python
import socket
ip = socket.gethostbyname('example.com')
print(ip)
```
该示例打印`example.com`的 IP 地址。
```py
$ ./get_ip.py
93.184.216.34
```
这是输出。
## Python UDP 套接字示例
UDP 是一种通信协议,它通过网络传输独立的数据包,不保证到达且也不保证传递顺序。 使用 UDP 的一项服务是每日报价(QOTD)。
`qotd_client.py`
```py
#!/usr/bin/env python
import socket
with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as s:
message = b''
addr = ("djxmmx.net", 17)
s.sendto(message, addr)
data, address = s.recvfrom(1024)
print(data.decode())
```
该示例创建一个连接到 QOTD 服务的客户端程序。
```py
import socket
```
我们导入`socket`模块。
```py
with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as s:
```
创建了用于 IPv4 的数据报套接字。
```py
message = b''
```
我们发送空消息; QOTD 服务通过向套接字发送任意数据来工作; 它只是用引号引起来。 为了通过 TCP/UDP 进行通信,我们使用二进制字符串。
```py
addr = ("djxmmx.net", 17)
```
我们提供地址和端口。
```py
s.sendto(message, addr)
```
我们使用`sendto()`方法发送数据。
```py
data, address = s.recvfrom(1024)
```
UDP 套接字使用`recvfrom()`接收数据。 它的参数是缓冲区大小。 返回值是一对(数据,地址),其中数据是代表接收到的数据的字节字符串,而地址是发送数据的套接字的地址。
```py
print(data.decode())
```
我们将解码后的数据打印到终端。
```py
$ ./qotd_client.py
"Oh the nerves, the nerves; the mysteries of this machine called man!
Oh the little that unhinges it, poor creatures that we are!"
Charles Dickens (1812-70)
```
这是一个示例输出。
## Python TCP 套接字示例
是提供当前时间的服务器。 客户端无需任何命令即可直接连接到服务器,服务器以当前时间作为响应。
> **注意**:时间服务器来来往往,因此我们可能需要在
Today is a beautiful day. We go swimming and fishing.
Hello there. How are you?
``` 这是输出。 ## 回显客户端服务器示例 回显服务器将来自客户端的消息发送回去。 这是用于测试和学习的经典示例。 `echo_server.py` ```py #!/usr/bin/env python import socket import time with socket.socket() as s: host = 'localhost' port = 8001 s.bind((host, port)) print(f'socket binded to {port}') s.listen() con, addr = s.accept() with con: while True: data = con.recv(1024) if not data: break con.sendall(data) ``` 回显服务器将客户端消息发送回客户端。 ```py host = 'localhost' port = 8001 ``` 服务器在端口 8001 上的 localhost 上运行。 ```py s.bind((host, port)) ``` `bind()`方法建立通信端点。 它将套接字绑定到指定的地址。 套接字必须尚未绑定。 (地址的格式取决于地址系列。) ```py s.listen() ``` `listen()`方法使服务器可以接受连接。 服务器现在可以侦听套接字上的连接。 `listen()`具有`backlog`参数。 它指定系统在拒绝新连接之前允许的不可接受的连接数。 自 Python 3.5 起,此参数是可选的。 如果未指定,则选择默认的积压值。 ```py con, addr = s.accept() ``` 使用`accept()`,服务器接受连接。 它阻止并等待传入连接。 套接字必须绑定到一个地址并监听连接。 返回值是一对`(con, addr)`,其中`con`是可用于在连接上发送和接收数据的新套接字对象,而`addr`是绑定到连接另一端上的套接字的地址。 请注意,`accept()`创建了一个新的套接字,用于与客户端进行通信,该套接字与侦听套接字不同。 `echo_client.py` ```py #!/usr/bin/env python import socket with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: host = "localhost" port = 8001 s.connect((host, port)) s.sendall(b'hello there') print(str(s.recv(4096), 'utf-8')) ``` 客户端将消息发送到回显服务器。 ## 异步服务器示例 为了提高服务器的性能,我们可以使用`asyncio`模块。 `async_server.py` ```py #!/usr/bin/env python # from threading import current_thread import asyncio async def handle_client(reader, writer): data = (await reader.read(1024)) writer.write(data) writer.close() loop = asyncio.get_event_loop() loop.create_task(asyncio.start_server(handle_client, 'localhost', 8001)) loop.run_forever() ``` 现在,我们可以测试阻塞服务器和非阻塞服务器的性能。 ```py $ ab -c 50 -n 1000 http://localhost:8001/ ``` 例如,我们可以使用 Apache 基准测试工具测试性能。 在我们的例子中,命令发送 1000 个请求,一次发送 50 个。 在本教程中,我们展示了如何在 Python 中使用套接字创建简单的网络程序。 您可能也对以下相关教程感兴趣: [Python 字符串](/lang/python/strings/), [Python Jinja 教程](/python/jinja/)和 [Python 教程](/lang/python/),或列出[所有 Python 教程](/all/#python)。