您的位置:首页 > 其它

几种并发服务器模型的实现:多线程,多进程,select,poll,epoll

2014-07-31 00:18 1041 查看
客户端使用select模型:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/select.h>
#include <signal.h>

#define ERR_EXIT(m) \
do { \
perror(m);\
exit(EXIT_FAILURE);\
}while(0)

static void do_client(int fd)
{
char recvbuf[MAXLINE + 1] = {0};
char sendbuf[MAXLINE + 1] = {0};

fd_set reade, ready;
FD_ZERO(&reade);
int fd_stdin = fileno(stdin);
FD_SET(fd_stdin, &reade);
FD_SET(fd, &reade);
int fd_max = (fd_stdin > fd) ? fd_stdin : fd;

int ret;
while(1)
{
ready = reade;
ret = select( fd_max+1, &ready, NULL, NULL, NULL);//轮询
if(ret == -1)
{
if(errno == EINTR)
continue;
ERR_EXIT("select");
}else if(ret ==  0)
{
continue;
}

if(FD_ISSET(fd_stdin, &ready))
{
if(fgets(sendbuf, sizeof(sendbuf), stdin) == NULL)
{
close(fd);
break;
}else
{
if( -1 == write(fd, sendbuf, strlen(sendbuf)))
printf("write\n");
}
}

if(FD_ISSET(fd, &ready))
{
int nread = read(fd, recvbuf, MAXLINE);
if(nread < 0)
ERR_EXIT("read");
if(nread == 0)//如果没接收到消息,打印关闭描述符,退出循环
{
fprintf(stdout, "fd close\n");
break;
}
fprintf(stdout, "receive:%s", recvbuf);
}
memset(recvbuf, 0, sizeof recvbuf);
memset(sendbuf, 0, sizeof sendbuf);
}
}
void handle(int signum)
{
printf("sigpipe\n");
}

int main(int argc, const char *argv[])
{
signal(SIGPIPE, SIG_IGN);
int fd = socket(AF_INET, SOCK_STREAM, 0);
if(fd < 0)
ERR_EXIT("socket");

struct sockaddr_in cliaddr;
cliaddr.sin_family = AF_INET;
cliaddr.sin_port = htons(8888);
cliaddr.sin_addr.s_addr = inet_addr("127.0.0.1");
socklen_t len = sizeof cliaddr;

int ret ;
if((ret = connect(fd, (struct sockaddr*)&cliaddr, len)) == -1)
{
close(fd);
ERR_EXIT("connect");
}
do_client(fd);
close(fd);
return 0;
}


1.并发多进程服务器

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include "rio.h"//封装了网络编程中的readn  writen   readline 3个函数
#include <signal.h>

#define ERR_EXIT(m) \
do { \
perror(m);\
exit(EXIT_FAILURE);\
}while(0)

static void do_service(int fd)
{
rio_t rt;
rio_init(&rt, fd);
char recvbuf[1024] = {0};
int ret;
while(1)
{
memset(recvbuf, 0, sizeof recvbuf);
ret = rio_readline(&rt, recvbuf, 1024);
if(ret == 0)
{
close(fd);
exit(EXIT_SUCCESS);
}
rio_writen(fd, recvbuf, strlen(recvbuf)) ;
}
}

void handle(int signum)//对SIGCHLD信号的处理函数
{
while(waitpid(-1, NULL, WNOHANG) > 0);
return ;
}

int listenf();//封装了socket,bind,listen3个函数,返回需要监听的连接socket描述符

int main(int argc, const char *argv[])
{
if(signal(SIGPIPE, SIG_IGN) == SIG_ERR)//对客户端关闭导致的信号的处理
ERR_EXIT("signal_pipe");
if(signal(SIGCHLD, handle) == SIG_ERR)//对子进程结束后资源的回收
ERR_EXIT("signal_chld");

int listenfd = listenf();

struct sockaddr_in cliaddr;
bzero(&cliaddr, sizeof(cliaddr));
socklen_t cli_len = sizeof cliaddr;
int clientfd;
while(1)
{
clientfd = accept(listenfd, (struct sockaddr*)&cliaddr, &cli_len);
if(clientfd == -1)
{
close(listenfd);
ERR_EXIT("accept");
}

int pid;
if((pid = fork()) == -1)
{
ERR_EXIT("fork");
}else if(pid == 0)
{
close(listenfd);
do_service(clientfd);
exit(EXIT_SUCCESS);
}
close(clientfd);
}
close(listenfd);
return 0;
}

