c#中connect函数_linux系统中socket错误码:eintr和eagain的
处理⽅法
1、什么是慢系统调⽤?
该术语适⽤于那些可能永远阻塞的系统调⽤。永远阻塞的系统调⽤是指调⽤永远⽆法返回,多数⽹络⽀持函数都属于这⼀类。如:若没有客户连接到服务器上,那么服务器的accept调⽤就会永远阻塞。
慢系统调⽤可以被永久阻塞,包括以下⼏个类别:
(1)读写‘慢’设备(包括pipe,终端设备,⽹络连接等)。读时,数据不存在,需要等待;写时,缓冲区满或其他原因,需要等待。
(2)当打开某些特殊⽂件时,需要等待某些条件,才能打开。例如:打开中断设备时,需要等到连接设备的modem响应才能完成。
(3)pause和wait函数。pause函数使调⽤进程睡眠,直到捕获到⼀个信号。wait等待⼦进程终⽌。
(4)某些ioctl操作。
(5)某些IPC操作。
linux下的sleep函数2、EINTR错误产⽣的原因-(阻塞的系统调⽤、或者⾮阻塞的系统调⽤)
如果进程在⼀个慢系统调⽤(slow system call)中阻塞时,当捕获到某个信号且相应信号处理函数返回时,这个系统调⽤不再阻塞⽽是被中断,就会调⽤返回错误(⼀般为-1)&&设置errno为EINTR(相应的错误描述为“Interrupted system call”)。
如下表所⽰的系统调⽤就会产⽣EINTR错误,当然不同的函数意义也不同。
3、解决办法
既然系统调⽤会被中断,那么别忘了要处理被中断的系统调⽤。有三种处理⽅式:
解决⽅法1:重启被中断的系统调⽤
当碰到EINTR错误的时候,有⼀些可以重启的系统调⽤要进⾏重启,⽽对于有⼀些系统调⽤是不能够重启的。例如:accept、read、write、select、和open之类的函数来说,是可以进⾏重启的。不过对于套接字编程中的connect函数是不能重启的,若connect函数返回⼀个EINTR错误的时候,我们不能再次调⽤它,否则将⽴即返回⼀个错误。针对connect不能重启的处理⽅法是,必须调⽤select来等待连接完成。
理解“重启”?⼀些IO系统调⽤执⾏时,如 read 等待输⼊期间,如果收到⼀个信号,系统将中断read, 转⽽执⾏信号处理函数. 当信号处理返回后, 系统遇到了⼀个问题: 是重新开始这个系统调⽤? 还是让系统调⽤失败?早期UNIX系统的做法是:中断系统调⽤,并让系统调⽤失败, ⽐如read返回 -1, 同时设置 errno 为EINTR中断了的系统调⽤是没有完成的调⽤,它的失败是临时性的,如果再次调⽤则可能成功,这并不是真正的失败,所以要对这种情况进⾏处理, 典型的⽅式为“重启”,采⽤accept函数为例⼦,代码如下
需要C/C++ Linux服务器架构师学习资料私信“资料”(资料包括C/C++,Linux,golang技
术,Nginx,ZeroMQ,MySQL,Redis,fastdfs,MongoDB,ZK,流媒体,CDN,P2P,K8S,Docker,TCP/IP,协
程,DPDK,ffmpeg等),免费分享
ACCEPT:    clifd = accept(srvfd,(struct sockaddr*)&cliaddr,&cliaddrlen);    if (clifd == -1) {        if (errno == EINTR) {            goto ACCEPT;        } else {
解决⽅法2:安装信号时设置 SA_RESTART属性(该⽅法对有的系统调⽤⽆效)
struct sigaction action;        action.sa_handler = handler_func;    sigemptyset(&action.sa_mask);    action.sa_flags = 0;    /* 设置SA_RESTART属性 */    action.sa_f 解决⽅法3: 忽略信号(让系统不产⽣信号中断)
struct sigaction action;        action.sa_handler = SIG_IGN;    sigemptyset(&action.sa_mask);        sigaction(SIGALRM, &action, NULL);
EAGAIN-(⼀般⽤于⾮阻塞的系统调⽤)
⾮阻塞的系统调⽤,由于资源限制/不满⾜条件,导致返回值为EAGAIN
在Linux环境下开发经常会碰到很多错误(设置errno),其中EAGAIN是其中⽐较常见的⼀个错误(⽐如⽤在⾮阻塞操作中)。
如:⾸先是把套接字设置为异步的了,然后在使⽤write发送数据时采取的⽅式是循环发送⼤量的数据;由于是异步的,writesend将要发送
的数据提交到发送缓冲区后是⽴即返回的,并不需要对端确认数据已接收。在这种情况下是很有可能出现发送缓冲区被填满,导致
writesend⽆法再向缓冲区提交要发送的数据。因此就产⽣了Resource temporarily unavailable的错误(资源暂时不可⽤),EAGAIN 的意
思也很明显,就是要你再次尝试。
从字⾯上来看,是提⽰再试⼀次。这个错误经常出现在当应⽤程序进⾏⼀些⾮阻塞(non-blocking)操作(对⽂件或socket)的时候。
如:以 O_NONBLOCK的标志打开⽂件/socket/FIFO,如果连续做read操作⽽没有数据可读。此时程序不会阻塞起来等待数据准备就绪返
回,read函数会返回⼀个错误EAGAIN,提⽰你的应⽤程序现在没有数据可读请稍后再试。
⼜例如,当⼀个系统调⽤(⽐如fork)因为没有⾜够的资源(⽐如虚拟内存)⽽执⾏失败,返回EAGAIN提⽰其再调⽤⼀次(也许下次就能成功)。
Linux - ⾮阻塞socket编程处理EAGAIN错误
在linux进⾏⾮阻塞的socket接收数据时经常出现Resource temporarily unavailable,errno代码为11(EAGAIN),这是什么意思? ⇒⇒
⇒ 这表明在⾮阻塞模式下调⽤了阻塞操作,在该操作没有完成就返回这个错误,这个错误不会破坏socket的同步,不⽤管它,下次循环接
着recv就可以。对⾮阻塞socket⽽⾔,EAGAIN不是⼀种错误。在VxWorks和Windows上,EAGAIN的名字叫做EWOULDBLOCK。iReadSizeOnce=read(iOpenCom,RxBuf+iReadSize,1024);if (iReadSizeOnce != ZERO){    if (iReadSizeOnce != EAGAIN)    {        continue;    }    else    {
慢系统调⽤:可能永远阻塞的系统调⽤,这很关键,不适⽤于⾮诸塞的情况。永远阻塞的系统调⽤是指调⽤永远⽆法返回,多数⽹络⽀持函
数都属于这⼀类。如:若没有客户连接到服务器上,那么服务器的accept调⽤就会⼀直阻塞。
EINTR说明:如果进程在⼀个慢系统调⽤(slow system call)中阻塞时,当捕获到某个信号且相应信号处理函数返回时,这个系统调⽤被中断,调⽤返回错误,设置errno为EINTR(相应的错误描述为“Interrupted system call”)。
怎么看哪些系统条⽤会产⽣EINTR错误呢?man 7 signal,在ubuntu 10.04上可以查看,哪些系统调⽤会产⽣ EINTR错误。
如何处理被中断的系统调⽤
既然系统调⽤会被中断,那么别忘了要处理被中断的系统调⽤。有三种处理⽅式:
◆ ⼈为重启被中断的系统调⽤
◆ 安装信号时设置 SA_RESTART属性(该⽅法对有的系统调⽤⽆效)
◆ 忽略信号(让系统不产⽣信号中断)
⼈为重启被中断的系统调⽤
⼈为当碰到EINTR错误的时候,有⼀些可以重启的系统调⽤要进⾏重启,⽽对于有⼀些系统调⽤是不能够重启的。例如:accept、read、write、select、和open之类的函数来说,是可以进⾏重启的。不过对于套接字编程中的connect函数我们是不能重启的,若connect函数
返回⼀个EINTR错误的时候,我们不能再次调⽤它,否则将⽴即返回⼀个错误。针对connect不能重启的处理⽅法是,必须调⽤select来等待连接完成。
这⾥的“重启”怎么理解?
⼀些IO系统调⽤执⾏时,如 read 等待输⼊期间,如果收到⼀个信号,系统将中断read, 转⽽执⾏信
号处理函数. 当信号处理返回后, 系统遇到了⼀个问题: 是重新开始这个系统调⽤, 还是让系统调⽤失败?早期UNIX系统的做法是, 中断系统调⽤,并让系统调⽤失败, ⽐如read返回 -1, 同时设置 errno 为EINTR中断了的系统调⽤是没有完成的调⽤,它的失败是临时性的,如果再次调⽤则可能成功,这并不是真正的失败,所以要对这种情况进⾏处理, 典型的⽅式为:
connect处理⽅式,抄袭3原⽂,没有测试过,处理⽅法是对的。
connect的问题,当connect遇到EINTR错误时,不能向上⾯那样重新进⼊循环处理,原因是,connect的请求已经发送向对⽅,正在等待对⽅回应,这是如果重新调⽤connect,⽽对⽅已经接受了上次的connect请求,这⼀次的connect就会被拒绝,因此,需要使⽤select或poll调⽤来检查socket的状态,如果socket的状态就绪,则connect已经成功,否则,视错误原因,做对应的处理。
#include poll.h int check_conn_is_ok(socket_t sock) {struct pollfd fd;int ret = 0;socklen_t len = 0; fd.fd = sock;fd.events = POLLOUT; while ( poll (&fd, 1, -1)
在调⽤connect时,这样使⽤:
#include erron.h ....if(connnect()) {    if(errno == EINTR) {        if(check_conn_is_ok() < 0) {              perror();              return -1;        }        else {            prin 我⼀般使⽤continue或者goto来处理。
安装信号时设置 SA_RESTART属性
我们还可以从信号的⾓度来解决这个问题, 安装信号的时候, 设置 SA_RESTART属性,那么当信号处理函数返回后, 不会让系统调⽤返
回失败,⽽是让被该信号中断的系统调⽤将⾃动恢复。
struct sigaction action;    action.sa_handler = handler_func;  sigemptyset(&action.sa_mask);  action.sa_flags = 0;  /* 设置SA_RESTART属性 */  action.sa_flags |=
但注意,并不是所有的系统调⽤都可以⾃动恢复。如msgsnd喝msgrcv就是典型的例⼦,msgsnd/msgrcv以block⽅式发送/接收消息
时,会因为进程收到了信号⽽中断。此时msgsnd/msgrcv将返回-1,errno被设置为EINTR。且即使在插⼊信号时设置了
SA_RESTART,也⽆效。在man msgrcv中就有提到这点:
msgsnd and msgrcv are never automatically restarted after being interrupted by a signal handler, regardless of the setting
of the SA_RESTART flag when establishing a signal handler.
忽略信号
当然最简单的⽅法是忽略信号,在安装信号时,明确告诉系统不会产⽣该信号的中断。
struct sigaction action;    action.sa_handler = SIG_IGN;  sigemptyset(&action.sa_mask);    sigaction(SIGALRM, &action, NULL);
#include  #include  #include  #include  #include  #include      void sig_handler(int signum)  {      printf("in handler");      sleep(1);      printf("handler return
在linux测试结果:
不设置SA_RESTART,执⾏结果如下:
说明接受信号处理完成以后,主函数收到EINTR信号,read函数返回-1,退出
设置SA_RESTART,执⾏结果如下:
说明设置SA_RESTART参数以后,⾃动重新调⽤read函数,没有体现在应⽤层代码中,在应⽤层看来,这个EINTR没有造成任何影响。
总结:
慢系统调⽤(slow system call)会被信号中断,系统调⽤函数返回失败,并且errno被置为EINTR(错误描述为“Interrupted system
call”)。
处理⽅法有以下三种:①⼈为重启被中断的系统调⽤;②安装信号时设置 SA_RESTART属性;③忽略信号(让系统不产⽣信号中断)。
有时我们需要捕获信号,但⼜考虑到第②种⽅法的局限性(设置 SA_RESTART属性对有的系统⽆效,如msgrcv),所以在编写代码时,⼀
定要“⼈为重启被中断的系统调⽤”。

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。