网络编程之LINUX信号

注意发送信号是给进程,不是线程,调用的是KILL函数,SIG是信号种类。pid=0是本进程的其他的进程。

 可以通过设置ERRNO来查看返回的错误,如下:

当目标进程收到信号后,要对信号进行一些执行操作:

定义的接受函数一般作为函数指针,交给系统调用sigaction进行执行函数的调用。

在LINUX中,信号有多种,一般只需要关注网络相关如下几个信号

1.通知系统状态变化:比如alarm定时器或者setitimer定时发送的信号 SIGALRM

2.SIGHUP:控制终端进行挂起

3.SIGPIPE:往读端被关闭的通道或者SOCKET连接中写入数据

4.SIGURG:SOCKET上接收到紧急数据。

代码如下:

#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <assert.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <signal.h>
#include <fcntl.h>

#define BUF_SIZE 1024

static int connfd;

void sig_urg( int sig )
{
    int save_errno = errno;
    
    char buffer[ BUF_SIZE ];
    memset( buffer, '\0', BUF_SIZE );
    int ret = recv( connfd, buffer, BUF_SIZE-1, MSG_OOB );//接受带外数据
    printf( "got %d bytes of oob data '%s'\n", ret, buffer );

    errno = save_errno;
}

void addsig( int sig, void ( *sig_handler )( int ) )
{
    struct sigaction sa;
    memset( &sa, '\0', sizeof( sa ) );
    sa.sa_handler = sig_handler;
    sa.sa_flags |= SA_RESTART;
    sigfillset( &sa.sa_mask );
    assert( sigaction( sig, &sa, NULL ) != -1 );
}

int main( int argc, char* argv[] )
{
    if( argc <= 2 )
    {
        printf( "usage: %s ip_address port_number\n", basename( argv[0] ) );
        return 1;
    }
    const char* ip = argv[1];
    int port = atoi( argv[2] );

    struct sockaddr_in address;
    bzero( &address, sizeof( address ) );
    address.sin_family = AF_INET;
    inet_pton( AF_INET, ip, &address.sin_addr );
    address.sin_port = htons( port );

    int sock = socket( PF_INET, SOCK_STREAM, 0 );
    assert( sock >= 0 );

    int ret = bind( sock, ( struct sockaddr* )&address, sizeof( address ) );
    assert( ret != -1 );

    ret = listen( sock, 5 );
    assert( ret != -1 );

    struct sockaddr_in client;
    socklen_t client_addrlength = sizeof( client );
    connfd = accept( sock, ( struct sockaddr* )&client, &client_addrlength );
    if ( connfd < 0 )
    {
        printf( "errno is: %d\n", errno );
    }
    else
    {
        addsig( SIGURG, sig_urg );//发起引号
        fcntl( connfd, F_SETOWN, getpid() );//使用SIGURG时,必须设置SOCKET的主进程或进程组

        char buffer[ BUF_SIZE ];
        while( 1 )
        {
            memset( buffer, '\0', BUF_SIZE );
            ret = recv( connfd, buffer, BUF_SIZE-1, 0 );
            if( ret <= 0 )
            {
                break;
            }
            printf( "got %d bytes of normal data '%s'\n", ret, buffer );
        }

        close( connfd );
    }

    close( sock );
    return 0;
}
/*带外信号就是紧急信息,比如TCP紧急指针,紧急信息有着更高级的发送优先权,所以会排在普通信息的最前面,第一个被发送出去。*/

上述情况要注意,阻塞I/O在阻塞时即使收到信号也不会调用信号处理,而是会被中断系统调用,比如read(),write()等阻塞I/O。

对于是暂停信号的接受,没有设置信号的处理函数,则会中断epoll_wait,connect等调用。

        下面介绍信号函数:

1.

signal系统调用一般并不常用,而是使用sigaction函数。

2.

注意二三参数都是sigaction类结构体指针,该结构体类型如下:

成员变量sa_handler是信号处理相关的函数指针。

sa_mask是成员设置进程的信号掩码,用于内核进行运算执行,其是信号集sigset_t类型。信号集就是一个数组,类似于fd_set,该数组的每一位都表示一个信号。对于网络编程,一般不需要设置特殊的sa_mask.用函数sigfillset进行初始化即可。

