概述
好久没写网络聊天室了,去年暑假可以说写了一暑假,最近复习这些,又因为我一直偏向于多线程,就用多进程复习一下。
下面给出昨天写的基于多进程、共享内存的网络聊天室代码。每个进程负责一个连接,多个进程之间仅共享读,不共享写,因此无需信号量来同步。分配的一段内存中,以数组的方式,分配给每个client一段buffer,每个clilent对应的buffer的索引就是connfd。当一个子进程收到客户端数据后,通过每客户端管道发送自己的pid给主进程,主进程通知除了该子进程的其他进程将该片内存写好的数据转发给其他客户端(sub_proess[pid]=connd)。
代码如下:
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <assert.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <sys/epoll.h>
#include <signal.h>
#include <sys/wait.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
const int USER_LIMIT = 3;
const int BUFFER_SIZE = 1024;
const int FD_LIMIT = 65545;
const int MAX_EVENT_NUMBER = 1024;
const int PROCESS_LIMIT = 65536;
//封装每个客户端连接数据
struct client_data {
sockaddr_in address;
int
connfd;
pid_t
pid;
//负责该客户端子进程的pid
int
pipefd[2];
//每个子进程pipe
};
static const char* shm_name = "/my_shm";
//共享内存的名字
int sig_pipefd[2];
//用来统一事件源
int epollfd;
int listenfd;
int shmfd;
char* share_mem = NULL;
//共享内存起始地址
//客户端连接数组,进程用客户连接的编号来索引这个数组,即可取得相关的客户连接数据
client_data* users = NULL;
//子进程和客户连接的关系映射表,用子进程的pid来索引这个数组,即可取得该进程处理的客户连接的编号
int* sub_process = 0;
int user_count = 0; //客户连接下标,这个名字有点误导,总之user_count>=USER_LIMIT即连接过多
bool stop_child = false;
//停止一个子进程,这个是全部变量,每个子进程都有自己拷贝的一份
int setnonblocking(int fd)
{
int old_option = fcntl(fd, F_GETFL);
int new_option = old_option | O_NONBLOCK;
fcntl(fd, F_SETFL, new_option);
return old_option;
}
void addfd(int epfd, int fd)
{
epoll_event event;
event.data.fd = fd;
event.events = EPOLLIN | EPOLLET;
epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &event);
setnonblocking(fd);
}
void sig_handler(int sig)
{
int save_errno = errno;
int msg = sig;
send(sig_pipefd[1], (char*)&msg, 1, 0);
errno = save_errno;
}
void addsig(int sig, void(*handler)(int), bool restart = true)
{
struct sigaction sa;
memset(&sa, '