int listenf()
{
int listenfd = socket(AF_INET, SOCK_STREAM, 0);//准备一个socketfd
if(listenfd == -1 )
ERR_EXIT("socket");

int on = 1;
if(setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1)//setsockopt设置端口复用
{
close(listenfd);
ERR_EXIT("setsockopt");
}

struct sockaddr_in seraddr;
seraddr.sin_family = AF_INET;
seraddr.sin_port = htons(8888);
seraddr.sin_addr.s_addr = htonl(INADDR_ANY);
socklen_t len = sizeof(seraddr);
if(bind(listenfd, (struct sockaddr*)&seraddr, len) == -1)//监听socket端口,
{
close(listenfd);
ERR_EXIT("bind");
}

if(listen(listenfd, 6) == -1)
{
close(listenfd);
ERR_EXIT("listen");
}
return listenfd;
}


2.多线程服务器模型:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include "rio.h"
#include <signal.h>
#include <pthread.h>

#define ERR_EXIT(m) \
do { \
perror(m);\
exit(EXIT_FAILURE);\
}while(0)

static void do_service(int fd)
{
rio_t rt;
rio_init(&rt, fd);
char recvbuf[1024] = {0};
int ret;
int rcnt = 0;
while(1)
{
memset(recvbuf, 0, sizeof recvbuf);
ret = rio_readline(&rt, recvbuf, 1024);
if(ret == 0)
{
close(fd);
//         break;
pthread_exit(NULL);
}
write(fd, recvbuf, strlen(recvbuf)) ;
}
}

void handle(int signum)
{
printf("hello\n");
}

void *pthread_func(void *arg)
{
pthread_detach(pthread_self());
int *q = (int*)arg;
int p = *(int*)arg;
free(q);
do_service(p);
close(p);
}
int listenf();

int main(int argc, const char *argv[])
{
if(signal(SIGPIPE, handle) == SIG_ERR)
ERR_EXIT("signal");
int listenfd = listenf();

struct sockaddr_in cliaddr;
bzero(&cliaddr, sizeof(cliaddr));
socklen_t cli_len = sizeof cliaddr;
int clientfd;
while(1)
{
clientfd = accept(listenfd, (struct sockaddr*)&cliaddr, &cli_len);
if(clientfd == -1)
{
close(listenfd);
ERR_EXIT("accept");
}

pthread_t tid;
int *p = (int*)malloc(sizeof(int));
*p = clientfd;
pthread_create(&tid, NULL, pthread_func, p);
}
close(listenfd);
return 0;
}

int listenf()
{
int listenfd = socket(AF_INET, SOCK_STREAM, 0);//准备一个socketfd
if(listenfd == -1 )
ERR_EXIT("listen");

int on = 1;
if(setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1)//setsockopt设置端口复用
{
close(listenfd);
ERR_EXIT("setsockopt");
}

struct sockaddr_in seraddr;
seraddr.sin_family = AF_INET;
seraddr.sin_port = htons(8888);
seraddr.sin_addr.s_addr = htonl(INADDR_ANY);
socklen_t len = sizeof(seraddr);
if(bind(listenfd, (struct sockaddr*)&seraddr, len) == -1)//监听socket端口,
{
close(listenfd);
ERR_EXIT("bind");
}

if(listen(listenfd, 6) == -1)
{
close(listenfd);
ERR_EXIT("listen");
}
return listenfd;
}


3.select服务器模型:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <signal.h>
#include <sys/select.h>
#define MAXLEN 1024

#define ERR_EXIT(m) \
do { \
perror(m);\
exit(EXIT_FAILURE);\
}while(0)

int listenf();//封装了网络socket的socekt,bind,listen函数,返回监听的socket描述符
void handle(int signum)//SIGPIPE(子进程结束)的信号的处理
{
printf("hello\n");
}
static void do_select(int);

int main(int argc, const char *argv[])
{
if(signal(SIGPIPE, handle) == SIG_ERR)
ERR_EXIT("signal");
int listenfd = listenf();
do_select(listenfd);
close(listenfd);
return 0;
}