sa_flags是标志量,用于告诉程序收到信号时的行为,应该做什么。

代码如下:

#include <sys/socket.h>
#include <assert.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include <signal.h>
#include <sys/epoll.h>
#include <pthread.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <iostream>
#include <fcntl.h>

const int MAX_EVENT_SIZE=1024;
static int pipefd[2];

int setnoblocking(int fd){
    int res=fcntl(fd,F_GETFL);
    int new_opation=res|O_NONBLOCK;
    fcntl(fd,F_SETFL,new_opation);
    return fd;
}

void addfd(int epollfd,int fd){
     epoll_event event;
     event.data.fd=fd;
     event.events=EPOLLET|EPOLLIN;
     epoll_ctl(epollfd,EPOLL_CTL_ADD,fd,event);
     setnoblocking(fd);
}

void sig_handler(int sig){
    int save_errno=errno;
    int mig=sig;
    send(pipefd[1],(char*)&mig,1,0);
    errno=save_errno;
}

void addsig(int sig){
    struct sigaction tem;
    tem.sa_handler=sig_handler;
    tem.sa_flags|=SA_RESTART;
    sigfillset(&tem.sa_mask);
   int ret=sigaction(sig,&tem,NULL);
   assert(ret>=0);
}


  int main(int argc,char* argv[]){
      if(argc<=2){
        std::cout<<"error"<<std::endl;
        exit(1);
      }

      const char* ip=argv[1];
      int port=atoi(argv[2]);

      int ret=0;
      struct sockaddr_in address;
      memest(&address,0,sizeof(address));
      address.sin_family=AF_INET;
      inet_pton(AF_INET,ip,&address.sin_addr);
      address.sin_port=htonl(port);

      int listenfd=socket(PF_INET,SOCK_STREAM,0);
      if(bind(listenfd,(struct sockaddr*)&address,sizeof(address))==-1){
        std::cout<<"bind() error"<<std::endl;
        exit(1);
      }

      if(listen(listenfd,5)==-1){
        std::cout<<"listen() error"<<std::endl;
        exit(1);
      }

     epoll_event events[MAX_EVENT_SIZE];
      int epollfd=epoll_create(5);
      addfd(epollfd,listenfd);
      
      ret=socketpair(PF_UNIX,SOCK_STREAM,0,pipefd);
       setnoblocking(pipefd[1]);//只将写入端加入到非阻塞,而不是监听
       addfd(epollfd,pipefd[0]);//注册可读事件

       addsig(SIGHUP);
       addsig(SIGCHLD);
       addsig(SIGTERM);
       addsig(SIGINT);

       bool stop_server=false;


      while(!stop_server){
int res=epoll_wait(epollfd,events,MAX_EVENT_SIZE,-1);
    if((res<0)&&(errno!=EINTR)){//EINTR 系统调用被中断
        std::cout<<"epoll error"<<std::endl;
        break;
    }
   for(int i=0;i<res;i++){
    int sockfd=events[i].data.fd;
    if(sockfd==listenfd){
        struct sockaddr_in client_data;
        socklen_t size=sizeof(client_data);
        int p=accept(listenfd,(struct sockaddr* &client_data,&size));
        addfd(epollfd,p);
    }else if(sockfd==pipefd[0]&&(events[i].events&EPOLLIN)){
        int sig;
        char siganls[1024];
        ret=recv(pipefd[0],siganls,sizeof(siganls),0);
        if(ret==-1){
            continue;
        }else if(ret==0){
            continue;
        }else{
            for(int i=0;i<ret;i++){
                switch (siganls[i])
                {
                case SIGCHLD:
                 case SIGHUP:{
                  continue;//
                }
                case SIGTERM:
                case SIGINT:
                {
                 stop_server=true;
                }
                
                }
            }
        }
    }
   else{

   }

      }
      }      
std::cout<<"close fd"<<std::endl;
close(listenfd);
close(pipefd[1]);
close(pipefd[0]);
return 0;


  }

总结就是设置信号处理函数时,处理函数将收到信号通过管道方式传递给主循环进行聆听判断,对不同信号做出相应的反应。

  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值