曲径通幽论坛

 找回密码
 立即注册
搜索
查看: 12489|回复: 0
打印 上一主题 下一主题

SIGPIPE 信号详解

[复制链接]

4917

主题

5879

帖子

3万

积分

GROAD

曲径通幽,安觅芳踪。

Rank: 6Rank: 6

积分
34382
跳转到指定楼层
楼主
发表于 2012-6-11 20:27:30 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
如果在写入套接字时,读取端已经关闭,那么此时可以得到一个 SIGPIPE 信号,该信号默认情况下会终止当前进程。

例如,当服务器关闭时,而客户端还试图向套接字写入数据的时候会产生一个 SIGPIPE 信号,此时会造成客户端的非正常的退出,防止这种情况可以给客户端安装一个 signal() 函数用来处理 SIGPIPE 信号。

服务器端代码:
[C++] 纯文本查看 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <linux/in.h>

#define     PORT     8099
#define  BACKLOG  5

void process_conn_server(int s);


int main(int argc, char *argv[])
{
    int sock_serv_fd, sock_client_fd;
    
    struct sockaddr_in server_addr;
    struct sockaddr_in client_addr;

    int err;
    
    pid_t pid;

    sock_serv_fd = socket(AF_INET, SOCK_STREAM, 0);
    if (sock_serv_fd < 0) {
        perror("socket");
        exit(EXIT_FAILURE);
    }
    
    bzero(&server_addr, sizeof(server_addr));
    server_addr.sin_family = AF_INET;
    server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    server_addr.sin_port = htons(PORT);

    err = bind(sock_serv_fd, (struct sockaddr *)&server_addr, sizeof(server_addr));
    if(err < 0) {
        perror("bind");
        exit(EXIT_FAILURE);
    }

    err = listen(sock_serv_fd, BACKLOG);
    if (err < 0) {
        perror("listen");
        exit(EXIT_FAILURE);
    }

    for(;;) {
       int addrlen = sizeof(struct sockaddr);
    
       sock_client_fd = accept(sock_serv_fd, (struct sockaddr *)&client_addr, &addrlen);
       if (sock_client_fd < 0)
        continue;

       pid = fork();
       if (pid == 0) {
        close(sock_serv_fd);
          process_conn_server(sock_client_fd);
       } else {
        close(sock_client_fd);
       }
    }
}

void process_conn_server(int s)
{
    ssize_t size = 0;
    char buffer[1024];

    for(;;) {
        size = read(s, buffer, 1024);
        if (size == 0)
           return;

        sprintf(buffer, "Server: %ld bytes readed\n", size);
        write(s, buffer, strlen(buffer)+1);
    }
}

客户端代码:
[C++] 纯文本查看 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <linux/in.h>

#define  PORT     8099

void process_conn_client(int s)
{
    ssize_t size = 0;
    char buffer[1024];

    for(;;) {
       size = read(0, buffer, 1024);
       if (size > 0) {
        write(s, buffer, size);
        size = read(s, buffer, 1024);
        write(1, buffer, size);
      }
    }
}

int main(int argc, char **argv)
{
    int sfd;
    struct sockaddr_in server_addr;
    int err;

    if (argc != 2) {
        printf ("Usage: %s <ip_address>\n", argv[0]);
        exit(EXIT_FAILURE);
    }

    sfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sfd < 0) {
        perror("socket");
        exit(EXIT_FAILURE);
    }

    bzero(&server_addr, sizeof(server_addr));
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(PORT);
    
    inet_pton(AF_INET, argv[1], &server_addr.sin_addr);

    connect(sfd, (struct sockaddr *)&server_addr, sizeof(struct sockaddr));

    process_conn_client(sfd);

    close(sfd);
}

先运行服务器端,然后运行客户端,然后在客户端里随意输入字符:
# ./client 192.168.1.104
sdfa
Server: 5 bytes readed
dsfas
Server: 6 bytes readed
sdfasdf
Server: 8 bytes readed
由上可见,服务器端和客户端的通讯是正常的。那么现在结束掉服务端程序,如果再在客户端上连续输入两次,客户端就会退出。为了验证客户端是接收到 SIGPIPE 信号而退出的,因此在客户端的代码里添加下面一个函数:
[C++] 纯文本查看 复制代码
void sig_pipe(int sign)
{
        printf("Catch a SIGPIPE signal\n");
}

然后在主函数里添加 signal(SIGPIPE, sig_pipe); 函数,接着做上面的实验,那么会在客户端一边看到输出:
# ./client 192.168.1.104
aaa
Server: 4 bytes readed
ddd
Server: 4 bytes readed
iiii
Server: 5 bytes readed
hhjhjh
Server: 7 bytes readed
5222
Server: 5 bytes readed
aaa
bbb
Catch a SIGPIPE signal
2222
Catch a SIGPIPE signal
现在来考察一下,为什么上面的输出中需要连续两次才会导致客户端退出(第一次输入“aaa",第二次输入”bbb“)。可以用 wireshark 这样的嗅探器进行相关数据的捕获:

第 1 条记录是客户端向服务器端发送 5222 这几个字符:

第 2 条记录服务器向客户端发送数据(PSH 标志表示”推送数据“):

第 3 条记录是客户端向服务器简单的发送一个应答。

第 4 条记录是我们按下 Ctrl+ C 结束服务器端时,服务器向客户端发送一个 FIN 标志和一个 ACK 标志。

第 5 条记录是客户端向服务器端的结束通知产生一个应答。

第 6 条记录记录中,客户端又向服务器推送数据了(这时是发送 "aaa"):


第 7 条记录”连接复位“。
一端的 TCP 可以拒绝一个连接请求,可以异常终止一条连接,或可以终止一条空闲的连接 --- 这些都可以用 RST (复位) 标志来完成。
假定在某一端的 TCP 请求要和并不存在的端口进行连接。在另一端的 TCP 就可以把TCP 数据包的控制字段中的 RST 标志位置 1 ,然后将报文发送出去,表示取消该请求。因此,当客户端发送 aaa 时,客户端还能收到服务器发送过来的信息。但是当再次发送时("bbb")时,系统自身就会给它来个 SIGPIPE 信号了,这个可以由程序中所安装的捕捉 SIGPIPE 函数可以证明。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?立即注册

x
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|曲径通幽 ( 琼ICP备11001422号-1|公安备案:46900502000207 )

GMT+8, 2024-4-28 00:16 , Processed in 0.077680 second(s), 24 queries .

Powered by Discuz! X3.2

© 2001-2013 Comsenz Inc.

快速回复 返回顶部 返回列表