UDP怎么会返回Connection refused错误

有时候,写UDP socket程序的时候,在调用sendto或者recvfrom的时候,会发现有Connection refused错误返回,错误码是ECONNREFUSED。对于懂得socket接口但是不很很懂网络的人,可能这根本就不是个问题,他会根据错误码知道远端没有这个服务端口,正如socket api的man手册中描述的那样:
ECONNREFUSED
              A remote host refused to allow the network connection (typically because it is not running the requested service).
有时候无知真的是一种幸福!但是如果你十分精通TCP/IP栈,那么就想不通了,UDP既然无连接,怎么知道远端的情况呢?UDP不正如协议标准描述的那样,发出去就不管了吗?对于接收,没有数据就一直等,如果设置了NOWAIT,则直接返回EAGAIN,表示稍后再试。不管怎么说,也不会有ECONNREFUSED这么详细的信息返回才对啊。
        既然UDP不会从对端返回任何错误信息,那么一定有别的什么返回了,总不能凭空猜测啊。这就涉及到了网络协议设计中的数据平面和控制平面了,对于控制平面的消息,可以是带内传输,也可以是带外传输。对于TCP而言,无疑是带内传输的,因为它本身就是有连接的协议,协议本身会处理任何的错误和异常,然而对于UDP而言,因为其设计目的就是保持简单性,故不再附带有任何带内的控制消息逻辑,互联网上为了弥补这一类协议的控制逻辑的缺失,ICMP协议才显得尤为重要!实际上,ICMP,根据名称就可以看出它是一种专门的控制协议,控制和指示IP层发生的事件。
        ECONNREFUSED正是ICMP返回的!然而并不是所有的UDP socket都可以享用ICMP带来的错误提示,毕竟带外控制消息和协议本身的关联太松散了。UDP socket必须显式的connect对端才可以。现在问题又来了,既然UDP根本就是一个无连接的协议,connect的意义何在呢?这其实是socket接口设计的范畴,和协议本身没有任何关系,当一个UDP socket去connect一个远端时,并没有发送任何的数据包,其效果仅仅是在本地建立了一个五元组映射,对应到一个对端,该映射的作用正是为了和UDP带外的ICMP控制通道捆绑在一起,使得UDP socket的接口含义更加丰满。
        我们知道,ICMP错误信息返回时,ICMP的包内容就是出错的那个原始数据包,根据这个原始数据包可以找出一个五元组,根据该五元组就可以对应到一个本地的connect过的UDP socket,进而把错误消息传输给该socket,应用程序在调用socket接口函数的时候,就可以得到该错误消息。如果一个UDP socket没有调用过connect,那么即使有ICMP数据包返回,由于socket保持了UDP的完整语义,协议栈也就不保存关于该socket和对端关联的任何信息,因此也就无法找到一个特定的五元组将错误码传给它。
        以下是一个测试程序:

#include <sys/types.h>
#include <sys/socket.h>
#include <string.h>
#include <netinet/in.h>
#include <stdio.h>
#include <arpa/inet.h>
#include <unistd.h>
void test( int sd, struct sockaddr *addr, socklen_t len)
{
        char buf[4];
        connect(sd, (struct sockaddr *)addr, len);
        sendto(sd, buf, 4, 0, (struct sockaddr *)addr, len);
         perror("write");
         sendto(sd, buf, 4, 0, (struct sockaddr *)addr, len);
         perror("write");
         recvfrom(sd, buf, 4, 0, (struct sockaddr *)addr, len);
         perror("read");
}
int main(int argc, char **argv)
{
        int sd;
        struct sockaddr_in addr;
        if(argc != 2) {
                exit(1);
        }
        bzero(&addr, sizeof(addr));
        addr.sin_family = AF_INET;
        addr.sin_port = htons(12345);
        inet_pton(AF_INET, argv[1], &addr.sin_addr);
        sd = socket(AF_INET, SOCK_DGRAM, 0);
        test(sd, (struct sockaddr *)&addr, sizeof(addr));
        return 0;
}

编译为UDPclient,执行./UDPclient 192.168.1.20,注意,这个地址一定要是个IP可达的地址,才好测试。按照上面的理论,结果应该是:第一个sendto成功,然后192.168.1.20返回了:
ICMP 192.168.1.20 udp port 12345 unreachable, length 40
接下来第二个sendto返回:
write: Connection refused
由于第二次没有发送任何数据包到达192.168.1.20,所以也不能企望它返回ICMP错误信息,因此接下来的recvfrom调用会阻塞。
        最后的一个问题时,你不能太指望这个Connection refused以及一切带外返回的错误信息,因为你不能保证一定能收到远端发送的ICMP包,如果中间的某个节点或者本机禁掉了ICMP,socket api调用就无法捕获这些错误。

