在阻塞和非阻塞模式下,connect函数的行为
2023-05-19
在阻塞和非阻塞模式下,connect函数的行为
当socket
在使用堵塞方式时,connect
如果网络环境不好,函数会被堵塞,直到有明确的结果才会回来,可能要等一会儿,影响感觉,
为解决这一问题,我们使用它。异步connect
技术
- 建立
socket
,将socket
将其设置为非阻塞模式 - 调用
connect
此时不管函数connect
函数是否连接成功,将立即返回,如果返回-1,则不一定意味着连接错误,如果此时错误码为EINPROGRESS
表示正在尝试连接 - 调用
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
检验是否可以写,是否可以得到正确的结果,即不能写;连接成功后,在检验中,就会变成可写的。 - 最后一个是Linux
socket
使用前未建立连接,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