简介:

在网络通讯中,如果socket处于阻塞模式运行时,就需要考虑处理socket操作超时的问题。

所谓阻塞模式,是指其完成指定的操作之前阻塞当前的进程或线程,直到操作有结果返回.

在我们直接调用socket操作函数时,如果不进行特意声明的话,它们都是工作在阻塞模式的,

如 connect, send, recv等.

更多关于阻塞/非阻塞,同步/异步的讲解可以参见我总结的相关专题文章:

http://blog.chinaunix.net/uid-26000296-id-3754118.html

http://blog.chinaunix.net/uid-26000296-id-3755264.html

http://blog.chinaunix.net/uid-26000296-id-3755268.html

简单分类的话,可以将超时处理分成两类:

连接(connect)超时;

发送(send), 接收(recv)超时;

下面对这两类超时一一做示例讲解

一、连接(connect)超时

基本实现流程如下:

1.建立socket;

2.将该socket设置为非阻塞(Non-blocking)模式;

3.调用connect();

正常情况下,因为TCP三次握手需要一些时间;

而非阻塞调用只要不能立即完成就会返回错误,

所以这里会返回EINPROGRESS,表示在建立连接但还没有完成。

4. 在读套接口描述符集(fd_set readset)和写套接口描述符集(fd_set writeset)中

将当前套接口置位(用FD_ZERO()、FD_SET()宏);

并设置好超时时间(struct timeval *timeout);

如果你设置的超时时间大于75秒就没有必要这样做了,因为内核中对connect有超时限制就是75秒。

5.使用select()检查该socket描述符是否可写(注意,是可写);

6.根据select()返回的结果判断connect()结果

返回0表示connect超时;

7.将socket设置为阻塞模式;

如果你的程序不需要用阻塞模式的,这步就省了,

不过一般情况下都是用阻塞模式的,这样也容易管理;

下面是示例代码的实现:

/*

* \brief

* tcp client

*/

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#define TRUE  1

#define FALSE 0

#define SERVPORT 8080

#define MAXDATASIZE 100

#define TIMEOUT_TIME 10

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

{

int sockfd, recvbytes;

char rcv_buf[MAXDATASIZE]; /*./client 127.0.0.1 hello */

char snd_buf[MAXDATASIZE];

struct hostent *host;             /* struct hostent

* {

* char *h_name; // general hostname

* char **h_aliases; // hostname's alias

* int h_addrtype; // AF_INET

* int h_length;

* char **h_addr_list;

* };

*/

struct sockaddr_in server_addr;

/* */

fd_set readset, writeset;

struct timeval timeout;

unsigned long ul = 1;

int error = -1, len = sizeof(int);

int bTimeoutFlag = FALSE;

int ret;

if (argc < 3)

{

printf("Usage:%s [ip address] [any string]\n", argv[0]);

return 1;

}

*snd_buf = '\0';

strcat(snd_buf, argv[2]);

if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)

{

perror("socket:");

exit(1);

}

server_addr.sin_family = AF_INET;

server_addr.sin_port = htons(SERVPORT);

inet_pton(AF_INET, argv[1], &server_addr.sin_addr);

memset(&(server_addr.sin_zero), 0, 8);

/*Setting socket to non-blocking mode */

ioctl(sockfd, FIONBIO, &ul);

/* create the connection by socket

* means that connect "sockfd" to "server_addr"

*/

if (connect(sockfd, (struct sockaddr *)&server_addr, sizeof(struct sockaddr)) == -1)

{

timeout.tv_sec  = TIMEOUT_TIME;

timeout.tv_usec = 0;

FD_ZERO(&writeset);

FD_SET(sockfd, &writeset);

ret = select(sockfd+1, NULL, &writeset, NULL, &timeout);

if (ret == 0)              //返回0,代表在描述词状态改变已超过timeout时间

{

getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &error, (socklen_t *)&len);

if (error == 0)          // 超时,可以做更进一步的处理,如重试等

{

bTimeoutFlag = TRUE;

printf("Connect timeout!\n");

}

else

{

printf("Cann't connect to server!\n");

}

goto end;

}

else if ( ret == -1)      // 返回-1, 有错误发生,错误原因存在于errno

