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

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

socket使用阻塞模式时,connect函数会阻塞到有明确结果才会返回,如果网络环境较差,可能要等一会,影响体验,

为了解决这个问题,我们使用异步connect技术

  1. 创建socket,将socket设置为非阻塞模式

  2. 调用connect函数,此时无论connect函数是否连接成功,都会立即返回,如果返回-1,不一定表示连接出错,如果此时错误码为EINPROGRESS表示正在尝试连接

  3. 调用select函数,在指定时间内判断该socket是否可写,可写说明连接成功,反之,连接失败

    上述流程代码

    #include <sys/types.h> 
    #include <sys/socket.h>
    #include <arpa/inet.h>
    #include <unistd.h>
    #include <iostream>
    #include <string.h>
    #include <stdio.h>
    #include <fcntl.h>
    #include <errno.h>#define SERVER_ADDRESS  "127.0.0.1"
    #define SERVER_PORT     3000
    #define SEND_DATA       "helloworld"int main(int argc, char* argv[])
    {//1.创建一个socketint 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 动作被信号中断,重试connectstd::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;//3.调用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

    image-20210706233125990

    把服务端关掉,在重新启动客户端,一看结果,还是

    为什么连接不上也会输出同样的结果?原因如下:

    • 在Windows上,一个socket没有建立连接之前,我们用select检测是否可写,是可以得到正确结果的,即不可写;连接成功后在检测,就会变为可写

    • 在Linux上一个socket没有建立连接之前,用select函数检测是否可写,我们也会得到可写的结果,**所以,在Linux上,我们不仅要用select检测socket是否可写还要用getsocketopt检测socket此时是否出错

      #include <sys/types.h>
      #include <sys/socket.h>
      #include <arpa/inet.h>
      #include <unistd.h>
      #include <iostream>
      #include <string.h>
      #include <stdio.h>
      #include <fcntl.h>
      #include <errno.h>#define SERVER_ADDRESS "127.0.0.1"
      #define SERVER_PORT     3000
      #define SEND_DATA       "helloworld"int main(int argc, char* argv[])
      {//1.创建一个socketint 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 动作被信号中断,重试connectstd::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;//3.调用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<socklen_t>(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;elsestd::cout << "connect to server error." << std::endl;close(clientfd);return 0;
      }
      

TCP网络编程的基本流程

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

Linux select函数用法和原理

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

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

获取socket对应的接收缓冲区中的可读数据量

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/408160.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

让模块支持“导入”“导出”功能

让模块支持“导入”“导出”功能DNN模块可以支持导入导出功能&#xff0c;通过将模块内容导入到XML文件可以便于模块内容备份和转移&#xff0c;也可将模块内容事先以XML格式保存通过导入功能实现模块内容的批量录入。如图:[img]http://esshs.cnblogs.com/images/cnblogs_com/e…

获取socket对应的接收缓冲区中的可读数据量

获取socket对应的接收缓冲区中的可读数据量 本文介绍如何获取当前socket对应的接收缓冲区的可读数据量 在Linux上可以使用ioctl函数 #include <sys/ioctl.h>int ioctl (int __fd, unsigned long int __request, ...)来看一个例子: #include <sys/types.h> #in…

ASP.NET 2.0 Club Web Site Starter Kit 补丁

ASP.NET2.0 Club Web Site Starter Kit 具有一个很大的缺陷&#xff1a;不支持中文。这里给出两种解决方案供大家参考方法一&#xff1a;1&#xff09;由于大家大部分都是用SQL SERVER2005 EXPRESS开发的&#xff0c;所以在建立好Club需要的数据表后&#xff0c;将每一个表的v…

Linux epoll的用法

Linux epoll的用法 epollfd_create函数 #include <sys/epoll.h>int epoll_create (int __size)参数含义__size此参数从Linux 2.6.8后就不再使用了,但必须设置成大于零的值 返回值含义>0可用的epollfd-1调用失败 epollfd_ctl函数 有了epollfd,我们需要将要检测事件…

EVC4.0 PPC2003 Emulator 转中文版

1. 安装Windows Mobile 2003 Second Edition Emulator Images for Pocket PC - CHS.msi 2. 将C:\Program Files\Windows CE Tools\wce420\POCKET PC 2003\Emulation\emul.xml文件中的start "" "C:\Program Files\Common Files\Microsoft Shared\Windo…

第一个 Win32 窗口程序

第一个 Win32 窗口程序 程序骨架 int WinMain(){ // 设计窗口外观及交互响应&#xff0c;注册&#xff0c;申请专利RegisterClass(...) ;// 生产窗口 CreateWindow(...); // 展示窗口 ShowWindow(...); // 粉刷窗口 UpdateWindow(...);// 进入消息循环 while (GetMessage(...)…

在C#中利用DirectX实现声音播放

在c#中利用directx实现声音播放我感觉声音的播放比较简单。我们从播放声音开始。为什么我这么觉得&#xff1f;我也不知道。 这里是展示最简单的directx播放声音的例子,我尽量省略了无关的代码。最后的代码只有19行,够简单了吧&#xff1f; 准备工作&#xff1a; 1.安装了dire…

windows网络编程

windows网络编程 TCP编程 服务端 这里我们有几点需要注意: 使用WSAStartup初始化网络库,即将与socket函数相关dll文件加载到进程地址空间中退出时,使用WSACleanup()卸载相关dll文件与Linux使用close函数关闭socket不同,windows需要使用closesocket函数关闭socket WSAStart…

TCP服务器epoll的多种实现

TCP服务器epoll的多种实现 对于网络IO会涉及到两个系统对象 用户空间中进程或者线程操作系统内核 比如发生read操作时就会经历两个阶段 等待数据就绪将数据从内核缓冲区拷贝到用户缓冲区 由于各个阶段多有不同的情况,一组合么就产生了多种网络 IO 模型 阻塞IO 在Linux中…

软件的社交能力

两个人之间互不相识&#xff0c;则无法交往&#xff0c;两个系统之间相互不能识别&#xff0c;那么也就不可能相互通讯。但是人有一种社交能力&#xff0c;这种能力可以保证两个不相识的人&#xff0c;相互认识&#xff0c;开始交往。两个系统之间没有这种能力&#xff0c;所以…

设计模式实践

以前写程序不是太注意&#xff0c;将程序中的执行接口、容器接口和加载工具混合在一起设计。例如一个服务工厂&#xff0c;对于调用方来说&#xff0c;他是不关心你的容器机制&#xff0c;他关心的是执行接口&#xff0c;即他的结果。而对于服务的加载程序&#xff0c;他关心如…

侯捷面向对象高级编程(二)

侯捷面向对象高级编程(二) 转换函数 转换函数没有返回值,返回值就是double即函数名,不需要自己写因为转换函数一般不会改变其中内容,所以要加const限定 两条路都可以走,就回产生歧义,报错 explict禁止自动转换,于是4无法转转换为Fraction pointer-like cliasses ->作用之后…