recvfrom,sendto
ssize_t recvfrom(
int sockfd,
void* buff,
size_t nbytes,
int flags,
struct sockaddr* from,
socklen_t *addrlen);
ssize_t sendto(
int sockfd,
const void* buff,
size_t nbytes,
int flags,
const struct sockaddr* to,
socklen_t addrlen);
使用sendto/recvfrom
发送和接收数据量为0
的数据报是允许的.
recvfrom
返回0
,并不意味着收到对端FIN
(sendto,recvfrom
使用的场景下没有连接的概念)
注意点
每个UDP
套接字有一个接收缓冲区.到达该套接字的每个数据报都进入这个套接字的接收缓冲区.
当进程针对此UDP
套接字调recvfrom
时,从缓冲区取出一个数据报返回给进程.UDP
一般无发送缓冲区.
sendto
调sendto
时,若关联的套接字未绑定一个明确的本地端口,本地IP
.发出数据报的源IP
,源端口会由内核自动指定.sendto
在将数据报送到本机某接口输出队列即返回.
若后续此数据报发送时遭遇失败,对未执行bind+connect
的UDP
套接字,应用无法获知之前发送失败的信息.
对执行了bind+connect
的UDP
套接字,可获知之前发送失败的信息.
未执行bind+connect
的UDP
套接字,首次sendto
时,内核确定该套接字本端端口,且后续维持不变.每次sendto
时,内核依据数据报实际外出接口,确定其本端IP
.
服务端要向确认本次接收数据报的目的IP
,对IPV4
,设置IP_RECVDSTADDR+recvmsg
,对IPV6
,设置IPV6_PKTINFO+recvmsg
是一种可取的方法.服务端要确认本次接收数据报的目的端口,用getsockname
即可.
调recvfrom
,若关联的套接字未绑定一个明确的本地IP
在弱端系统下,只要数据报目的地址为本机IP
地址之一,即使数据报到达接口和其目的地址不一致,也能被本机接收.
这种情况下,有时,我们希望知道所接收数据报的目的IP是什么
一种方法:
多宿主机上,针对每个本机IP+
服务端口创建一个套接字,且对所有此服务的套接字调select
,select
返回时,对应套接字的本机IP
即为其所收到数据报的目的IP
.
这样产生一种情形:
客户A
通过UDP
套接字S1
向一个指定IP
地址,端口号的主机发UDP
消息M1
.然后客户等待接收回复.
指定IP
地址的主机上,一个未绑定IP
地址的UDP
套接字D1
在指定端口上等待接收请求.
D1
会收到M1
,然后发回回复A M1
.
A M1
的源IP
地址会由内核设定为外出接口的IP
地址.(可能和客户指定的IP
地址不同,因为此主机有多个IP
地址)
客户A
收到回复A M1
.A M1
的源IP
地址和客户A
发送请求时使用的目的IP
地址不同.但此回复确实是来自目的主机的回复.
要解决此问题,可选的方案是:
客户把发送请求的目的IP
地址借助DNS
得到主机名.客户把收到回复的源IP
地址也用DNS
得到主机名.两种如果匹配,则即为目的主机发回的回复.
sendto
在把数据报放入主机某接口外出输出队列后,即成功返回.但后续传输数据有可能发生错误.但错误无法通知到应用.仅仅在UDP
套接字采用connect
关联到一个确定的对端时,诸如发生对端不可达,对端相应端口没套接字等才会通知到应用.
connect
给UDP
调connect
1.没有三路握手
内核检查是否存在已知错误,记录对端IP
地址,端口号,立即返回.
2.connect
下UDP
套接字
a. 未连接UDP
套接字
b. 已连接UDP
套接字
后续用此套接字send
时,不必指定目的IP
,目的端口.(改用send/write
替代sendto
)
后续用此套接字recv
时,不必获取源IP
,源端口.(改用read/recv/recvmsg
替代recvfrom
)
只有目的IP+
目的端口为此套接字本端IP
和本端端口,源IP+
源端口为connect
指定的IP
和端口的数据报,才会投递到本UDP
套接字.
对UDP
执行connect
时,若之前未对其执行bind
,则内核此时设置其本端IP+
本端端口,且设置后维持不变.(因为维持不变,所以可用getsockname
获取这些信息)后续用此套接字send
时,产生了异步错误,进程可在recv
时获知.
2.对UDP
套接字多次调connect
对TCP
套接字只能调connect
一次,对已经连接UDP
套接字再次调connect
.若把套接字地址结构的地址族设为AF_UNSPEC
,地址结构其余部分全部为0
,可用于断开连接.若connect
指定了正常的主机IP+
端口,相当于对连接定向到另一个目的地.
UDP中的外出接口
connect
执行后,返回的套接字不仅绑定了目的IP
,目的端口.还绑定了本地IP
,本地端口.
高级UDP
1.确定外来UDP
数据报目的地址
若实现支持IP_RECVDSTADDR
选项,用选项
若不支持,捆绑所有接口地址+select
2.UDP
优势
a. 如要使用广播,多播,则必须用UDP
b. UDP
没有连接建立,拆除开销
3.TCP
优势
a. 接收确认机制(用于保证有序接收,识别重传)
b. 流量控制
接收端可以告知发送端自己的接收缓冲区大小,发送端未确认数据量不能超过告知的缓冲区大小
c. 慢启动,拥塞避免
原则:
a. 多播,广播必须用UDP
b. 非持续,短连接适合UDP
,如需可靠性,需在应用层添加序号,超时,重传,流量控制,拥塞避免等特性.
c. 持续的长连接适合TCP
如UDP
下服务端想采用并发模型,常见模式为:
服务端在服务端口绑定套接字,等待读取客户申请,读到申请后,fork
子进程,子进程中创建新UDP
套接字A
,绑定临时端口,发给客户回复.此后客户向子进程的套接字A
发送请求与接收回复.