注意发送信号是给进程,不是线程,调用的是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;
}
总结就是设置信号处理函数时,处理函数将收到信号通过管道方式传递给主循环进行聆听判断,对不同信号做出相应的反应。