c++ linux服务器4种模型

文章通过六个测试案例,从一对一处理连接到使用线程、select、poll以及epoll来演示在Linux下如何处理多个客户端连接。测试显示,一对一处理只能处理单个连接,线程池适合少量连接但受限于内存,select和poll能处理更多连接但有局限,而epoll则更高效地解决了大量连接的问题。
摘要由CSDN通过智能技术生成

一 一客户一线程
二 select
三 poll
四 epoll
代码如下:

#include <errno.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>

#include <sys/poll.h>
#include <sys/epoll.h>


#include <pthread.h>
 
#define MAXLNE  4096

#define POLL_SIZE	1024

//8m * 4G = 128 , 512
//C10k
void *client_routine(void *arg) { //

	int connfd = *(int *)arg;

	char buff[MAXLNE];

	while (1) {

        int n = recv(connfd, buff, MAXLNE, 0);
        if (n > 0) {
            buff[n] = '\0';
            printf("recv msg from client: %s\n", buff);

            send(connfd, buff, n, 0);
        } else if (n == 0) {
            close(connfd);
            break;
        }

	}

	return NULL;
}


int main(int argc, char **argv) 
{
    int listenfd, connfd, n;
    struct sockaddr_in servaddr;
    char buff[MAXLNE];
 
    if ((listenfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
        printf("create socket error: %s(errno: %d)\n", strerror(errno), errno);
        return 0;
    }
 
    memset(&servaddr, 0, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
    servaddr.sin_port = htons(8080);
 
    if (bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr)) == -1) {
        printf("bind socket error: %s(errno: %d)\n", strerror(errno), errno);
        return 0;
    }
 
    if (listen(listenfd, 10) == -1) {
        printf("listen socket error: %s(errno: %d)\n", strerror(errno), errno);
        return 0;
    }
 #if 0
 	//test1
    struct sockaddr_in client;
    socklen_t len = sizeof(client);
    if ((connfd = accept(listenfd, (struct sockaddr *)&client, &len)) == -1) {
        printf("accept socket error: %s(errno: %d)\n", strerror(errno), errno);
        return 0;
    }

    printf("========waiting for client's request========\n");
    while (1) {

        n = recv(connfd, buff, MAXLNE, 0);
        if (n > 0) {
            buff[n] = '\0';
            printf("recv msg from client: %s\n", buff);

	    	send(connfd, buff, n, 0);
        } else if (n == 0) {
            close(connfd);
        }
        
        //close(connfd);
    }

#elif 0

	//test2
    printf("========waiting for client's request========\n");
    while (1) {
		struct sockaddr_in client;
	    socklen_t len = sizeof(client);
	    if ((connfd = accept(listenfd, (struct sockaddr *)&client, &len)) == -1) {
	        printf("accept socket error: %s(errno: %d)\n", strerror(errno), errno);
	        return 0;
	    }

        n = recv(connfd, buff, MAXLNE, 0);
        if (n > 0) {
            buff[n] = '\0';
            printf("recv msg from client: %s\n", buff);

	    	send(connfd, buff, n, 0);
        } else if (n == 0) {
            close(connfd);
        }
        
        //close(connfd);
    }

#elif 0
	//test3
	while (1) {

		struct sockaddr_in client;
	    socklen_t len = sizeof(client);
	    if ((connfd = accept(listenfd, (struct sockaddr *)&client, &len)) == -1) {//三次握手后,任意取一个节点
	        printf("accept socket error: %s(errno: %d)\n", strerror(errno), errno);
	        return 0;
	    }

		pthread_t threadid;
		pthread_create(&threadid, NULL, client_routine, (void*)&connfd);

    }

#elif 0

	// test4
	fd_set rfds, rset, wfds, wset;

        FD_ZERO(&rfds);//clear
        FD_SET(listenfd, &rfds);//设置rfds区监控listenfd

	FD_ZERO(&wfds);

	int max_fd = listenfd;//z砸门循环遍历的时候最大的一个fd

	while (1) {

		rset = rfds;
		wset = wfds;
        int nready/*总共数量*/ = select(max_fd+1/*最大监听bit位,用于遍历*/,\
                                    &rset/*监控是否可读*/, &wset/*监控是否可写*/,\
                                    NULL, NULL/*设置超时时间,多久时间遍历一次,设置NULL,代表阻塞,当没有监控到要读或写的客户端那么一直阻塞等待*/);
		if (FD_ISSET(listenfd, &rset)) { //用于区分哪个需要读

			struct sockaddr_in client;
      		socklen_t len = sizeof(client);
      		if ((connfd = accept(listenfd, (struct sockaddr *)&client, &len)) == -1) {
      			 printf("accept socket error: %s(errno: %d)\n", strerror(errno), errno);
      		 	return 0;
      		}
      		FD_SET(connfd, &rfds);
	    	if (connfd > max_fd) max_fd = connfd;
      		if (--nready == 0) continue;
		}

		int i = 0;
		for (i = listenfd+1/*listenfd 除外,依次后面增加,012位,为标准输入输出错误位*/;i <= max_fd;i ++) {

			if (FD_ISSET(i, &rset)) { // 判断对应读集合,对应连接fd

				n = recv(i, buff, MAXLNE, 0);
		        if (n > 0) {
		            buff[n] = '\0';
		            printf("recv msg from client: %s\n", buff);

					FD_SET(i, &wfds);

					//reactor
					//send(i, buff, n, 0);
		        } else if (n == 0) { 

					FD_CLR(i, &rfds);
					//printf("disconnect\n");
		            close(i);
					
		        }
				if (--nready == 0) break;
			} else if (FD_ISSET(i, &wset)) {// 判断对应写集合,对应连接fd

				send(i, buff, n, 0);
				FD_SET(i, &rfds);
			
			}

		}
		

	}

#elif 0

	//test5
	struct pollfd fds[POLL_SIZE] = {0};
	fds[0].fd = listenfd;
	fds[0].events = POLLIN;

        int max_fd = listenfd;
	int i = 0;
	for (i = 1;i < POLL_SIZE;i ++) {
		fds[i].fd = -1;
	}

	while (1) {

		int nready = poll(fds, max_fd+1, -1);

	
		if (fds[0].revents & POLLIN) {

                    struct sockaddr_in client;
		    socklen_t len = sizeof(client);
		    if ((connfd = accept(listenfd, (struct sockaddr *)&client, &len)) == -1) {
		        printf("accept socket error: %s(errno: %d)\n", strerror(errno), errno);
		        return 0;
		    }

                    printf("accept \n");
                    fds[connfd].fd = connfd;
                    fds[connfd].events = POLLIN;

                    if (connfd > max_fd) max_fd = connfd;

                    if (--nready == 0) continue;
		}

		//int i = 0;
		for (i = listenfd+1;i <= max_fd;i ++)  {

                    if (fds[i].revents & POLLIN) {

                        n = recv(i, buff, MAXLNE, 0);
                        if (n > 0) {
                            buff[n] = '\0';
                            printf("recv msg from client: %s\n", buff);

                                        send(i, buff, n, 0);
                        } else if (n == 0) { //

                                    fds[i].fd = -1;

                        close(i);
					
		        }
				if (--nready == 0) break;

			}

		}

	}

#else
	//test6
	//poll/select --> 
	// epoll_create 
	// epoll_ctl(ADD, DEL, MOD)
	// epoll_wait

	int epfd = epoll_create(1); //int size

    struct epoll_event events[POLL_SIZE] = {0};//快递员收快递
	struct epoll_event ev;

	ev.events = EPOLLIN;
	ev.data.fd = listenfd;

	epoll_ctl(epfd, EPOLL_CTL_ADD, listenfd, &ev);

	while (1) {
            int nready = epoll_wait(epfd, events, POLL_SIZE, 5/*需要大于1 为了和以前做兼容*/);
            if (nready == -1) {
                    continue;
            }
            int i = 0;
            for (i = 0;i < nready;i ++) {

                int clientfd =  events[i].data.fd;
                if (clientfd == listenfd) {
                    struct sockaddr_in client;
                    socklen_t len = sizeof(client);
                    if ((connfd = accept(listenfd, (struct sockaddr *)&client, &len)) == -1) {
                        printf("accept socket error: %s(errno: %d)\n", strerror(errno), errno);
                        return 0;
                    }
                    printf("accept\n");
                    ev.events = EPOLLIN;
                    ev.data.fd = connfd;
                    epoll_ctl(epfd, EPOLL_CTL_ADD, connfd, &ev);
                } else if (events[i].events & EPOLLIN) {
                    n = recv(clientfd, buff, MAXLNE, 0);
                    if (n > 0) {
                        buff[n] = '\0';
                        printf("recv msg from client: %s\n", buff);
                        send(clientfd, buff, n, 0);
                    } else if (n == 0) { //
                        ev.events = EPOLLIN;
                        ev.data.fd = clientfd;
                        epoll_ctl(epfd, EPOLL_CTL_DEL, clientfd, &ev);
                        close(clientfd);
                    }
                }
            }
	}

#endif
 
    close(listenfd);
    return 0;
}


分析

test1

一对一,问题 只能对第一个连接有效,第二个连接能连接成功但是操作不了
主要原因是由于accept函数被占用阻塞

test2

1对多,问题对每个客户端都能发送消息,但是只能发送一次
原因:每一次循环阻塞再accept,每一次连接后 服务器处理不了,没有保存当前连接的客户端

test3

每个客户端分配一个线程,这个在客户端不多的时候,还可以,完全可行,但是大量就不行了,这是由于一个线程标准为8M栈空间,大约1G的内存能分配128个,这是还没有算内核,其他应用在用的控件。可以测试 客户端慢慢增加,看看该服务器电脑内存是否有变化
优点:逻辑简单
缺点:不适合大量连接
很难突破10000个线程

test4

使用select函数:
当我们在有大量连接的时候没办法保证哪一个客户端需要数据,那么砸门需要一个组件准确知道哪个客户端有数据要处理,有消息再让服务器处理
1个select ,1024fd ,多做几个select,可以突破10000万个连接的限制,很难突破100 0000 个连接,原因是她需要大量copy fd,去进行读写操作

test5

pool
找一个pool去管理fd,有读写的时候再计入其中

test6

epoll

还有很多需要补充。。。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

老了希望

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值