注册 登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

还东国的博客

行之苟有恒,久久自芬芳

 
 
 

日志

 
 

在处理SOCKET的各种异常时的问题--关于信号  

2015-02-04 10:32:10|  分类: 网络服务编程技术 |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |

在处理SOCKET的各种异常时的问题

Sigpipe信号会产生三种行为:

系统里边定义了三种处理方法:

1SIG_DFL    /* Default action */

1)如果默认动作是暂停线程,则该线程的执行被暂时挂起。当线程暂停期间,发送给线程的任何附加信号都不交付,直到该线程开始执行,但是SIGKILL除外。

2)把挂起信号的信号动作设置成SIG_DFL,且其默认动作是忽略信号 (SIGCHLD)

2SIG_IGN    /* Ignore action */

3SIG_ERR    /* Error return */

其中第一种为默认的情况,会导致进程的退出。

第二种是忽略掉它,第三种是做为一种错误返回。

针对上面的三种情况,有三种解决方式:

第一种:设置send或者recvflag忽略信号。类似:send(Socket_id, msg, MsgLen_t, MSG_NOSIGNAL)

第二种:调用signal()直接使用全局设置忽略信号 signal(SIG_PIPE, SIG_IGN);

第三种:捕获信号自己处理

struct sigaction action;

action.sa_handler = handle_pipe;

sigemptyset(&action.sa_mask);

action.sa_flags = 0;

sigaction(SIGPIPE, &action, NULL);

void handle_pipe(int sig)

{

        //不做任何处理即可

}

当然这里也可以用signal来处理:

int sig_int(); //My signal handler

    ...

    signal(SIGINT, sig_int);

    ...

int sig_int()

{

   signal(SIGINT, sig_int);

    ....

}

二者的区别如下:   

signal函数每次设置具体的信号处理函数(SIG_IGN)只能生效一次,每次在进程响应处理信号时,随即将信 号处理函数恢复为默认处理方式.所以如果想多次相同方式处理某个信号,通常的做法是,在响应函数开始,再次调用signal设置。如果你不想反复的调用signal,就得使用sigaction,其对处理不断出现的相同的信号比前者要更简单一些。

这里需要强调的,所有的信号机制都是针对进程的,针对线程使用信号,目前还是比较复杂的,或者说,不是特别的靠谱的。

理论上讲,在所有的子线程启动前设置这个信号处理机制后,都应该受到影响,但是如果你只是想影响一个子线程那么就要在单独的子线程内进行了这个信号的处理。

诸如此类:

sigset_t signal_mask;

sigemptyset (&signal_mask);

sigaddset (&signal_mask, SIGPIPE);

int rc = pthread_sigmask (SIG_BLOCK, &signal_mask, NULL);

if (rc != 0) {

printf("block sigpipe error/n");

}

 

这里有一个问题,什么情况下会发生这个信号呢?对一个已经收到FIN包的socket调用read方法, 如果接收缓冲已空, 则返回0, 这就是常说的表示连接关闭. 但第一次对其调用write方法时, 如果发送缓冲没问题, 会返回正确写入(发送). 但发送的报文会导致对端发送RST报文, 因为对端的socket已经调用了close, 完全关闭, 既不发送, 也不接收数据. 所以, 第二次调用write方法(假设在收到RST之后), 会生成SIGPIPE信号, 导致进程退出.

那就又有问题了,什么情况下会出现RST文呢?

1), SYN到达某端口但此端口上没有正在监听的服务器。

2), TCP想取消一个已有连接

3), TCP接收了一个根本不存在的连接上的分节。

在处理SOCKET的各种异常时的问题--关于信号 - 还东国 - 还东国的博客

 

一般来说,这个错误会报产生如下的现象:

write(..) on a socketthat has been closed at the other end will cause a SIGPIPE

Stopped: Broken pipe(Signal SIGPIPE)

另外,在网络通信中会遇到EINTR错误,它的整个机理如下:

慢系统调用(slow system call):此术语适用于那些可能永远阻塞的系统调用。永远阻塞的系统调用是指调用有可能永远无法返回,多数网络支持函数都属于这一类。如:若没有客户连接到服务器上,那么服务器的accept调用就没有返回的保证。

EINTR错误的产生:当阻塞于某个慢系统调用的一个进程捕获某个信号且相应信号处理函数返回时,该系统调用可能返回一个EINTR错误。例如:在socket服务器端,设置了信号捕获机制,有子进程,当在父进程阻塞于慢系统调用时由父进程捕获到了一个有效信号时,内核会致使accept返回一个EINTR错误(被中断的系统调用)

当碰到EINTR错误的时候,可以采取有一些可以重启的系统调用要进行重启,而对于有一些系统调用是不能够重启的。例如:acceptreadwriteselect、和open之类的函数来说,是可以进行重启的。不过对于套接字编程中的connect函数我们是不能重启的,若connect函数返回一个EINTR错误的时候,我们不能再次调用它,否则将立即返回一个错误。针对connect不能重启的处理方法是,必须调用select来等待连接完成。

1write

表示:由于信号中断,没写成功任何数据。

The call was interrupted by a signal before any data was written.

2read

表示:由于信号中断,没读到任何数据。

The call was interrupted by a signal before any data was read.

3sem_wait

函数调用被信号处理函数中断

The call was interrupted by a signal handler.

4recv

由于信号中断返回,没有任何数据可用。

function was interrupted by a signal that was caught, before any data was available.

5EINTR的错误号:

4   EINTR

Interrupted system call

 

自在如是,自在亦不如是。

 

  评论这张
 
阅读(381)| 评论(0)
推荐 转载

历史上的今天

在LOFTER的更多文章

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2017