int listenf()
{
int listenfd = socket(AF_INET, SOCK_STREAM, 0);//准备一个socketfd
if(listenfd == -1 )
ERR_EXIT("listen");

int on = 1;
if(setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1)//setsockopt设置端口复用
{
close(listenfd);
ERR_EXIT("setsockopt");
}

struct sockaddr_in seraddr;
seraddr.sin_family = AF_INET;
seraddr.sin_port = htons(8888);
seraddr.sin_addr.s_addr = htonl(INADDR_ANY);
socklen_t len = sizeof(seraddr);
if(bind(listenfd, (struct sockaddr*)&seraddr, len) == -1)//监听socket端口,
{
close(listenfd);
ERR_EXIT("bind");
}

if(listen(listenfd, 6) == -1)
{
close(listenfd);
ERR_EXIT("listen");
}
return listenfd;
}

void do_select(int fd)
{
//struct sockaddr_in cliaddr;
//memset(&cliaddr, 0, sizeof(cliaddr));//此处不需要客户端的地址信息
fd_set set;
fd_set rset;
FD_ZERO(&rset);
FD_SET(fd, &rset);
int nready;
int fd_set[MAXLEN];

int i;
for(i = 0;i < MAXLEN; ++i)
fd_set[i] = -1;
fd_set[0] = fd;

int maxi = fd;//初始最大轮询fd是监听的fd
int  arrlen = 1;//表示数组长度

char recvbuf[1024] = {0};
while(1)
{
set = rset;
nready = select(maxi+1, &set, NULL, NULL, NULL);
if(nready == -1)
{
ERR_EXIT("select");
}
if(FD_ISSET(fd, &set))//查看书否有新的客户端请求
{
int clifd = accept(fd, NULL, NULL);
if(clifd == -1)
ERR_EXIT("accept");
for(i = 1; i < MAXLEN; ++i)
{ if(fd_set[i] == -1)
{
fd_set[i] = clifd;
break;
}
}
FD_SET(clifd, &rset);
if(clifd > maxi)
maxi = clifd;
arrlen++;
--nready;
}

for(i = 1; i < arrlen; ++i)//轮询数据连接
{
int set_fd = fd_set[i];
if(FD_ISSET(set_fd, &set))
{
int n = read(set_fd, recvbuf, 1024);
if(n == -1)
ERR_EXIT("read");
else if(n == 0)//客户端退出,
{
FD_CLR(set_fd, &rset);
close(set_fd);
fd_set[i] = -1;
arrlen--;
}
write(set_fd, recvbuf, strlen(recvbuf));
memset(recvbuf, 0, 1024);
if(--nready < 0)
break;
}
}
}
}


4

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <signal.h>
#include <poll.h>
#define MAXLEN 1024

#define ERR_EXIT(m) \
do { \
perror(m);\
exit(EXIT_FAILURE);\
}while(0)

static void do_service(int);
int listenf();
void handle(int signum)
{
printf("hello\n");
}
static void do_poll(int);

int main(int argc, const char *argv[])
{
if(signal(SIGPIPE, handle) == SIG_ERR)
ERR_EXIT("signal");
int listenfd = listenf();
do_poll(listenfd);
close(listenfd);
return 0;
}

int listenf()
{
int listenfd = socket(AF_INET, SOCK_STREAM, 0);//准备一个socketfd
if(listenfd == -1 )
ERR_EXIT("listen");

int on = 1;
if(setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1)//setsockopt设置端口复用
{
close(listenfd);
ERR_EXIT("setsockopt");
}

struct sockaddr_in seraddr;
seraddr.sin_family = AF_INET;
seraddr.sin_port = htons(8888);
seraddr.sin_addr.s_addr = htonl(INADDR_ANY);
socklen_t len = sizeof(seraddr);
if(bind(listenfd, (struct sockaddr*)&seraddr, len) == -1)//监听socket端口,
{
close(listenfd);
ERR_EXIT("bind");
}

if(listen(listenfd, 6) == -1)
{
close(listenfd);
ERR_EXIT("listen");
}
return listenfd;
}

