socket编程 errno ECONNREFUSED EINPROGRESS

#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <cstdint>
#include <stdio.h>
#include <string>
#include <assert.h>
#include <arpa/inet.h>
#include <string.h>

int SetNonBlocking(int nSockFd)
{
    int nOldOption = fcntl(nSockFd, F_GETFL);
    if(nOldOption == -1)
    {
        assert(false);
        return -1;
    }
    int nNewOption = nOldOption | O_NONBLOCK;
    if(fcntl(nSockFd, F_SETFL, nNewOption) == -1)
    {
        assert(false);
        return -1;
    }
    return 0;
}

int main()
{
    struct sockaddr_in stServerAddress;
    bzero(&stServerAddress, sizeof(stServerAddress));
    stServerAddress.sin_family = AF_INET;
    inet_pton(AF_INET, "127.0.0.1", &stServerAddress.sin_addr);
    stServerAddress.sin_port = htons(12345);
    int fd = socket(PF_INET, SOCK_STREAM, 0);
    assert(fd >= 0);

    SetNonBlocking(fd);

    int ret = connect(fd, (struct sockaddr*)&stServerAddress, sizeof(stServerAddress));
    printf("ret = %d, errno is %d, error:%s\n", ret, errno, strerror(errno));
    ret = connect(fd, (struct sockaddr*)&stServerAddress, sizeof(stServerAddress));
    printf("ret = %d, errno is %d, error:%s\n", ret, errno, strerror(errno));
    ret = connect(fd, (struct sockaddr*)&stServerAddress, sizeof(stServerAddress));
    printf("ret = %d, errno is %d, error:%s\n", ret, errno, strerror(errno));
}

我的执行结果是:

ret = -1, errno is 115, error:Operation now in progress
ret = -1, errno is 111, error:Connection refused
ret = -1, errno is 115, error:Operation now in progress

为什么第二个connect才是ECONNREFUSED,第一个connect其实就连不上。
现在困惑的是哪些情况下返回ECONNREFUSED,哪些情况下返回EINPROGRESS,这两个错误码有什么区别呢?

阅读 3.3k
1 个回答

问题分析

对于TCP,connect函数实际是进行三次握手的过程,该过程需要一些时间。

  • 对于非阻塞的socket,调用connect将直接返回,由于此时三次握手正在进行中,没有握手成功,也没有握手失败,这时将返回EINPROGRESS(注意理解错误码名字)。在此过程中客户端发送了SYN报文。
  • 第二个connect调用时,先前客户端发送的SYN报文,对端收到了,但此时服务端没有启动,内核检测到没有监听该端口的应用程序,故回应了RST报文。那么connect函数就返回了ECONNREFUSED。
  • 相关套接字接口,如send\recv等函数的错误码都有一定的滞后性。返回错误时,往往错误是前面发生的。

另外,对于非阻塞套接字fd,不要多次调用connect函数,应创建新的套接字fd再次连接。

EINPROGRESS和ECONNREFUSED

关于错误码的详细信息最好查阅man手册。对于当前问题man connect信息节选如下:

ECONNREFUSED
    A connect() on a stream socket found no one listening on the remote address.
EINPROGRESS
    The socket is nonblocking and the connection cannot be completed immediately.  It is possible to select(2) or poll(2) for completion by selecting the socket for  writing.   After select(2)  indicates  writability,  use  getsockopt(2) to read the SO_ERROR option at level SOL_SOCKET to determine whether connect() completed successfully (SO_ERROR is zero) or unsuccessfully (SO_ERROR is one of the usual error codes listed here, explaining the reason for the failure).

可见:

  • ECONNREFUSED:代表对端没有套接字在监听该端口;
  • EINPROGRESS:代表连接建立需要时间,不能立即完成,而非阻塞会立即返回。此时该fd可能建立失败也可能建立成功。
撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题