目录
1 先简单回顾下客户端和服务端通信的知识
2 服务端常用函数
3 客户端常用函数
4 服务端和客户端都用的函数
5 示例介绍客户端和服务端通信过程
6 建立服务端套接制
7 创建服务端函数socket.create_server()
8 创建客户端套接字
9 客户端连接函数socket.create_connection()
10 socket使用with ... as 上下文管理器
socket 是 Python 标准库中的一个模块,它提供了低级别的网络通信接口。使用 socket 模块,你可以创建客户端和服务器应用程序,以便在网络上进行数据交换。
先简单回顾下客户端和服务端通信的知识
以前我记得面试的时候经常会被问到一个问题:当在浏览器上输入百度域名并回车后,我们能看到浏览器返回了首页的内容,那中间的通信过程是什么?
当然中间经历的过程涉及到了很多通信知识,比如ip地址/tcp协议/http协议/DNS协议等(OSI的七层模型),浏览器的html/js/css等等方方面面,大家可以自行学习。
那要想完成客户端到服务端的tcp连接,首先服务器端要监听ip和port,等待很多个客户端主动发起连接,从而发送数据进行通信。而socket模块的函数就可以支持完成通信过程,我们先介绍下socket的一些常用函数。
服务端常用函数:
bind(address)方法用于将套接字绑定到指定的地址。地址通常是一个包含IP地址和端口号的元组,例如 ('localhost', 80)。
listen(backlog) 方法用于在服务器端套接字上启用监听,等待客户端的连接。backlog 参数指定了最多可以挂起的连接数。
accept() 方法用于接受一个客户端的连接请求,并返回一个新的套接字对象和客户端的地址。这个方法通常用在服务器端,用于处理多个客户端的连接。
客户端常用函数
connect(address) 方法用于客户端连接到指定的服务器地址。地址是一个包含服务器IP地址和端口号的元组。
服务端和客户端都用的函数
send() 和 sendall()
send(bytes) 方法用于发送数据到连接的套接字。sendall(bytes) 方法也是用于发送数据,但它会尝试发送所有数据,直到没有数据需要发送或发生错误。
recv(bufsize, flags=0) 方法用于接收套接字的数据。bufsize 参数指定了要接收的最大字节数。
close()方法用于关闭套接字连接。一旦套接字被关闭,就不能再发送或接收数据。
示例介绍客户端和服务端通信过程
建立服务端套接字
import socket
# 创建一个 socket 对象 基于ipv4和tcp
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 绑定到本地地址和端口
server_address = ('localhost', 8080)
server_socket.bind(server_address)
# 开始监听连接
server_socket.listen(1)
print('Waiting for a connection...')
#接收客户端的连接
connection, client_address = server_socket.accept()
print(f'Connection from {client_address}')
#接收客户端发送的数据
data = connection.recv(1024)
print(f'Received {len(data)} bytes from {client_address}')
#解码2进制内容
if data:
print(data.decode())
senddata = data.decode() + ',已收到该消息。'
else:
senddata = '没有收到消息,等待中'
# 往客户端发送数据
connection.sendall(senddata)
#关闭连接
connection.close()
执行该python程序后:服务端会等待客户端连接:
Waiting for a connection...
创建服务端函数socket.create_server()
python3.9及以上版本提供了socket.create_server() 函数允许快速创建一个套接字并绑定到指定的地址和端口,同时它还可以选择性地监听套接字。这个函数的原型如下:
socket.create_server(address, family=socket.AF_INET, backlog=None, dualstack_ipv6=False)
参数解释:
- address:一个元组,包含主机地址和端口号,例如 ('localhost', 8000)。
- family:地址族,默认为 socket.AF_INET(IPv4),也可以使用 socket.AF_INET6(IPv6)。
- backlog:指定最多连接数,待处理队列的长度。如果未指定或为 None,则使用默认值。
- dualstack_ipv6:一个布尔值,如果为 True,并且地址族是 socket.AF_INET6,则创建一个双栈套接字,该套接字可以在 IPv4 和 IPv6 之间透明地工作。
以上创建的服务端可以简化:
import socket
server_address = ('localhost', 8080)
#创建服务端socket连接 返回套接字对象
server_socket = socket.create_server(server_address)
print('Waiting for a connection...')
#接收客户端的连接
connection, client_address = server_socket.accept()
print(f'Connection from {client_address}')
#接收客户端发送的数据
data = connection.recv(1024)
print(f'Received {len(data)} bytes from {client_address}')
#解码2进制内容
if data:
print(data.decode())
senddata = data.decode() + ',已收到该消息。'
else:
senddata = '没有收到消息,等待中'
# 往客户端发送数据
connection.sendall(senddata)
#关闭连接
connection.close()
创建客户端套接字
import socket
# 创建一个 socket 对象 基于ipv4和tcp
client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 连接到刚才的服务端的地址和端口
server_address = ('localhost', 8080)
client_socket.connect(server_address)
# 发送数据 往服务端发送数据
message = 'hello world!'
client_socket.sendall(message.encode())
#等待接收服务端发送的数据
data = client_socket.recv(1024)
print(f'Received {len(data)} bytes {data.decode()}')
# 关闭连接
client_socket.close()
执行客户端python程序后,日志打印如下,接收到了服务端发送的内容:
Received 38 bytes hello world!,已收到该消息。
而服务端也收到了客户端发送的内容:
Received 14 bytes from ('127.0.0.1', 51223)
hello world!
客户端连接函数socket.create_connection()
用于简化创建套接字并与服务器建立 TCP 连接的过程。这个函数会创建套接字,尝试连接到指定的地址,并返回连接好的套接字对象。如果连接失败,它会引发一个异常。
socket.create_connection() 的原型如下:
create_connection(address, timeout=None, source_address=None)
参数解释:
- address:一个包含服务器地址和端口号的元组,例如 ('localhost', 8080)。
- timeout:可选参数,指定连接超时时间(秒)。如果连接在超时时间内未完成,则抛出异常。默认为 None,表示不设置超时。
- source_address:可选参数,一个包含源地址和端口号的元组,用于绑定套接字到特定的本地地址和端口。
import socket
# 创建一个 socket 对象 基于ipv4和tcp 连接到刚才的服务端的地址和端口
server_address = ('localhost', 8080)
client_socket=socket.create_connection(server_address)
# 发送数据 往服务端发送数据
message = 'hello world!'
client_socket.sendall(message.encode())
#等待接收服务端发送的数据
data = client_socket.recv(1024)
print(f'Received {len(data)} bytes {data.decode()}')
# 关闭连接
client_socket.close()
socket使用with ... as 上下文管理器
socket这个class支持__enter__() 和__exit__()魔法方法,所以也支持使用with ..as 语句。当程序退出时自动关闭socket连接
import socket
with socket.create_connection(('localhost', 8000)) as sock:
# 使用 sock 发送和接收数据
pass # 在这里编写你的代码
共勉: 东汉·班固《汉书·枚乘传》:“泰山之管穿石,单极之绠断干。水非石之钻,索非木之锯,渐靡使之然也。”
-----指水滴不断地滴,可以滴穿石头;
-----比喻坚持不懈,集细微的力量也能成就难能的功劳。
----感谢读者的阅读和学习,谢谢大家。