{

printf("Cann't connect to server!\n");

goto end;

}

else                      // 成功,返回描述词状态已改变的个数

{

printf("Connect success!\n");

}

}

else

{

printf("Connect success!\n");

ret = TRUE;

}

ul = 0;

ioctl(sockfd, FIONBIO, &ul); //重新将socket设置成阻塞模式

/* 同步阻塞模式,未设置超时 */

if (send(sockfd, snd_buf, sizeof(snd_buf), 0) == -1)

{

perror("send:");

exit(1);

}

printf("send:%s\n", snd_buf);

if ((recvbytes = recv(sockfd, rcv_buf, MAXDATASIZE, 0)) == -1)

{

perror("recv:");

exit(1);

}

rcv_buf[recvbytes] = '\0';

printf("recv:%s\n", rcv_buf);

end:

close(sockfd);

return 0;

}

以上代码,仅供参考,也是为初学者提供一些提示;

主要用到的几个函数,select, ioctl, getsockopt都可以找到相关资料;

需要再说明的是:

1. 虽然用ioctl把套接口设置为非阻塞模式,但select本身是阻塞的,

阻塞的时间就是其超时的时间,

由调用select 的时候的最后一个参数timeval类型的变量指针指向的timeval结构变量来决定的,

timeval结构由一个表示秒数的和一个表示微秒数(long类型)的成员组成,

一般我们设置了秒数就行了,把微妙数设为0(注:1秒等于100万微秒)。

2. select函数中另一个值得一提的参数就是上面我们用到的fd_set类型的变量指针。

调用之前,这个变量里面存了要用select来检查的描述符,

调用之后,针对上面的程序这里面是可写的描述符,我们可以用宏FD_ISSET来检查某个描述符是否在其中。

由于这里只有一个套接口描述符,就没有使用FD_ISSET宏来检查调用select之后这个sockfd是否在set里面,

其实是需要加上这个判断的。

不过这里用了getsockopt来检查,这样才可以判断出这个套接口是否是真的连接上了,

因为我们只是变相的用select来检查它是否连接上了,

实际上select检查的是它是否可写,而对于可写,是针对以下三种条件任一条件满足时都表示可写的:

1) 套接口发送缓冲区中的可用控件字节数大于等于套接口发送缓冲区低潮限度的当前值,

且或者

i) 套接口已连接,或者

ii)套接口不要求连接(UDP方式的)

2) 连接的写这一半关闭。

3) 有一个套接口错误待处理。

这样,我们就需要用getsockopt函数来获取套接口目前的一些信息来判断是否真的是连接上了,

没有连接上的时候还能给出发生了什么错误,

当然我程序中并没有标出那么多状态,只是简单的表示可连接/不可连接。

下面谈谈对这个程序测试的结果。这里针对3种情形做了测试:

1. 目标机器网络正常的情况

可以连接到目标主机,并能成功以阻塞方式进行发包收包作业。

2.目标机器网络断开的情况

在等待设置的超时时间(上面的程序中为10秒)后,显示目标主机不能连接。

3.程序运行前断开目标机器网络,超时时间内,恢复目标机器的网络

在恢复目标主机网络连接之前,程序一只等待;

恢复目标主机后,程序显示连接目标主机成功,并能成功以阻塞方式进行发包收包作业。

以上各种情况的测试结果表明,这种设置connect超时的方法是完全可行的。

九五,飞龙在天,利见大人。

【白话】九五,龙飞上了高空,利于出现德高势隆的大人物。

《象》曰:“飞龙在天”,大人造也。

【白话】《象辞》说:“龙飞上了高空”,象征德高势隆的大人物一定会有所作为。

