原文链接:https://segmentfault.com/a/1190000016501735
1、背景:Socket 应用最常见的类型就是客户端/服务器 应用,服务器用来等待客户端的连接。
2、Socket API概览:主要用到的Socket API函数如下
- socket()
- bind()
- listen()
- accept()
- connect()
- connect_ex()
- send()
- recv()
- close()
【备注1】:socket.socket()
创建一个类型为 socket.SOCK_STREAM
的 socket 对象,默认将使用 Transmission Control Protocol(TCP) 协议
【备注2】:Socket API 的调用顺序和 TCP 的数据流:
- 「监听」Socket 做的事情就像它的名字一样。它会监听客户端的连接,当一个客户端连接进来的时候,服务器将调用
accept()
来「接受」或者「完成」此连接 - 客户端调用
connect()
方法来建立与服务器的链接,并开始三次握手。握手很重要是因为它保证了网络的通信的双方可以到达,也就是说客户端可以正常连接到服务器,反之亦然 - 上图中间部分往返部分表示客户端和服务器的数据交换过程,调用了
send()
和recv()
方法 - 下面部分,客户端和服务器调用
close()
方法来关闭各自的 socket
例子:1)打印程序服务端echo-server.py
import socket
HOST = '127.0.0.1' # 标准的回环地址 (localhost)
PORT = 65432 # 监听的端口 (非系统级的端口: 大于 1023)
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: # socket.AF_INET表示IPv4地址族
s.bind((HOST, PORT)) # 如果host是空字符,服务器将接受本机所有可用的IPv4地址
s.listen() # 使服务器可以接受连接请求
# 如果你的服务器需要同时接收很多连接请求,增加backlog参数的值可以加大等待链接请求队列的长度,最大长度取决于操作系统。
conn, addr = s.accept() # 获取客户端socket连接对象 conn
# accept()方法阻塞并等待传入连接。当一个客户端连接时,它将返回一个新的socket对象,对象中有表示当前连接的conn和一个由主机、端口号组成的IPv4/v6连接的元组。注意!!!调用accept()方法后拥有了一个新的socket对象,你将用这个socket对象和客户端进行通信。和监听的socket不同的是后者只用来授受新的连接请求
with conn:
print('Connected by', addr)
while True:
data = conn.recv(1024) # 最多每次接受1024个字节
# 1024是缓冲区数据大小限制最大值参数bufsize;send()方法也是这个原理,它返回发送内容的字节数,结果可能小于传入的发送内容;
# 我们可以使用sendall()方法来回避这个问题;和send()方法不一样的是,sendall()方法会一直发送字节,只到所有的数据传输完成或者中途出现错误。成功的话会返回None引用
if not data:
break
conn.sendall(data)
2)打印程序客户端echo-client.py
import socket
HOST = '127.0.0.1' # 服务器的主机名或者 IP 地址
PORT = 65432 # 服务器使用的端口
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.connect((HOST, PORT))
s.sendall(b'Hello, world')
data = s.recv(1024)
print('Received', repr(data)) # repr()函数将对象转化为一个对象的string格式。
3)运行打印程序的客户端和服务端
$ ./echo-server.py,此时命令行将被挂起,因为程序有一个阻塞调用conn, addr = s.accept(),等待着客户端的连接——$ ./echo-client.py