套接字:程序在网络间进行数据传输的一种技术手段,Python使用socket模块。
- 流式套接字(SOCK_STREAM):采用TCP协议,以字节流方式实现数据传输(面向连接、可靠)
- 数据报套接字(SOCK_DGRAM):采用UDP协议,以数据报形式实现数据传输(面向无连接、不可靠)
socket模块相关细节参见https://docs.python.org/zh-cn/3/library/socket.html
一、流式套接字:TCP传输
1.将实现通信的两台主机,分别用server端和client端表示,且只有相同类型的套接字才能进行通信
2.为防止server端和client端都阻塞,recv和send要配合
1.server端
①.创建套接字
sockfd = socket.socket(family=AF_INET, type=SOCK_STREAM,proto=0, fileno=None)
参数:family 网络地址类型
AF_INET默认表示ipv4;
AF_INET6表示ipv6;
AF_UNIX只用于单一的Unix系统进程间通信
type 套接字类型
SOCK_STREAM 流式,用于TCP传输;
SOCK_DGRAM 数据报,用于UDP传输;
SOCK_RAW 原始套接字(可以处理普通的套接字无法处理的ICMP、IGMP等报文)
可以提供可靠的UDP传输,保证数据交付但不保证顺序
proto 默认为0,表示系统会根据地址格式和套接字类别,自动选择一个合适的协议
返回值:套接字对象
②.绑定本机网络地址
sockfd.bind(address)
参数:address的格式取决于地址簇,默认AF_INET对应二元元组(ip, port)
③.设置监听——将套接字设置为监听套接字,确定监听队列大小
sockfd.listen([backlog])
参数:backlog是监听队列的大小,为0表示默认的合理值,最低为0。它指定系统accept的连接数,超过后将拒绝新连接。python3.5及以后版本为可选参数。
④.接受一个连接——等待处理客户端连接请求
connfd, addr = sockfd.accept()
返回值:返回二元元组(connfd, addr)
connfd 新的套接字对象,用于连接客户端收发数据
addr 连接的客户端地址
⑤.消息收发
1)接收(套接字)消息,来自client端
data = connfd.recv(bufsize)
参数:bufsize指定一次接收的最大数据量
返回值:返回一个字节对象,表示接收到的数据
2)发送消息(给套接字),好让client端接收
n = connfd.send(bytes)
参数:bytes格式的数据
返回值:返回已发送的字节数
connfd.sendall(bytes)
与send()方法不同,本方法持续从bytes发送数据,直到所有数据都已发送或发生错误为止;
send成功会返回None,出错抛异常,并不能统计发送多少数据
⑥.关闭套接字
sockfd.close()
2.client端
①.创建套接字
sockfd = socket.socket(family=AF_INET, type=SOCK_STREAM,proto=0, fileno=None)
②.请求连接服务器
sockfd.connect(address)
参数:address的格式取决于地址簇,默认AF_INET对应二元元组(ip, port)
③.收发消息
1)发送消息(给套接字),好让server端接收
n = connfd.send(bytes)
参数:bytes格式的数据
返回值:返回已发送的字节数
2)接收(套接字)消息,来自server端
data = connfd.recv(bufsize)
参数:bufsize指定一次接收的最大数据量
返回值:返回一个字节对象,表示接收到的数据
④.关闭套接字
sockfd.close()
3.TCP网络收发缓冲区
作用:用于协调数据的收发速度(这里的速度并不指代网速,而是一次接收或发送的字节数量)
说明:send和recv实际是向缓冲区发送接收数据,当缓冲区不为空时,recv就不会阻塞
TCP粘包:
1. 概念:多个数据包被一次接收,前后相接,像是被“粘”在一起(没有明显的消息边界)的现象
2. 原因:
1. 接发数据的时机没有配合好(受server端和client端程序执行时间的影响)
2. TCP以字节流方式传输数据,且默认使用Nagle算法(尽量减少网络中的报文段数量)
3. 解决方法:
1. 手动添加消息边界
2. 在程序的适当位置设置一定的睡眠时间
二、数据报套接字:udp传输
1.server端
①.创建数据报套接字
sockfd = socket.socket(family=AF_INET, type=SOCK_DGRAM, proto=0, fileno=None)
②.绑定本机网络地址
sockfd.bind(address)
③.消息收发
1)接收(套接字)消息
data, address = sockfd.recvfrom(bufsize)
参数:bufsize指定一次接收的最大数据量
返回值:返回元组(bytes, address),其中bytes时字节对象,表示接收数据,address是发送端套接字地址
2)发送消息(给套接字)
n = sockfd.sendto(data, address)
参数:data是bytes格式的发送数据
address是目标地址
返回值:返回已发送的字节数
④.关闭套接字
sockfd.close()
2.client端
①.创建数据报套接字
sockfd = socket.socket(family=AF_INET, type=SOCK_DGRAM)
②.收发消息
1)发送消息(给套接字)
n = sockfd.sendto(data, address)
参数:data是bytes格式的发送数据
address是目标地址
返回值:返回已发送的字节数
2)接收(套接字)消息
data, address = sockfd.recvfrom(bufsize)
参数:bufsize指定一次接收的最大数据量
返回值:返回元组(bytes, address),其中bytes时字节对象,表示接收数据,address是发送端套接字地址
③.关闭套接字
sockfd.close()
三、部分socket模块函数
更多方法请参见socket函数:docs.python.org/zh-cn/3/library/socket.html
import socket
socket.gethostname() # 获取主机名
socket.gethostbyname('www.jd.com') # 获取主机ip地址
socket.getservbyname('mysql') # 获取服务端口号
socket.getservport(3306) # 获取端口对应服务
socket.inet_aton('192.168.1.1') # 将IP地址转换为bytes子串
socket.inet_ntoa(b'\xc0\xa8\x01\x01') # 将bytes子串转换为IP地址
四、套接字对象属性/函数
from socket import *
sockfd = socket(AF_INET, SOCK_STREAM)
sockfd.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1) # 设置套接字端口立即重用
sockfd.bind(('0.0.0.0', 8888))
sockfd.listen(3)
connfd, addr = sockfd.accept()
print("Connect:", addr)
connfd.recv(1024)
sockfd.type # 套接字类型
sockfd.family # 套接字地址类型
sockfd.getsockname() # 获取套接字绑定地址
sockfd.fileno() # 获取套接字的文件描述符
sockfd.getpeername() # 获取连接套接字客户端的地址
sockfd.setsockopt(level, optname, value) # 设置套接字选项
参数:level 选项类别
SOL_SOCKET 基本套接口
IPPROTO_IP IPv4套接口
IPPROTO_IPV6 IPv6套接口
IPPROTO_TCP TCP套接口
optname 选项名称
SOL_SOCKET
value 选项值
sockfd.getsockopt(level, optname) # 获取套接字选项值
level级别为SOL_SOCKET时,对应的选项及其含义
选项 | 含义 |
SO_BROADCAST | 允许广播地址发送和接收数据包,只对UDP有效 |
SO_REUSEADDR | 当socket关闭后,允许立刻重用本地地址和端口 |
SO_BINDTODEVICE | 可以使socket只在某个特殊的网络接口(网卡)有效 |
SO_DONTROUTE | 禁止通过路由器和网关往外发送数据包,主要为了安全而用在以太网上UDP通信的一种方法 不管目的地是何IP地址,都可以防止数据离开本地网络 |
SO_KEEPALIVE | 可以使TCP通信的数据包保持连续性。这些数据包可以在没有数据传输的时候,使得通信的双方确定连接是保持的 |
SO_LINGER | 延迟关闭连接 |
SO_OOBINLINE | 可以把收到的不正常数据看成是正常的数据,也就是说会通过一个标准的对recv()的调用来接收这些数据 |
SO_TYPE | 获取套接字类型 |
SO_ERROR | 获取套接字错误 |
SO_DEBUG | 允许调试 |