php socket recv 超时,socket编程中的超时设置示例详解之一相关推荐

  1. c语言自定义color,forecolor c语言中的颜色设置语句详解

    backcolor与forecolor的区别 backcolor:用来设置图像的背景颜色,也用来设置文档.表格.图像等的背景颜色. forecolor:用来设置图像的前景颜色,也用来设置文档.表格.图 ...

  2. java中的守护线的应用_JVM中的守护线程示例详解

    前言 在Java中有两类线程:User Thread(用户线程).Daemon Thread(守护线程) 用个比较通俗的比如,任何一个守护线程都是整个JVM中所有非守护线程的保姆: 只要当前JVM实例 ...

  3. python惰性求值的特点_C#教程之C#函数式编程中的惰性求值详解

    https://www.xin3721.com/eschool/python.html 惰性求值 在开始介绍今天要讲的知识之前,我们想要理解严格求值策略和非严格求值策略之间的区别,这样我们才能够深有体 ...

  4. python编程顺序_Python编程中归并排序算法的实现步骤详解

    基本思想:归并排序是一种典型的分治思想,把一个无序列表一分为二,对每个子序列再一分为二,继续下去,直到无法再进行划分为止.然后,就开始合并的过程,对每个子序列和另外一个子序列的元素进行比较,依次把小元 ...

  5. c 语言字体怎么改,VC++中的字体设置方法详解

    VC++中static text字体改变 窗口都有2个和字体有关的函数:CWnd::GetFont()和SetFont(CFont*, BOOL); 1)CFont* pFont = m_static ...

  6. 【Unity编程】Unity中关于四元数的API详解

    Unity中关于四元数的API详解 Quaternion类 Quaternion(四元数)用于计算Unity旋转.它们计算紧凑高效,不受万向节锁的困扰,并且可以很方便快速地进行球面插值. Unity内 ...

  7. python编程字典100例_python中字典(Dictionary)用法实例详解

    本文实例讲述了python中字典(Dictionary)用法.分享给大家供大家参考.具体分析如下: 字典(Dictionary)是一种映射结构的数据类型,由无序的"键-值对"组成. ...

  8. python编程midi键盘按键_Python中捕获键盘的方式详解

    python中捕获键盘操作一共有两种方法 第一种方法: 使用pygame中event方法 使用方式如下:使用键盘右键为例 if event.type = pygame.KEYDOWN and even ...

  9. Python中的select、epoll详解

    Python中的select.epoll详解 文章目录 Python中的select.epoll详解 一.select 1.相关概念 2.select的特性 1.那么单进程是如何实现多并发的呢??? ...

最新文章

  1. 获得分辨率_直播教程 | 直播画质认知及如何获得最优画质
  2. WP7之题样式与数据绑定
  3. leetcode51. N 皇后(回溯算法)
  4. 基于MATLAB,应用SMOTE算法对小样本类进行过采样
  5. 使用laravel快速开发网站流程(composer)
  6. ERROR: Cannot uninstall ‘PyYAML’. It is a distutils installed project and thus we cannot accurately
  7. 《Vue插件》瀑布流插件vue-masonry的使用与踩坑记录
  8. infer的用法_使用 Infer 进行代码扫描
  9. 3w服务器把信息组织成,HTML小白入坑日记~qwq
  10. latex中表格怎么加标题_导出到LaTeX时,使组织表格标题显示在表格下方 | 所有编程讨论 | zhouni.net...
  11. 粤嵌实习-linux下madplay播放器的下载和使用、线程的介绍和创建一个广告循环播放线程
  12. 安装docker的可视化UI——Portainer
  13. 2023北京老博会·老年用品展·老年食品展·北京老年助浴展
  14. 使用iperf测试峰值带宽
  15. visio 新建个人模板
  16. 展讯走出困境开始爬坡
  17. Qt/C++实现多功能计算器
  18. 双极晶体管是什么?MOS FET和CMOS的关系?
  19. 【Access + SQL + VBA】更新查询同一字段多条件更新
  20. 区块链相关概念与简介

热门文章

  1. HTML复习笔记——CSS排版
  2. Docker:第一章:Docker常用命令
  3. 在网页上打印时用javascript设置打印区域和不打印区域,分页等
  4. 积极的心态带动积极的行为
  5. 为什么近几年一直在说互联网进入了下半场?
  6. 森林防火(资源监管)“空天地人”四位一体监测系统方案
  7. session 生命周期,cookie详解
  8. App Inventor 模拟器问题的解决
  9. http中各个状态码的含义
  10. VMware ESXi 8.0U1 集成网卡驱动和 NVMe 驱动 (网卡驱动集成版,整合版)