You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

UDP仅发送首个数据包后提示连接拒绝问题求助

问题描述

我制作了一个可复现的示例(保留了原大型源码的所有头文件),目前仅能成功发送第一个字符a,后续调用send()时会报错send() failed: Connection refused。

我发现仅在程序初始化时创建一次socket、结束时关闭的场景下会出现该问题;如果每次调用send()前创建socket、发送后立即关闭则无报错。

可复现代码

#include <pcap.h>
#include <cstdio>
#include <getopt.h>
#include <cstring>
#include <cstdlib>
#include <string>
#include <iostream>
#include <map>
#include <vector>

#define BUFSIZE 256

#define __FAVOR_BSD

#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <unistd.h>
#include<netdb.h>
#include<err.h>

#include <arpa/inet.h>

#include <netinet/ether.h>

#include <netinet/ip.h>
#include <netinet/ip6.h>

#include <netinet/tcp.h>
#include <netinet/udp.h>

#include <netinet/ip_icmp.h>
#include <netinet/icmp6.h>

using namespace std;

#define BUFFER 1024                // buffer length
int sock;                        // socket descriptor

void start_connection(){
    struct sockaddr_in server, from; // address structures of the server and the client
    struct hostent *servent;         // network host entry required by gethostbyname()

    memset(&server,0,sizeof(server)); // erase the server structure
    server.sin_family = AF_INET;

    // make DNS resolution of the first parameter using gethostbyname()
    if ((servent = gethostbyname("127.0.0.1")) == NULL) // check the first parameter
        errx(1,"gethostbyname() failed\n");

    // copy the first parameter to the server.sin_addr structure
    memcpy(&server.sin_addr,servent->h_addr,servent->h_length);

    server.sin_port = htons(2055);
    if ((sock = socket(AF_INET , SOCK_DGRAM , 0)) == -1)   //create a client socket
        err(1,"socket() failed\n");

    // create a connected UDP socket
    if (connect(sock, (struct sockaddr *)&server, sizeof(server))  == -1)
        err(1, "connect() failed");
}

void send_data(char * string){
    char buffer[BUFFER];
    int msg_size,i;

    //send data to the server
    memcpy(buffer,string,2);
    msg_size=2;
    i = send(sock,buffer,msg_size,0);     // send data to the server
    if (i == -1)                   // check if data was sent correctly
        err(1,"send() failed");
    else if (i != msg_size)
        err(1,"send(): buffer written partially");
    printf("%s",string);

}


int main(int argc, char *argv[])
{

    //Start UDP connection
    start_connection();

    send_data("a");
    send_data("b");
    send_data("c");

    //Close the UDP connection
    close(sock);
    return 0;
}

问题原因

你使用的是已连接的UDP套接字(通过connect()绑定了固定服务端地址),UDP本身是无连接协议,但connect()会让系统记住目标地址,同时监听对应ICMP错误。如果服务端未监听2055端口,第一次发送后系统会收到ICMP端口不可达报文,此时系统会把这个套接字的错误状态标记下来,后续再调用send()就会直接返回Connection refused,不会再尝试发送数据。

而每次新建套接字的方式不会触发这个问题,因为每次都是全新的套接字,系统还没收到对应的ICMP错误,所以第一次发送能正常执行。

解决方案

方案1:改用未连接UDP套接字(推荐)

去掉connect()调用,每次发送用sendto()指定服务端地址,这样即使收到ICMP错误,也不会影响后续发送(没有绑定固定连接,系统不会缓存错误状态)。修改后的代码如下:

#include <pcap.h>
#include <cstdio>
#include <getopt.h>
#include <cstring>
#include <cstdlib>
#include <string>
#include <iostream>
#include <map>
#include <vector>

#define BUFSIZE 256

#define __FAVOR_BSD

#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <unistd.h>
#include<netdb.h>
#include<err.h>

#include <arpa/inet.h>

#include <netinet/ether.h>

#include <netinet/ip.h>
#include <netinet/ip6.h>

#include <netinet/tcp.h>
#include <netinet/udp.h>

#include <netinet/ip_icmp.h>
#include <netinet/icmp6.h>

using namespace std;

#define BUFFER 1024                // buffer length
int sock;                        // socket descriptor
struct sockaddr_in server;       // 全局保存服务端地址

void start_connection(){
    struct hostent *servent;         // network host entry required by gethostbyname()

    memset(&server,0,sizeof(server)); // 清空服务端地址结构
    server.sin_family = AF_INET;

    // 解析服务端地址
    if ((servent = gethostbyname("127.0.0.1")) == NULL)
        errx(1,"gethostbyname() failed\n");

    memcpy(&server.sin_addr,servent->h_addr,servent->h_length);
    server.sin_port = htons(2055);

    // 创建UDP套接字
    if ((sock = socket(AF_INET , SOCK_DGRAM , 0)) == -1)
        err(1,"socket() failed\n");

    // 移除connect调用,改用sendto指定目标
}

void send_data(char * string){
    char buffer[BUFFER];
    int msg_size,i;

    memcpy(buffer,string,2);
    msg_size=2;
    // 使用sendto代替send,明确指定服务端地址
    i = sendto(sock,buffer,msg_size,0, (struct sockaddr *)&server, sizeof(server));
    if (i == -1)
        err(1,"sendto() failed");
    else if (i != msg_size)
        err(1,"sendto(): buffer written partially");
    printf("%s",string);

}


int main(int argc, char *argv[])
{
    start_connection();

    send_data("a");
    send_data("b");
    send_data("c");

    close(sock);
    return 0;
}

方案2:清除套接字错误状态(兼容已连接场景)

如果必须使用已连接的UDP套接字,可以在每次send()前清除套接字的错误队列,这样即使之前收到ICMP错误,后续发送也会重新尝试。在send_data函数开头添加以下代码:

int error;
socklen_t len = sizeof(error);
// 获取并清除套接字错误状态
getsockopt(sock, SOL_SOCKET, SO_ERROR, &error, &len);

不过这种方式不如方案1可靠,因为ICMP错误可能会重复触发,而且不同系统的处理逻辑可能有差异。


内容的提问来源于stack exchange,提问作者ampersander

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.12 08:10:46