我正在编写服务器正在发送数据和客户端的udp服务器/客户端应用程序 正在接收。当数据包丢失时,客户端应该向服务器发送nack。我将套接字设置为 O_NONBLOCK以便我可以注意到客户端是否没有收到数据包
if (( bytes = recvfrom (....)) != -1 ) {
do something
}else{
send nack
}
我的问题是,如果服务器没有开始发送数据包,则客户端的行为就像 数据包丢失并开始向服务器发送nack。 (当没有数据可用时,recvfrom失败)我想要一些建议如何在这些情况之间做出改变,如果服务器没有开始发送数据包,如果它发送,但数据包真的丢失
答案 0 :(得分:1)
您正在使用UDP。对于这个协议,如果有需要,它可以丢弃数据包。因此,就“发送的内容将会到达”而言,这是不可靠的。您在客户端需要做的就是检查所有需要到达的数据包,如果没有,请礼貌地与您的服务器交谈以重新发送您未收到的数据包。要实现这些东西并不容易,
如果你必须使用UDP传输大量数据,那么设计一个小的应用程序级协议来处理可能的数据包丢失和重新排序(这是TCP为你做的一部分)。我会选择这样的东西:
数据报的大小(加上IP和UDP标头)大小(比如1024字节),以避免IP碎片。 每个数据报的固定长度标题,包括数据长度和序列号,因此您可以将数据拼接在一起,并检测遗漏,重复和重新排序的部分。 来自接收方的致谢已成功收到并汇总的内容。 当这些ack没有在适当的时间内到达时,发送方的超时和重传。
你有一个循环调用select()
或poll()
来确定数据是否已经到达 - 如果是这样你然后调用recvfrom()来读取数据。
您可以设置接收数据的超时时间如下
ssize_t供 recv_timeout(int fd,void * buf,size_t len,int flags) {
ssize_t ret;
struct timeval tv;
fd_set rset;
// init set
FD_ZERO(&rset);
// add to set
FD_SET(fd, &rset);
// this is set to 60 seconds
tv.tv_sec =
config.idletimeout;
tv.tv_usec = 0;
// NEVER returns before the timeout value.
ret = select(fd, &rset, NULL, NULL, &tv);
if (ret == 0) {
log_message(LOG_INFO,
"Idle Timeout (after select)");
return 0;
} else if (ret < 0) {
log_message(LOG_ERR,
"recv_timeout: select() error \"%s\". Closing connection (fd:%d)",
strerror(errno), fd);
return;
}
ret = recvfrom(fd, buf, len, flags);
return ret;
}
它告诉我们,如果有数据就绪,通常,read()应该返回到你指定的最大字节数,这可能包括零字节(这实际上是一个有效的事情!),但是它应该在之前报告准备就绪后永远不会阻止。
在Linux下,select()可以将套接字文件描述符报告为“就绪” 阅读“,尽管如此,后来的阅读块。这可以 例如,当数据到达但经过检查时发生 错误的校验和并被丢弃。可能还有其他情况 哪个文件描述符被虚假报告为就绪。因此它可能 在不应阻塞的套接字上使用O_NONBLOCK会更安全。
答案 1 :(得分:0)
查找滑动窗口协议here。
这个想法是你将有效负载分成适合物理udp数据包的数据包,然后对它们进行编号。您可以将缓冲区可视化为一个时隙环,以某种方式顺序编号,例如,顺时针。
然后你开始从12点移动发送到1,2,3 ...在这个过程中,你可能(或可能不)从服务器接收包含你发送的数据包的插槽号的ACK数据包。
如果收到ACK,则可以从环中删除该数据包,并将下一个未发送的数据包放在环中。
如果收到您发送的数据包的NAK,则表示服务器收到数据包损坏的数据包,然后您从NAK中报告的环网槽重新发送数据包。
此协议类允许通过数据或数据包丢失的通道进行传输(如RS232,UDP等)。如果您的基础数据传输协议不提供校验和,则需要为您发送的每个环包添加校验和,以便服务器检查其完整性,并向您报告。
来自服务器的ACK和NAK数据包也可能丢失。要处理此问题,您需要将计时器与每个环形槽相关联,如果在计时器达到您设置的超时限制时没有收到插槽的ACK或NAK,则重新传输数据包并重置计时器。
最后,为了检测致命的连接丢失(即服务器发生故障),您可以为环中的所有数据包建立最大超时值。要评估这一点,您只需计算单个插槽的连续超时数。如果此值超过您设置的最大值,则可以考虑连接丢失。
显然,此协议类需要基于数据包编号在两侧进行数据集组装,因为数据包可能无法按顺序发送或接收。 “环”有助于此,因为仅在成功传输之后才移除数据包,并且仅在前一个数据包编号已被移除并附加到不断增长的数据集时才在接收端移除数据包。但是,这只是一种策略,还有其他策略。
希望这一点。