再分享一下我老师大神的人工智能教程吧。零基础!通俗易懂!风趣幽默!还带黄段子!希望你也加入到我们人工智能的队伍中来!https://blog.csdn.net/jiangjunshow

原文地址:https://www.cnblogs.com/ksiwnhiwhs/p/10388816.html

时间: 2024-09-30 05:53:56

UDP怎么会返回Connection refused错误的相关文章

Linux 出现telnet: 127.0.0.1: Connection refused错误解决办法

Linux 出现telnet: connect to address 127.0.0.1: Connection refused错误解决办法 没有xinetd服务: 1./etc/init.d目录中放置了系统中各个daemon服务的脚本,xinetd是其中之一. 2.xinetd是一种特殊的daemon服务(super daemon),它本身管理了一系列的daemon服务,这些服务只有在用户调用时才由xinetd启动,它们启动速度稍慢于独立的daemon服务,这些服务在/etc/xinetd.c

【linux杂谈】在SSH连接中,openssh如何解决&#39;Connection refused&#39;错误?

openssh是SSH (Secure SHell) 协议的免费开源实现.SSH协议族可以用来进行远程控制, 或在计算机之间传送文件. 这就意味着远程登陆,文件推拉特别是搭建集群后公钥的部署,经常要利用到openssh.本人之前搭建hadoop集群模式的时候,公钥投送就和ssh相关,如今搭建ansible测试环境的时候,再一次用到了ssh相关技术. 在向被管控机器投送公钥的时候,使用到了ssh-copy-id命令,但提示我connection refused.连输入密码那一步都没到,可见是直接进

使用putty远程登录Ubuntu时,报Network error:Connection refused错误解决方案

使用putty 远程登陆Ubuntu 时提示 Network error:Connection refused 就是因为Ubuuntu没有安装ssh服务. 执行安装命令:sudo apt install openssh-server 安装完成后,远程登陆正常 原文地址:https://www.cnblogs.com/wenyi/p/10807376.html

网络连接返回getsockopt: connection refused错误原因 及 listen

客户端访问的ip写错或者服务端的listen ip写错 listen(":8080") ==  listen("0.0.0.0:8080") 可以用127.0.0.1:8080访问,也可以用网卡ip:8080访问 listen("127.0.0.1:8080")只能通过127.0.0.1:8080访问,目前所知一般用于代理转发到127.0.0.1:8080,如nginx的proxy_pass

Ubuntu 14.04 下安装redis后运行redis-cli 报出redis Connection refused错误【已解决】

在运行redis-cli运行后爆出错误,看了网上的都没有用例如:改ip,注释bind 127.0.0.1,或者是先运行./redis-server redis.conf,都没有用 只需要: 找到redis.conf 并修改 daemonize no 为 daemonize yes 然后再在终端输入: redis-server /usr/local/redis/redis.conf 就完美解决了 原文地址:https://www.cnblogs.com/smartisn/p/11684743.ht

ssh: connect to host localhost port 22: Connection refused (2)

命令:ssh localhost 错误信息:ssh: connect to host localhost port 22: Connection refused 错误原因: 1.sshd 未安装 2.sshd 未启动 3.防火墙 解决方法: 1.确定安装sshd:  sudo apt-get install openssh-server 2.启动sshd: net start sshd 3.检查防火墙设置 检验方法: 输入命令:ssh localhost 若成功,则表示安装成功,且连接通过: 转

java.net.ConnectException: Connection refused

所出问题:java.net.ConnectException: Connection refused 报错项目:Zookeeper+AMQ 错误原因:Zookeeper集群在启动时会链接各个节点并根据选举算法选出leader,所以顺序启动时会报java.net.ConnectException: Connection refused 错误 解决办法:全部节点启动完就没事了,忽略即可.

Could not connect to Redis at 127.0.0.1:6379: Connection refused

启动redis:  redis-server ../redis.conf redis启动成功后 执行命令行redis-cli报:Could not connect to Redis at 127.0.0.1:6379: Connection refused错误 那是因为redis安装路径下的redis.conf文件配置未修改: redis.conf文件中: 将daemonize no 修改为 daemonize yes 再输入 redis-server /usr/local/src/redis-

NFS错误Starting NFS quotas: Cannot register service: RPC: Unable to receive; errno=Connection refused

NFS报错一例 [[email protected] ~]# /etc/init.d/portreserve start Starting portreserve:                                      [  OK  ] [[email protected] ~]# /etc/init.d/nfs start Starting NFS services:                                     [  OK  ] Starting