Skip to content

Instantly share code, notes, and snippets.

@cadl
Created March 17, 2014 11:03
Show Gist options
  • Save cadl/9597426 to your computer and use it in GitHub Desktop.
Save cadl/9597426 to your computer and use it in GitHub Desktop.
simple_server
#define MAX_EVENTS 512
#define LOCK_NAME ".server.lock"
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <signal.h>
#include <assert.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/file.h>
#include <sys/socket.h>
#include <sys/epoll.h>
#include <netinet/in.h>
struct worker_proc {
pid_t pid;
struct worker_proc *next;
}root;
void sig_handler(int sig) {
struct worker_proc* wp = &root;
if (sig == SIGINT) {
while (wp) {
kill(wp->pid, SIGINT);
wp = wp->next;
}
}
}
static inline int acquire_lock(char *file_name) {
int lockfd;
if ((lockfd = open(file_name, O_CREAT|O_RDWR, 0666)) < 0) {
return -1;
}
if (flock(lockfd, LOCK_NB|LOCK_EX) < 0) {
close(lockfd);
return -1;
}
return lockfd;
}
static inline void release_lock(int lockfd) {
flock(lockfd, LOCK_UN);
close(lockfd);
}
static int setnonblock(int fd) {
int flags, s;
flags = fcntl(fd, F_GETFL, 0);
if (flags == -1) {
perror("fcntl get flag");
return -1;
}
flags |= O_NONBLOCK;
s = fcntl(fd, F_SETFL, flags);
if (s == -1) {
perror("fcntl set flags");
return -1;
}
return 0;
}
void worker_proc_read(int fd) {
int nread;
char read_buf[512];
while ((nread = read(fd, read_buf, 512)) > 0) {
read_buf[nread] = '\0';
}
if (nread == -1 && errno != EAGAIN) {
perror("read error");
}
}
void worker_proc_write(int fd) {
int nwrite, data_size, n;
char write_buf[256] = "HTTP/1.1 200 OK\r\nContent-Length: 11\r\n\r\nHello World";
data_size = strlen(write_buf);
n = data_size;
while (n > 0) {
nwrite = write(fd, write_buf+data_size-n, n);
if (nwrite < n) {
if (nwrite == -1 && errno != EAGAIN) {
perror("write errpr");
return;
}
break;
}
n -= nwrite;
}
close(fd);
}
void worker_proc_loop(int listen_fd) {
struct epoll_event ev, events[MAX_EVENTS];
int epollfd, nfds, connfd;
int lockfd, fd;
struct sockaddr_in client_addr;
socklen_t length = sizeof(client_addr);
epollfd = epoll_create(10);
if (epollfd == -1) {
perror("epoll_create");
exit(EXIT_FAILURE);
}
ev.events = EPOLLIN;
ev.data.fd = listen_fd;
if (epoll_ctl(epollfd, EPOLL_CTL_ADD, listen_fd, &ev) == -1) {
perror("epoll_ctl: listen_sock");
exit(EXIT_FAILURE);
}
for (;;) {
nfds = epoll_wait(epollfd, events, MAX_EVENTS, -1);
if (nfds == -1) {
perror("epoll_wait");
exit(EXIT_FAILURE);
}
for (int i=0; i<nfds; i++) {
fd = events[i].data.fd;
if (fd == listen_fd) {
lockfd = acquire_lock(LOCK_NAME);
if (lockfd == -1) {
continue;
}
while ((connfd = accept(listen_fd,
(struct sockaddr *)&client_addr,
&length)) > 0) {
if (setnonblock(connfd) == -1) {
perror("nonblock");
exit(EXIT_FAILURE);
}
ev.events = EPOLLIN| EPOLLET;
ev.data.fd = connfd;
if (epoll_ctl(epollfd, EPOLL_CTL_ADD, connfd, &ev) == -1) {
perror("epoll_ctl: conn_sock");
exit(EXIT_FAILURE);
}
}
release_lock(lockfd);
if (connfd == -1) {
if (errno != EAGAIN && errno != ECONNABORTED
&& errno != EPROTO && errno != EINTR) {
perror("accept");
}
}
}
else
{
if (events[i].events & EPOLLIN) {
worker_proc_read(fd);
ev.data.fd = fd;
ev.events = events[i].events | EPOLLOUT;
if (epoll_ctl(epollfd, EPOLL_CTL_MOD, fd, &ev) == -1) {
perror("epoll_ctl: mode");
exit(EXIT_FAILURE);
}
}
if (events[i].events & EPOLLOUT) {
worker_proc_write(fd);
}
}
}
}
}
struct worker_proc* worker_proc_new(int listen_fd){
pid_t pid;
struct worker_proc *wp, *tmp;
wp = &root;
pid = fork();
assert(pid >= 0);
if (pid > 0) {
while (wp->next) {
wp = wp->next;
}
tmp = (struct worker_proc *)malloc(sizeof(struct worker_proc));
tmp->pid = pid;
tmp->next = NULL;
wp->next = tmp;
return wp;
}
else {
worker_proc_loop(listen_fd);
return NULL;
}
}
int main() {
int server_fd, wait_status;
struct sockaddr_in server_addr;
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = htons(INADDR_ANY);
server_addr.sin_port = htons(8888);
server_fd = socket(AF_INET, SOCK_STREAM, 0);
if (server_fd < 0) {
perror("socket");
exit(EXIT_FAILURE);
}
if (setnonblock(server_fd) == -1) {
perror("nonblock");
exit(EXIT_FAILURE);
}
if (bind(server_fd, (struct sockaddr *)&server_addr,
sizeof(server_addr)) < 0) {
perror("bind");
exit(EXIT_FAILURE);
}
if (listen(server_fd, 5) < 0) {
perror("listen");
exit(EXIT_FAILURE);
}
for (int i=0; i<4; i++) {
worker_proc_new(server_fd);
}
close(server_fd);
for (int i=0; i<4; i++) {
wait(&wait_status);
}
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment