基于共享内存和信号触发的聊天室服务器方案(包括多进程处理)

源码部分借鉴自游双的《Linux高性能服务器编程》
完整源码参见github-chatroom

1.全局配置

client_data
#define BUFFER_SIZE 64
#define USER_LIMIT 5
#define FD_LIMIT 65535
#define MAX_EVENT_NUMBER 1024
#define PROCESS_LIMIT 65535

int sig_pipefd[2];
bool stop_child = false; //子进程的标志位
struct client_data
{
   
   
    sockaddr_in address;  //客户端的socket地址
    int connfd;       //socket文件描述符
    pid_t pid;         //处理这个连接的子进程PID
    int pipefd[2];   //和父进程通信用的管道
};

users = new client_data[USER_LIMIT+1];

在主进程里开辟一个空间,储存每个连接的客户的信息。包括客户端的socket地址,socket文件描述符,每一个连接客户端会生成一个工作进程用于处理他,pid储存这个工作进程的pid。每个工作线程通过pipefd[2]和主进程通信。注意这个pipefd是由

 //在主进程和子进程之间建立管道,以传递必要数据
  ret = socketpair(PF_UNIX,SOCK_STREAM,0,users[user_count].pipefd);

建立的,是双向的通信。约定pipefd[0]是工作线程操作的端口, pipefd[1]是主进程操作的端口。

信号处理
//信号处理函数使用管道把信号传递给主循环
void sig_handler(int sig){
   
   
    //保留原来的errno,在函数最后恢复,以保证可重入性。
    int save_errno = errno;
    int msg = sig;
    send(sig_pipefd[1],(void*)&msg,1,0);
    errno = save_errno;
}

//void (*handler)(int) 表示 接受一个int类型参数,返回值为void的函数指针handler
void addsig(int sig,void (*handler)(int),bool restart = true)
{
   
   
    struct sigaction sa;
    memset(&sa,'\0',sizeof(sa));
    sa.sa_handler = handler;
    if (restart)
        sa.sa_flags |=SA_RESTART;
    sigfillset(&sa.sa_mask);
    assert(sigaction(sig,&sa,NULL)!=-1);//捕获到信号sig,使用sa中规定的方法处理
}

2.子进程函数

idx表示该子进程处理的客户连接的编号
users是保存所有客户连接数据的数组
share_mem指出共享内存的起始地址
子进程使用epoll来同时监听两个文件描述符:

  1. 客户连接socket.connfd即users[idx].connfd;
  2. 与父进程(主进程)通信的管道文件描述符pipefd =即users[idx].pipefd[1];
int run_child(int idx,client_data* users,char* share_mem){
   
   
    epoll_event events[MAX_EVENT_NUMBER];
    int child_epollfd = epoll_create(5);
    assert(child_epollfd != -1);
    printf("child pid=%d is running\n",getpid());
    int connfd = users[idx].connfd;
    addfd(child_epollfd,connfd);
    int pipefd = users[idx].pipefd[1];
    addfd(child_epollfd,pipefd);

    int ret;
    addsig(SIGTERM,child_term_handler,false);//设置进程被终止时的信号处理函数

    while(!stop_child){
   
   
        int number = epoll_wait(child_epollfd,events,MAX_EVENT_NUMBER,-1);
        if ((number<0) && (errno != EINTR))
        {
   
   
            /*EINTR是linux中函数的返回状态,在不同的函数中意义不同。
            表示某种阻塞的操作,被接收到的信号中断,造成的一种错误返回值。*/
            printf("epoll failure\n");
            break;
        }
        for (int i=0;i<number;i++){
   
   
            int sockfd = events[i].data.fd;
            //本子进程负责的socket有数据进来
            if ((sockfd == connfd) && 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值