当前位置: 首页 > 知识库问答 >
问题:

C udp recvfrom客户端

公冶高义
2023-03-14

我正在编写udp服务器/客户端应用程序,其中服务器发送数据,客户端接收数据。当数据包丢失时,客户端应该向服务器发送nack。我将套接字设置为O_NONBLOCK,这样我就可以注意到客户端是否没有收到数据包

if (( bytes = recvfrom (....)) != -1 ) {
  do something
}else{
  send nack
}

我的问题是,若服务器并没有开始发送数据包,那个么客户端的行为就是数据包丢失,并且开始向服务器发送nack。(没有可用数据时recvfrom失败)我需要一些建议,如果服务器没有开始发送数据包,如果服务器发送数据包,但数据包确实丢失了,我如何区分这些情况

共有2个答案

陆啸
2023-03-14

在这里查找滑动窗口协议。

其思想是将有效负载划分为适合物理udp数据包的数据包,然后对它们进行编号。您可以将缓冲区视为一个插槽环,以某种方式(例如顺时针)顺序编号。

然后从12点开始发送,移动到1,2,3。。。在此过程中,您可以(也可以不)从服务器接收包含您发送的数据包的插槽号的ACK数据包。

如果您收到ACK,那么您可以从环中删除该数据包,并将下一个未发送的数据包放在环中,而该数据包尚未在环中。

如果您收到发送的数据包的NAK,则表示该数据包是由服务器接收到的,并且存在数据损坏,然后您从NAK中报告的环槽重新发送该数据包。

该协议类允许在有数据或数据包丢失的信道上传输(如RS232、UDP等)。如果您的基础数据传输协议不提供校验和,那么您需要为您发送的每个环形数据包添加校验和,以便服务器可以检查其完整性,并向您报告。

来自服务器的ACK和NAK数据包也可能丢失。要处理这个问题,您需要将一个计时器与每个环槽相关联,如果当计时器达到您设置的超时限制时,您没有收到某个槽的ACK或NAK,那么您需要重新传输数据包并重置计时器。

最后,为了检测致命的连接丢失(即服务器崩溃),您可以为环中的所有数据包建立一个最大超时值。要评估这一点,您只需计算单个插槽的连续超时次数。如果此值超过您设置的最大值,则可以考虑丢失的连接。

显然,该协议类要求基于数据包号在两边组装数据集,因为数据包可能不能按顺序发送或接收。“环”有助于做到这一点,因为只有在成功传输后,数据包才会被删除,而在接收端,只有在先前的数据包号已经被删除并附加到不断增长的数据集中时,数据包才会被删除。然而,这只是一种策略,还有其他策略。

希望这一切都会好起来。

许俊晤
2023-03-14

您正在使用UDP。对于这个协议,如果有必要的话,完全可以丢弃数据包。因此,就“发送的内容将到达”而言,它是不可靠的。在客户机中,您需要做的是检查是否所有需要的数据包都已到达,如果没有,请礼貌地与服务器交谈,重新发送您没有收到的数据包。实现这些东西并不是那么容易,

如果您必须使用UDP传输较大的数据块,那么设计一个小型应用程序级协议来处理可能的数据包丢失和重新排序(这是TCP为您所做的工作的一部分)。我会这样说:

数据报的大小小于MTU(加上IP和UDP报头)(例如1024字节),以避免IP碎片。每个数据报的固定长度标头,包括数据长度和序列号,因此您可以将数据拼接回一起,并检测遗漏、重复和重新排序的部分。接收方对成功接收和汇总的内容的确认。当这些ack没有在适当的时间内到达时,发送端的超时和重传。

您有一个循环调用Select()轮询()来确定数据是否已经到达-如果是,那么您将调用recvfrom()来读取数据。

您可以按如下方式设置接收数据的时间

ssize_t recv_超时(int fd、void*buf、size_t len、int标志){

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 \"&#37;s\". Closing connection (fd:%d)",
            strerror(errno), fd);
    return;
} 

ret = recvfrom(fd, buf, len, flags);
return ret;

}

它告诉如果有数据准备就绪,通常,read()应该返回到您指定的最大字节数,其中可能包括零字节(这实际上是一个有效的事情发生!),但它不应该在之前报告准备就绪后阻塞。

在Linux下,select()可能会将套接字文件描述符报告为“ready for reading”(准备读取),但随后的读取会阻塞。例如,当数据已到达但检查时校验和错误并被丢弃时,可能会发生这种情况。在其他情况下,可能会错误地将文件描述符报告为就绪。因此,在不应阻塞的套接字上使用O_NONBLOCK可能更安全。

 类似资料:
  • 我想在一些计算机之间建立点对点连接,这样用户就可以在没有外部服务器的情况下聊天和交换文件。我最初的想法如下: 我在服务器上制作了一个中央服务器插座,所有应用程序都可以连接到该插座。此ServerSocket跟踪已连接的套接字(客户端),并将新连接的客户端的IP和端口提供给所有其他客户端。每个客户端都会创建一个新的ServerSocket,所有客户端都可以连接到它。 换句话说:每个客户端都有一个Se

  • URI 方法 URI() string 返回当前客户端使用的服务器地址。 SetURI 方法 SetURI(uri string) 设置当前客户端使用的服务器地址。如果你想要设置多个服务器地址,请使用 SetURIList 方法代替该方法。 URIList 方法 URIList() []string 返回当前客户端可使用的服务器地址列表。 SetURIList 方法 SetURIList(uriL

  • 客户端事件通过 SetEvent 方法进行设置。 客户端事件有两个,它们分别定义为: type onErrorEvent interface { OnError(name string, err error) }   type onFailswitchEvent interface { OnFailswitch(Client) } 因为 go 语言不需要显式实现接口的特点,所以这两

  • 创建客户端有两种方式,一种是直接使用特化的构造器函数,另一种是使用工厂构造器函数。 第一种方式返回的是具体的客户端结构体指针对象,第二种方式返回的是客户端接口对象。 使用特化的构造器函数创建客户端 特化的构造器函数有下面几个: func NewHTTPClient(uri ...string) (client *HTTPClient) func NewTCPClient(uri ...string

  • 客户端下载地址: windows 32位安装包 windows 64位安裝包 mac 安装包 Android App iOS App

  • 问题 你想使用网络上提供的服务。 解决方案 创建一个基本的 TCP 客户机。 在 Node.js 中 net = require 'net' domain = 'localhost' port = 9001 connection = net.createConnection port, domain connection.on 'connect', () -> console.log

  • 客户端调用服务端 服务端的proto文件copy到客户端 获取grpc客户端 //sample 为服务名称 var client = await provider.FindGrpcClient<RpcTest.RpcTestClient>("sample"); 调用服务方法 var result = await client.SayHelloAsync(new HelloRequest() {

  • Disque 在源码中附带了命令行客户端程序 disque , 通过这个程序可以以交互的形式向 Disque 发送命令: $ ./disque 127.0.0.1:7711> PING PONG 另外你也可以使用以下编程语言的客户端来操作 Disque : Ruby 客户端 https://rubygems.org/gems/disque Java 客户端 https://github.com/x