首先给出基本所有的信号,方便以后查询.
*1 SIGHUP 终止 终止控制终端或进程
2 SIGINT 终止 键盘产生的中断(Ctrl-C)
3 SIGQUIT dump 键盘产生的退出
4 SIGILL dump 非法指令
5 SIGTRAP dump debug中断
6 SIGABRT/SIGIOT dump 异常中止
7 SIGBUS/SIGEMT dump 总线异常/EMT指令
8 SIGFPE dump 浮点运算溢出
9 SIGKILL 终止 强制进程终止
10 SIGUSR1 终止 用户信号,进程可自定义用途
11 SIGSEGV dump 非法内存地址引用
12 SIGUSR2 终止 用户信号,进程可自定义用途
*13 SIGPIPE 终止 向某个没有读取的管道中写入数据
14 SIGALRM 终止 时钟中断(闹钟)
15 SIGTERM 终止 进程终止
16 SIGSTKFLT 终止 协处理器栈错误
17 SIGCHLD 忽略 子进程退出或中断
18 SIGCONT 继续 如进程停止状态则开始运行
19 SIGSTOP 停止 停止进程运行
20 SIGSTP 停止 键盘产生的停止
21 SIGTTIN 停止 后台进程请求输入
22 SIGTTOU 停止 后台进程请求输出
*23 SIGURG 忽略 socket发生紧急情况
24 SIGXCPU dump CPU时间限制被打破
25 SIGXFSZ dump 文件大小限制被打破
26 SIGVTALRM 终止 虚拟定时时钟
27 SIGPROF 终止 profile timer clock
28 SIGWINCH 忽略 窗口尺寸调整
29 SIGIO/SIGPOLL 终止 I/O可用
30 SIGPWR 终止 电源异常
31 SIGSYS/SYSUNUSED dump 系统调用异常
与网络编程有关的信号主要有:SIGHUP
,SIGPIPE
和SIGURG
,以下分别来介绍:
-
SIGHUP
: SIGHUP信号的触发时机是在进程的控制终端被挂起
,对于没有控制终端的网络后台程序来说,通常是利用SIGHUP信号来强制服务器程序重读相关的配置文件
,一个典型的例子就是xinetd超级服务器程序。(说实话,目前还没有遇到和处理过这个信号) -
SIGPIPE
:往读端关闭的管道或socket连接中写数据将会触发SIGPIPE信号
,服务器程序需要在代码中捕获并处理该信号,或者至少忽略它,因为SIGPIPE信号的默认处理行为是结束所在进程.- 具体
socket
中产生SIGPIPE
信号的情况是:对端close()通信连接后,本端继续发数据,本端将会收到一个RST复位报文提示本端对端已经关闭连接、需重新建立连接,此时再继续往对端发数据,本端系统将会产生SIFPIPE信号 - 产生的结果是:引起
SIGPIPE
信号的写操作将设置errno
为EPIPE
- 如何处理:
- send函数的情况:send()系统调用的
MSG_NOSIGNAL
可选项可禁止写操作触发SPGPIPE
信号,所以在这种情况下可以使用send()函数反馈的errno
值来判断管道或者socket
连接的对端是否关闭(errno == EPIPE)。 - I/O复用:IO复用也可以用来检测管道和socket连接的读(对)端是否已经关闭,如poll,当读对端关闭时,本(写)端描述符的POLLHUP事件将被触发,当socket连接被对端关闭时,socket上的POLLRDHUP事件将被触发。
- send函数的情况:send()系统调用的
- 具体
-
SIGURG
: 内核通知应用程序带外数据到达的信号.
信号是一种异步事件
,信号处理函数和程序的主循环是两条不同的执行路线(信号处理函数需要尽可能快地执行完毕,以确保信号不被屏蔽太久(因为为了避免一些竞态条件,信号在处理期间,系统不会再次触发它))
网络编程处理的事件主要有I/O,信号和定时器!!!
而其中定时器说到底还是与信号有关,所以处理的主要有两种事件:信号和I/O.而信号又是一种异步事件
,频繁地直接处理信号不利于程序的性能和可靠性,我们需要将他们统一起来处理.
- 如何统一:把
信号的主要处理逻辑放到主循环
中,而不是在信号处理函数中进行主要的逻辑处理。信号处理函数被触发时,只是简单地通知主循环程序接收到信号
,并把信号值传递给主循环,主循环程序根据接收到的信号值执行目标信号对应的逻辑代码。 - 信号处理函数怎么把接收到的信号通知传递给主循环程序呢?利用管道:信号处理函数往管道写端写入信号值,主循环程序从管道的读端读出该信号
- 那么就剩下最后一个问题了-主循环如何知道管道上何时有数据可读呐?so easy!!! 使用I/O复用监听管道读端即可.
上面把信号和I/O事件统一出来处理的方法就是所谓的统一事件源了.
很多优秀的I/O框架库和后台服务程序都统一处理事件和I/O事件,比如Libenent I/O框架库和xinetd超级服务。
实现代码:
base.hpp
#ifndef _BASE_H
#define _BASE_H
#include <sys/socket.h>
#include <errno.h>
#include <unistd.h>
#include <string.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/time.h>
#include <sys/types.h>
#include <stdio.h>
#include <signal.h>
#include <fcntl.h>
#include <sys/epoll.h>
#include <pthread.h>
#include <netdb.h>
#include <math.h>
#include <stdexcept>
#include <iostream>
#include <assert.h>
class CallFailed
{
public:
explicit CallFailed(const std::string &s, const int &line) : ErrString(s), LineNo(line)
{
std::cout << ErrString << LineNo << std::endl;
std::cout << "errno == " << errno << std::endl;
perror("The reason is :");
exit(0);
}
std::string ErrString;
int LineNo;
};
class other_error : public std::logic_error
{
/*等待填充*/
};
#endif
socket.h
#ifndef _SERVSOCKET_H
#define _SERVSOCKET_H
#include "base.hpp"
class BaseSocket
{
public:
BaseSocket() //默认构造函数
{
base_socket_ = socket(AF_INET, SOCK_STREAM, 0);
if (base_socket_ < 0)
throw CallFailed(" socket.h 文件: socket create failed !!! at line ", __LINE__);
}
BaseSocket(const int fd) { base_socket_ = fd; }
//拷贝构造函数
BaseSocket(const BaseSocket &rfs) { base_socket_ = rfs.base_socket_; }
BaseSocket(BaseSocket &&) = delete; //移动构造函数
BaseSocket &operator=(BaseSocket &&) = delete; //移动赋值操作符
BaseSocket &operator=(const BaseSocket &) = delete; //赋值操作符
~BaseSocket()
{
close(base_socket_);
}
inline void SetBaseSocket(const int &fd) { base_socket_ = fd; }
inline int GetBaseSocket() { return base_socket_; }
inline void Close()
{
if (close(base_socket_) == -1)
throw CallFailed(" socket.h 文件: close function failed !!! at line ", __LINE__);
}
inline int Send(const void *buf, size_t len, int flags = SOCK_NONBLOCK)
{
}
inline int Recv(void *buffer, size_t length, int &index, int flags = SOCK_NONBLOCK)
{
}
protected:
int base_socket_ = -1;
};
class ServSocket : public BaseSocket
{
/*继承了 base_socket_ */
public:
ServSocket() = delete;
ServSocket(const ServSocket &) = delete;
ServSocket &operator=(const ServSocket &) = delete;
ServSocket(ServSocket &&) = delete; //移动构造函数
ServSocket &operator=(ServSocket &&) = delete; //移动赋值操作符
explicit ServSocket(const char *ip, const int port)
{
bzero(&serv_addr, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
inet_pton(AF_INET, ip, &serv_addr.sin_addr);
serv_addr.sin_port = htons(port);
}
inline void SetReuse()
{
int optval = 1;
if (setsockopt(base_socket_, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(int)) < 0)
throw CallFailed(" socket.h 文件: setsockopt function failed !!! at line ", __LINE__);
}
inline int Bind()
{
/* Bind(listenfd, (struct sockaddr *)&address, sizeof(address));**/
if (bind(base_socket_, reinterpret_cast<struct sockaddr *>(&serv_addr), sizeof(serv_addr)) < 0)
throw CallFailed(" socket.h 文件: bind function failed !!! at line ", __LINE__);
return (base_socket_);
}
inline int Listen()
{
int backlog = 1024;
char *ptr = NULL;
if ((ptr = getenv("LISTENQ")) != NULL)
backlog = atoi(ptr);
if (listen(base_socket_, backlog) < 0)
throw CallFailed(" socket.h 文件: listen function failed !!! at line ", __LINE__);
return (base_socket_);
}
inline int Accept(struct sockaddr *sa, socklen_t *salenptr)
{
int n;
again:
if ((n = accept(base_socket_, sa, salenptr)) < 0)
{
/*连接在listen后建立,在accept前夭折,此时accept可能会返回错误,对此必须处理,*/
#ifdef EPROTO
if (errno == EPROTO || errno == ECONNABORTED || errno == EWOULDBLOCK)
#else
if (errno == ECONNABORTED)
#endif
goto again;
else
throw CallFailed(" socket.h 文件: accept function failed !!! at line ", __LINE__);
}
return (n);
}
private:
struct sockaddr_in serv_addr;
};
#endif
epoll.h
#ifndef _EPOLL_H
#define _EPOLL_H
#include "base.hpp"
const int MAX_EVENTS_NUMBER = 10000;
class Epoll
{
public:
Epoll()
{
this->epollfd_ = epoll_create(5); /*参数大于0即可*/
if (this->epollfd_ < 0)
throw CallFailed("Epoll.hpp 文件:epoll_create function failed !!! at line ", __LINE__);
}
Epoll(const Epoll &) = delete;
Epoll &operator=(const Epoll &) = delete;
~Epoll() /*不抛异常*/
{
close(epollfd_);
}
inline int Add(const int &fd, bool oneshot = false)
{
epoll_event event;
event.data.fd = fd;
event.events = EPOLLIN | EPOLLET;
if (oneshot)
event.events |= EPOLLONESHOT;
int ret = epoll_ctl(epollfd_, EPOLL_CTL_ADD, fd, &event);
if (ret < 0)
throw CallFailed("Epoll.hpp 文件:epoll_ctl function failed !!! at line ", __LINE__);
return (ret);
}
inline int RemoveEvent(struct epoll_event &ev, int &fd)
{
int ret = epoll_ctl(epollfd_, EPOLL_CTL_DEL, fd, &ev);
if (ret < 0)
throw CallFailed("Epoll.hpp 文件:epoll_ctl function failed !!! at line ", __LINE__);
return (ret);
}
inline int RemoveFd(int &fd)
{
int ret = epoll_ctl(epollfd_, EPOLL_CTL_DEL, fd, 0);
if (ret < 0)
throw CallFailed("Epoll.hpp 文件:epoll_ctl function failed !!! at line ", __LINE__);
return (ret);
}
inline int ModifyEvent(struct epoll_event &ev, int &fd)
{
int ret = epoll_ctl(epollfd_, EPOLL_CTL_MOD, fd, &ev);
if (ret < 0)
throw CallFailed("Epoll.hpp 文件:epoll_ctl function failed !!! at line ", __LINE__);
return (ret);
}
inline int Wait()
{
/* -1: 代表阻塞 */
int ret = epoll_wait(epollfd_, events, MAX_EVENTS_NUMBER, -1);
/*epoll_wait 出错 */
if ((ret < 0) && (errno != EINTR))
throw CallFailed("Epoll.hpp 文件:epoll_wait function failed !!! at line ", __LINE__);
return (ret);
}
inline int GetEpollFd() { return epollfd_; }
inline int GetFdByIndex(const int &index)
{
return events[index].data.fd;
}
inline struct epoll_event *GetEventAddressByIndex(const int &index)
{
return &events[index];
}
inline uint32_t GetEventsByIndex(const int &index)
{
return events[index].events;
}
private:
int epollfd_ = -1;
struct epoll_event events[MAX_EVENTS_NUMBER];
};
#endif
main.cpp
/*************************************************************************
> File Name: main.cpp
> Author: Liu Shengxi
> Mail: 13689209566@163.com
> Created Time: 2018年12月31日 星期一 21时35分13秒
************************************************************************/
#include <iostream>
#include "socket.h"
#include "epoll.h"
using namespace std;
int listenfd = -1;
static int pipefd[2];
void SetNonBlock(const int &fd)
{
int old_option = fcntl(fd, F_GETFL);
int new_option = old_option | O_NONBLOCK;
fcntl(fd, F_SETFL, new_option);
}
void sig_handler(int sig)
{
int save_errno = errno;
int msg = sig;
send(pipefd[1], (char *)&msg, 1, 0); /*将信号值写入管道,通知主循环*/
errno = save_errno;
}
void addsig(int sig)
{
struct sigaction sa;
memset(&sa, '\0', sizeof(sa));
sa.sa_handler = sig_handler;
/*由此信号中断的系统调用是否要再启动*/
sa.sa_flags |= SA_RESTART;
/*sigfillset()用来将参数set 信号集初始化, 然后把所有的信号加入到此信号集里*/
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];
const int port = atoi(argv[2]);
ServSocket server(ip, port);
server.Bind();
server.Listen();
Epoll epoll;
listenfd = server.GetBaseSocket();
epoll.Add(listenfd);
SetNonBlock(listenfd);
auto ret = socketpair(PF_UNIX, SOCK_STREAM, 0, pipefd);
assert(ret != -1);
//pipefd[0] 用来读 pipefd[1] 用来写
// 将信号注册进pipe管道的读端pipefd[0],通过对读端的监听,来实现统一事件源。
SetNonBlock(pipefd[1]);
epoll.Add(pipefd[0]);
addsig(SIGHUP);
addsig(SIGCHLD);
addsig(SIGTERM);
addsig(SIGINT);
while (true)
{
int number = epoll.Wait();
for (int i = 0; i < number; i++)
{
int sockfd = epoll.GetFdByIndex(i);
if (sockfd == listenfd)
{
struct sockaddr_in cli_addr;
socklen_t len = sizeof(cli_addr);
int connfd = server.Accept(reinterpret_cast<struct sockaddr *>(&cli_addr), &len);
}
else if (sockfd == pipefd[0] && (epoll.GetEventsByIndex(i) & EPOLLIN))
{
int sig;
char signals[1024];
ret = recv(pipefd[0], signals, sizeof(signals), 0);
if (ret == -1)
continue;
else if (ret == 0)
continue;
else
{
/*每个信号占一个字节,所以可以按照字节来逐个接受信号*/
for (int i = 0; i < ret; i++)
{
switch (signals[i])
{
case SIGCHLD:
cout << "SIGSHLD signal" << endl;
break;
case SIGHUP:
cout << "SIGHUP signal" << endl;
break;
case SIGTERM:
cout << "SIGTERM signal" << endl;
break;
case SIGINT:
cout << "SIGINT signal" << endl;
break;
}
}
}
}
else
{
}
}
}
close(pipefd[1]);
close(pipefd[0]);
return 0;
}
执行结果: