简介
在上篇笔记中,记录了无锁的accept
结构,但是出现惊群现象。最简单的避免方式是添加一个信号量锁即可,参考这篇笔记即可。
代码
仅仅添加几行改动,客户端使用前一个笔记的。给出代码:
#include <sys/socket.h>
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <string.h>
#include <assert.h>
#include <stdlib.h>
#include <signal.h>
#include <wait.h>
#include <semaphore.h>
#include <sys/stat.h>
#include <fcntl.h>
const int MAX_PROCESS_NUMBER = 15;
const int MAX_BUFFER_SIZE = 1024;
bool stop_server = false;
pid_t* pids = nullptr;
int processNum = 0;
const char* sem_name = "my_sem";
int sem; // 全局锁
void sig_int (int signo); // 终止整个线程池
void process (int fd); // 子进程处理连接
pid_t child_make (int fd); // 产生新的子进程
void child_main (int fd); // 子进程的主要操作
int main (int argc, char* argv[]) {
if (argc != 3) {
printf ("Usage: %s <port of server> <number of process>\n", argv[0]);
exit (0);
}
int port = atoi (argv[1]);
if (port < 1024 || port > 65535) {
perror ("port error\n");
exit (0);
}
processNum = atoi (argv[2]);
if (processNum < 0 || processNum > MAX_PROCESS_NUMBER) {
perror ("process number error\n");
exit (0);
}
struct sockaddr_in serv;
bzero (&serv, sizeof (serv) );
serv.sin_family = AF_INET;
serv.sin_port = htons (port);
serv.sin_addr.s_addr = htonl (INADDR_ANY);
int listenfd = socket (AF_INET, SOCK_STREAM, 0);
if (listenfd < 0) {
perror ("socket() error\n");
exit (1);
}
if (bind (listenfd, (struct sockaddr*) &serv, sizeof (serv) ) < 0) {
perror ("bind() error\n");
exit (1);
}
if (listen (listenfd, 32) < 0) {
perror ("listen() error\n");
exit (1);
}
struct sigaction sa; // 注册终止信号
bzero (&sa, sizeof (sa) );
sa.sa_handler = sig_int;
sa.sa_flags = SA_RESTART;
if (sigaction (SIGTERM, &sa, NULL) < 0) {
perror ("sigaction() error\n");
exit (1);
}
sem = sem_open (sem_name, O_CREAT, 0660, 1); // 在这里创建全局锁
pids = new pid_t[processNum];
for (int i = 0; i < processNum; ++i) {
pids[i] = child_make (listenfd);
}
while (!stop_server) {
sleep (1);
}
exit (0);
}
void sig_int (int signo) {
if (signo != SIGTERM) {
return;
}
for (int i = 0; i < processNum; ++i) {
kill (pids[i], SIGTERM); // 终止子进程
}
if (pids != nullptr) {
delete[] pids;
}
stop_server = true;
}
pid_t child_make (int fd) {
pid_t pid;
if ( (pid = fork() ) > 0) {
return pid;
}
child_main (fd);
}
void child_main (int fd) {
struct sockaddr_in address;
bzero (&address, sizeof (address) );
socklen_t addrlen = sizeof (address);
for (;;) {
// 只需要在这里加锁即可,这样可以有效避免惊群现象
sem_post (&sem);
int connfd = accept (fd, (struct sockaddr*) &address, &addrlen);
sem_wait (&sem);
if (connfd < 0) {
return;
}
puts ("accept data");
process (connfd);
close (fd);
}
}
void process (int fd) {
char buf[MAX_BUFFER_SIZE];
for (;;) {
if (recv (fd, buf, MAX_BUFFER_SIZE, 0) <= 0) {
return;
}
printf ("%s", buf);
}
}