网络编程常用信号与统一事件源

首先给出基本所有的信号,方便以后查询.

*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,SIGPIPESIGURG,以下分别来介绍:

  • SIGHUP : SIGHUP信号的触发时机是在进程的控制终端被挂起,对于没有控制终端的网络后台程序来说,通常是利用SIGHUP信号来强制服务器程序重读相关的配置文件,一个典型的例子就是xinetd超级服务器程序。(说实话,目前还没有遇到和处理过这个信号)

  • SIGPIPE: 往读端关闭的管道或socket连接中写数据将会触发SIGPIPE信号,服务器程序需要在代码中捕获并处理该信号,或者至少忽略它,因为SIGPIPE信号的默认处理行为是结束所在进程.

    • 具体socket中产生SIGPIPE信号的情况是:对端close()通信连接后,本端继续发数据,本端将会收到一个RST复位报文提示本端对端已经关闭连接、需重新建立连接,此时再继续往对端发数据,本端系统将会产生SIFPIPE信号
    • 产生的结果是:引起SIGPIPE信号的写操作将设置errnoEPIPE
    • 如何处理:
      • send函数的情况:send()系统调用的MSG_NOSIGNAL可选项可禁止写操作触发SPGPIPE信号,所以在这种情况下可以使用send()函数反馈的errno值来判断管道或者socket连接的对端是否关闭(errno == EPIPE)。
      • I/O复用:IO复用也可以用来检测管道和socket连接的读(对)端是否已经关闭,如poll,当读对端关闭时,本(写)端描述符的POLLHUP事件将被触发,当socket连接被对端关闭时,socket上的POLLRDHUP事件将被触发。
  • 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;
}

执行结果:

在这里插入图片描述

posted @ 2019-01-01 17:00  Tattoo_Welkin  阅读(189)  评论(0编辑  收藏  举报