在阻塞和非阻塞模式下,connect函数的行为

2023-05-19


在阻塞和非阻塞模式下,connect函数的行为


socket在使用堵塞方式时,connect如果网络环境不好,函数会被堵塞,直到有明确的结果才会回来,可能要等一会儿,影响感觉,


为解决这一问题,我们使用它。异步connect技术


  1. 建立socket,将socket将其设置为非阻塞模式
  2. 调用connect此时不管函数connect函数是否连接成功,将立即返回,如果返回-1,则不一定意味着连接错误,如果此时错误码为EINPROGRESS表示正在尝试连接
  3. 调用select在规定的时间内判断函数。socket是否可以写,可以写表示连接成功,相反,连接失败
    以上流程代码
#include  
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#define SERVER_ADDRESS  "127.0.0.1"
#define SERVER_PORT     3000
#define SEND_DATA       "helloworld"

int main(int argc, char* argv[])
{
    创建一个socket
    int clientfd = socket(AF_INET, SOCK_STREAM, 0);
    if (clientfd == -1)
    {
        std::cout << "create client socket error." << std::endl;
        return -1;
    }
///将clientfd设置为非阻塞方式
int oldSocketFlag = fcntl(clientfd, F_GETFL, 0);
int newSocketFlag = oldSocketFlag | O_NONBLOCK;
if (fcntl(clientfd, F_SETFL,  newSocketFlag) == -1)
{
close(clientfd);
std::cout << "set socket to nonblock error." << std::endl;
return -1;
}
//2.连接服务器
struct sockaddr_in serveraddr;
serveraddr.sin_family = AF_INET;
serveraddr.sin_addr.s_addr = inet_addr(SERVER_ADDRESS);
serveraddr.sin_port = htons(SERVER_PORT);
for (;;)
{
int ret = connect(clientfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr));
if (ret == 0)
{
std::cout << "connect to server successfully." << std::endl;
close(clientfd);
return 0;
} 
else if (ret == -1) 
{
if (errno == EINTR)
{
//connect 信号中断了动作,再次尝试connect
std::cout << "connecting interruptted by signal, try again." << std::endl;
continue;
} 
else if (errno == EINPROGRESS)
{
//正在尝试连接
break;
} 
else
{
//的确出了问题,
close(clientfd);
return -1;
}
}
}
fd_set writeset;
FD_ZERO(&writeset);
FD_SET(clientfd, &writeset);
struct timeval tv;
tv.tv_sec = 3;  
tv.tv_usec = 0;
使用select函数来判断socket是否可以写作。
if (select(clientfd   1, NULL, &writeset, NULL, &tv) == 1)
{
std::cout << "[select] connect to server successfully." << std::endl;
} 
else 
{
std::cout << "[select] connect to server error." << std::endl;
}

close(clientfd);
return 0;
}

首先先用nc启动服务器程序并执行指令


nc -v -l -n 0.0.0.0 3000

然后运行程序,我使用的clion


关闭服务器,重新启动客户端,看看结果,或者


为什么不能连接也会导出相同的结果?理由如下:


  • 一个Windows,socket在建立连接之前,我们使用它。select检验是否可以写,是否可以得到正确的结果,即不能写;连接成功后,在检验中,就会变成可写的。
  • 最后一个是Linuxsocket使用前未建立连接,select函数检验是否可以写,我们也可以得到可以写的结果。因此,在Linux上,我们不仅要使用它。select检验socket是否可以写还要用getsocketopt检验socket这个时候是否有错误
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#define SERVER_ADDRESS "127.0.0.1"
#define SERVER_PORT     3000
#define SEND_DATA       "helloworld"

int main(int argc, char* argv[])
{
    创建一个socket
    int clientfd = socket(AF_INET, SOCK_STREAM, 0);
    if (clientfd == -1)
    {
        std::cout << "create client socket error." << std::endl;
        return -1;
    }

    ///将clientfd设置为非阻塞方式
    int oldSocketFlag = fcntl(clientfd, F_GETFL, 0);
    int newSocketFlag = oldSocketFlag | O_NONBLOCK;
    if (fcntl(clientfd, F_SETFL,  newSocketFlag) == -1)
    {
        close(clientfd);
        std::cout << "set socket to nonblock error." << std::endl;
        return -1;
    }

    //2.连接服务器
    struct sockaddr_in serveraddr;
    serveraddr.sin_family = AF_INET;
    serveraddr.sin_addr.s_addr = inet_addr(SERVER_ADDRESS);
    serveraddr.sin_port = htons(SERVER_PORT);
    for (;;)
    {
        int ret = connect(clientfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr));
        if (ret == 0)
        {
            std::cout << "connect to server successfully." << std::endl;
            close(clientfd);
            return 0;
        }
        else if (ret == -1)
        {
            if (errno == EINTR)
            {
                //connect 信号中断了动作,再次尝试connect
                std::cout << "connecting interruptted by signal, try again." << std::endl;
                continue;
            }
            else if (errno == EINPROGRESS)
            {
                //正在尝试连接
                break;
            }
            else
            {
                //的确出了问题,
                close(clientfd);
                return -1;
            }
        }
    }

    fd_set writeset;
    FD_ZERO(&writeset);
    FD_SET(clientfd, &writeset);
    struct timeval tv;
    tv.tv_sec = 3;
    tv.tv_usec = 0;
    使用select函数来判断socket是否可以写作。
    if (select(clientfd   1, NULL, &writeset, NULL, &tv) != 1)
    {
        std::cout << "[select] connect to server error." << std::endl;
        close(clientfd);
        return -1;
    }

    int err;
    socklen_t len = static_cast(sizeof err);
    //4.调用getsockopt检查此时socket是否有错误?
    if (::getsockopt(clientfd, SOL_SOCKET, SO_ERROR, &err, &len) < 0)
    {
        close(clientfd);
        return -1;
    }

    if (err == 0)
        std::cout << "connect to server successfully." << std::endl;
    else
        std::cout << "connect to server error." << std::endl;

    close(clientfd);

    return 0;
}

基本流程的TCP网络编程


Linux和C 11多线程编程(学习笔记)


Linux select函数的用法和原理


socket的阻塞方式和非阻塞方式(send和recv函数在阻塞和非阻塞方式中的表现)


在阻塞和非阻塞模式下,connect函数的行为


在接收缓冲区获得相应的socket可读信息量。




本文仅代表作者观点,版权归原创者所有,如需转载请在文中注明来源及作者名字。

免责声明:本文系转载编辑文章,仅作分享之用。如分享内容、图片侵犯到您的版权或非授权发布,请及时与我们联系进行审核处理或删除,您可以发送材料至邮箱:service@tojoy.com