void do_poll(int fd)
{
struct pollfd pfd[MAXLEN];//设置的最大连接数,存放结构体信息
char recvbuf[1024] = {0};
int i;
for(i = 0; i < MAXLEN; ++i)
pfd[i].fd = -1;

pfd[0].fd = fd;
pfd[0].events = POLLIN;//将监听连接的描述符防止数组开头

int nready;
int maxi = 0;
while(1)
{
nready = poll(pfd, maxi+1, -1);//轮询的是有效数组长度,不能少1
if(nready == -1)
{
ERR_EXIT("select");
}

if(pfd[0].revents & POLLIN)//有客户端请求连接
{
int clifd = accept(fd, NULL, NULL);
if(clifd == -1)
ERR_EXIT("accept");
for(i = 1; i < MAXLEN; ++i)
{
if(pfd[i].fd == -1)
{
pfd[i].fd = clifd;
pfd[i].events = POLLIN;
break;
}
}
if(i > maxi)
maxi = i;
--nready;
}

for(i = 1; i <= maxi; ++i)
{
if(pfd[i].fd == -1)
continue;

if( pfd[i].revents & POLLIN)
{
int n = read(pfd[i].fd, recvbuf, 1024);
if(n == -1)
ERR_EXIT("read");
else if(n == 0)//客户端退出,则从集合中清除
{
printf("%d close\n", pfd[i].fd);
close(pfd[i].fd);
pfd[i].fd = -1;
continue;
}
write(pfd[i].fd, recvbuf, strlen(recvbuf));
memset(recvbuf, 0, 1024);

if(--nready < 0)
break;
}
}
}
}


5.epoll服务器模型:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <signal.h>
#include <sys/epoll.h>
#define MAXLEN 1024

#define ERR_EXIT(m) \
do { \
perror(m);\
exit(EXIT_FAILURE);\
}while(0)

int listenf();
static void do_epoll(int);

int main(int argc, const char *argv[])
{
int listenfd = listenf();
do_epoll(listenfd);
close(listenfd);
return 0;
}

int listenf()
{
int listenfd = socket(AF_INET, SOCK_STREAM, 0);//准备一个socketfd
if(listenfd == -1 )
ERR_EXIT("listen");

int on = 1;
if(setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1)//setsockopt设置端口复用
{
close(listenfd);
ERR_EXIT("setsockopt");
}

struct sockaddr_in seraddr;
seraddr.sin_family = AF_INET;
seraddr.sin_port = htons(8888);
seraddr.sin_addr.s_addr = htonl(INADDR_ANY);
socklen_t len = sizeof(seraddr);
if(bind(listenfd, (struct sockaddr*)&seraddr, len) == -1)//监听socket端口,
{
close(listenfd);
ERR_EXIT("bind");
}

if(listen(listenfd, 6) == -1)
{
close(listenfd);
ERR_EXIT("listen");
}
return listenfd;
}

void do_epoll(int fd)
{
char recvbuf[MAXLEN] = {0};
int epollfd = epoll_create(2048);//设置的最大连接数
if(epollfd == -1)
ERR_EXIT("epoll_create");

struct epoll_event ev;
ev.data.fd = fd;
ev.events = EPOLLIN;
if(epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &ev) == -1)//加入轮询
ERR_EXIT("epoll_ctl_add");

struct epoll_event events[2048];//数组在epoll_wait返回结果的时候使用
int ret;
int i;//在下面while的for循环中遍历使用
int rfd;
int clientfd;
int nread;
while(1)
{
ret = epoll_wait(epollfd, events, 2048, -1);
if(ret == -1)
ERR_EXIT("epoll_wait");

for(i = 0; i < ret; ++i )
{
rfd = events[i].data.fd;
if(rfd == fd)
{
if((clientfd = accept(fd, NULL, NULL)) == -1)
ERR_EXIT("accept");
ev.data.fd = clientfd;
ev.events = EPOLLIN;
if(epoll_ctl(epollfd, EPOLL_CTL_ADD, clientfd, &ev) == -1)
ERR_EXIT("epoll_ctl");
}else
{
int nread = read(rfd, recvbuf, MAXLEN);
if(nread == -1)
{
if(errno == EINTR)
continue;
ERR_EXIT("read");
}else if( nread == 0)//客户端退出,从epoll轮询中删除
{
printf("%d fd close\n", rfd);
ev.data.fd = rfd;
ev.events = EPOLLIN;
if(epoll_ctl(epollfd, EPOLL_CTL_DEL, rfd, &ev) == -1)
ERR_EXIT("epoll_ctl");
}else
{
if(write(rfd, recvbuf, strlen(recvbuf)) == -1)
ERR_EXIT("write");
memset(recvbuf, 0, MAXLEN);
}
}
}
}
close(epollfd);
}


.poll服务器模型:
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