Linux connect() 后的 UDP 发送行为

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/4397195/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-05 00:14:53  来源:igfitidea点击:

UDP send behaviour after connect()

clinuxsocketsudp

提问by Raju V

#include <stdio.h>  
#include <errno.h>  
#include <stdlib.h>  
#include <string.h>  
#include <sys/types.h>  
#include <sys/socket.h>  
#include <netinet/in.h>  
#include <arpa/inet.h>  

int main()  
{    
    struct sockaddr_in addr;  
    int fd, cnt,ret;  
    char ch = 'y',msg[] ="How are you";  

    if ((fd=socket(AF_INET,SOCK_DGRAM,0)) < 0) {  
        printf("Error: socket");  
        exit(1);  
    }
    printf("\nDone socket\n");  

    /* set up destination address */  
    memset(&addr,0,sizeof(addr));  
    addr.sin_family=AF_INET;  
    addr.sin_addr.s_addr=inet_addr("128.88.143.113");  
    addr.sin_port=htons(9090);  

    ret=connect(fd,(struct sockaddr *)&addr,sizeof(addr));  
    perror("Connect:");  

    while(ch == 'y'){  
        cnt =  send(fd,msg,sizeof(msg),0);  
        if(cnt < 0)  
        perror("send:");  
        printf("\nNumber of bytes sent = %d , \n",cnt);  
        printf("Continue (y/n)\n");  
        scanf(" %c",&ch);  

     }

     return 0;  
}  

The above code is compiled to run on a Linux machine.

上面的代码被编译为在 Linux 机器上运行。

Assume that the above code sends data to a machine at IP address 128.88.143.113. No UDP socket is bound to port 9090at 128.88.143.113.

假设上述代码将数据发送到 IP 地址为 的机器128.88.143.113。没有UDP套接字绑定到端口9090128.88.143.113

In the whileloop, the first call to send()succeeds(the packet actually goes out on the wire; checked it using trace) and the second send()fails with Connection refused. The third send()succeeds and the forth fails and so on.

while循环中,第一次调用send()成功(数据包实际上在线路上发出;使用 进行检查trace),第二次调用send()失败并显示Connection refusedthird send()成功和第四失败等等。

I suspect that after first send()the stack receives an ICMP error message(seen in tcpdumpon the Linux machine) which is saved in the socket structure. The second send()fails upon seeing this error and no packet is actually sent out. The second send()also clears the error in the socket structure. Therefore the third send()succeeds and the forth fails and so on.

我怀疑首先send()堆栈收到一条 ICMP 错误消息(在tcpdumpLinux 机器上看到),该消息保存在套接字结构中。第二个send()在看到此错误时失败,并且实际上没有发送任何数据包。第二个send()也清除了套接字结构中的错误。因此第三个send()成功,第四个失败,依此类推。

Questions:

问题:

  1. Is this assumption correct?
  2. What should be the correct behavior? Is there any RFC standard defining such behavior?
  3. Since UDP does not maintain any connection state, shouldn't every send()succeed?
  1. 这个假设正确吗?
  2. 正确的行为应该是什么?是否有任何 RFC 标准定义了这种行为?
  3. 既然 UDP 不维护任何连接状态,难道不是每个都send()成功吗?

回答by user207421

To start at the other end, if you connect a UDP socket you can collect errors on the next send. If you don't want that, don't connect!

从另一端开始,如果您连接 UDP 套接字,您可以在下一次发送时收集错误。如果您不想要那样,请不要连接!

回答by Len Holgate

It would be interesting to compare the equivalent code using sendto()rather than connect()and send().

使用sendto()而不是connect()和来比较等效代码会很有趣send()

Does the code shown fail in the same way if you leave a period of time between each send, i.e. is the ICMP error state being kept in the socket for a period of time, or would it still fail the second send()if you left it, say, an hour?

如果您在每次发送之间留出一段时间,显示的代码是否会以相同的方式失败,即 ICMP 错误状态是否在套接字中保留了一段时间,或者send()如果您离开它,它仍然会在第二次失败,例如, 一小时?

I expect that your assumption is correct, the network stack is trying to be clever. There's no other point when it could return 'connection refused' as nothing is sent when the connect()call is issued it simply stores the address given so that the socket is 'logically' connected and calls to send()can then work.

我希望您的假设是正确的,网络堆栈正试图变得聪明。没有其他点可以返回“连接被拒绝”,因为在发出connect()调用时没有发送任何内容,它只是存储给定的地址,以便套接字“逻辑”连接,send()然后调用可以工作。

回答by Hasturkun

According to the linux man page for udp:

根据udplinux 手册页

All fatal errors will be passed to the user as an error return even when the socket is not connected. This includes asynchronous errors received from the network. You may get an error for an earlier packet that was sent on the same socket. This behaviour differs from many other BSD socket implementations which don't pass any errors unless the socket is connected. Linux's behaviour is mandated by RFC 1122.

即使套接字未连接,所有致命错误也将作为错误返回传递给用户。这包括从网络接收到的异步错误。对于在同一套接字上发送的较早数据包,您可能会收到错误消息。此行为不同于许多其他 BSD 套接字实现,除非套接字已连接,否则它们不会传递任何错误。Linux 的行为由RFC 1122强制要求。

Specifically the RFC(4.1.3.3) states:

特别是RFC(4.1.3.3) 指出:

UDP MUST pass to the application layer all ICMP error messages that it receives from the IP layer. Conceptually at least, this may be accomplished with an upcall to the ERROR_REPORT routine

UDP 必须将它从 IP 层接收到的所有 ICMP 错误消息传递给应用层。至少从概念上讲,这可以通过调用 ERROR_REPORT 例程来完成

回答by caf

Your hypothesis is correct. The Linux udp(7) man page describes the situation thus:

你的假设是正确的。Linux udp(7) 手册页描述了这种情况:

All fatal errors will be passed to the user as an error return even when the socket is not connected. This includes asynchronous errors received from the network. You may get an error for an earlier packet that was sent on the same socket.
This behavior differs from many other BSD socket implementations which don't pass any errors unless the socket is connected. Linux's behavior is mandated by RFC 1122.

When the IP_RECVERRoption is enabled all errors are stored in the socket error queue and can be received by recvmsg(2)with the MSG_ERRQUEUEflag set.

即使套接字未连接,所有致命错误也将作为错误返回传递给用户。这包括从网络接收到的异步错误。对于在同一套接字上发送的较早数据包,您可能会收到错误消息。
此行为不同于许多其他 BSD 套接字实现,除非套接字已连接,否则它们不会传递任何错误。Linux 的行为由RFC 1122强制要求。

IP_RECVERR选项启用所有的错误都存储在套接字错误队列,并且可以通过接收recvmsg(2)MSG_ERRQUEUE标志设置。

回答by Chris Maes

I had the same problem; and the is due to the fact that the udp message queue is filled if nobody is listening and emptying the queue.

我有同样的问题; 这是因为如果没有人在侦听和清空队列,则 udp 消息队列已满。