多路处理 apr实现

12.3 多路处理(Multiplex Processing)

12.3 多路处理(Multiplex Processing)

Go back to server-sample.c again. It has a loop to keep calling apr_socket_accept(), so that the server process can accept multiple clients. However, it doesn't call apr_socket_accept() until it completes the session between one client. Obviously, when multiple clients connect to the server simultaneously, the server handles only one client at a moment. Such a server is normally useless. We need concurrent server. In general, there are three programming models to develop concurrent server; multi-process model, multi-threaded model, and multiplexing model. Here, we focus on the final one.

我们再次回到server-sample.c文件,我们在一个循环中持续调用apr_socket_accept(),因此我们的服务器可以处理多个客户端的请求。但是在一个客户端会话结束前不会再次调用apr_socket_accept()。显而易见,当有多个客户端同时请求服务器的时候,服务器在同一时刻只能处理一个客户端请求,像这样的服务器在通常的情况下是毫无意义的。我们需要一个支持并发的服务器。一般来说,开发一个支持并发的服务器有三种模式,多进程模式,多线程模式和多路模式(multiplexing model)。这里我将焦点集中在左后一种模式中。

Historically, multiplexing model relies on select(2) or poll(2) APIs, and the code is often based on event-driven style. The essential idea is that we check multiple sockets whether they are ready to read or write. Then, when we know some of the sockets are ready to read/write, we do actual read/write with them. In addition, we need non-blocking sockets for multiplex processing. I'll describe how to use non-blocking sockets in the next section.

开发多路模式通常依赖于select或poll,并且依赖于事件驱动为基础的代码。基本的思路是我们通过检查这些socket是否可以读写,然后当我们得知有socket可以进行读写操作时,我们在对其进行真正的读写操作。我们需要一个非阻塞式的socket来进行多路处理。我们将在下一段落描述怎样使用非阻塞式socket。

Let's take a look at pollset-sample.c. The creation of the listening socket is almost same as server-sample.c. The different is that we make it non-blocking. Then, we create a pollset obejct by apr_pollset_create(). pollset is a set of sockets to watch. The prototype declaration is as follows:

我们看一下pollset-sample.c的代码。创建监听socket的方式基本和server-sample.c一样,不同的是我们需要使他编程非阻塞式。这时我们需要通过apr_pollset_create()创建一个pollset对象。pollset是一组socket的集合。下面是他的原型声明:

/* excerted from apr_poll.h */
APR_DECLARE(apr_status_t) apr_pollset_create(apr_pollset_t **pollset, apr_uint32_t size, apr_pool_t *p, apr_uint32_t flags);

The first argument is result argument. The second argument is size of pollset. Unfortunately, pollset is not a dynamic-sized set. We have to specify the size of set at creation. The third argument is memory pool to use. The fourth argument is flag, but it's a reserved argument now.

第一个参数是结果参数(result argument)。第二个参数是pollset的大小,不幸的是,pollset的大小不能够动态调节。我们需要在创建pollset的时候指定他的大小。第三个参数是内存池。第四个参数是一个标志位,但是现在只是一个保留参数而已。

After creating pollset object, we can add sockets to the pollset by apr_pollset_add(). In the following example, we add the first and second sockets to the pollset to know whether the socket is ready to read. Then we remove a socket from the pollset because we are not interested in its readability. Finally, we add the third socket to know whether it is ready to write.

创建pollset对象后,我们需要通过apr_pollset_add()将socket加入到pollset中。在下面这个例子中,我们向pollset中添加的第一个和第二个socket用于读取操作,然后我们是从pollset 中删除了一个socket。最后我们添加了一个socket用于写操作。

/* excerpted from pollset-sample.c */

/* we watch @lsock(listening socket) to know whether it is ready to read(APR_POLLIN) */
apr_pollfd_t pfd = { mp, APR_POLL_SOCKET, APR_POLLIN, 0, { NULL }, NULL };
pfd.desc.s = lsock;
apr_pollset_add(pollset, &pfd);

/* we watch @ns(connected socket) to know whether it is ready to read(APR_POLLIN) */
apr_pollfd_t pfd = { mp, APR_POLL_SOCKET, APR_POLLIN, 0, { NULL }, serv_ctx };
pfd.desc.s = ns;
apr_pollset_add(pollset, &pfd);

/* we are not interested in the socket's readability, so remove it from the pollset */
apr_pollfd_t pfd = { serv_ctx->mp, APR_POLL_SOCKET, APR_POLLIN, 0, { NULL }, serv_ctx };
pfd.desc.s = sock;
apr_pollset_remove(pollset, &pfd);

/* we watch @sock(connected socket) to know whether it is ready to write(APR_POLLOUT) */
pfd.reqevents = APR_POLLOUT;
apr_pollset_add(pollset, &pfd);

We need to look at apr_pollfd_t's definition. It is in apr_poll.h as follows:

我们需要看一下apr_pollfd_t的定义,他在apr_poll.h中:

/* excerpted from apr_poll.h */

/** Union of either an APR file or socket. */
typedef union {
    apr_file_t *f;              /**< file */
    apr_socket_t *s;            /**< socket */
} apr_descriptor;

/** Poll descriptor set. */
struct apr_pollfd_t {
    apr_pool_t *p;              /**< associated pool */
    apr_datatype_e desc_type;   /**< descriptor type */
    apr_int16_t reqevents;      /**< requested events */
    apr_int16_t rtnevents;      /**< returned events */
    apr_descriptor desc;        /**< @see apr_descriptor */
    void *client_data;          /**< allows app to associate context */
};

pollset is designed to work for files as same as for sockets, but we ignore files in this document. We set apr_pollfd_t::desc_type to APR_POLL_SOCKET, and we specify socket object as apr_pollfd_t::desc. apr_pollfd_t::reqevents and apr_pollfd_t::rtnevents have a relation, the former is input and the latter is output. The values are bit-wised flag of APR_POLLIN, APR_POLLOUT, etc. The following sample shows it.

pollset设计不光是为了socket还可以是file,但是在这篇文档中我们忽略文件的部分。我们设置apr_pollfd_t::desc_type为APR_POLL_SOCKET,并且我们为apr_pollfd_t::desc指定一个socket对象。apr_pollfd_t::reqevents和apr_pollfd_t::rtnevents为输入标识和输出标识符,他们为别为APR_POLLIN, APR_POLLOUT。请看如下示例:

/* if you want to know whether the socket is ready to read or write, you can specify the bit-wised flag as follows */
apr_pollfd_t pfd = { mp, APR_POLL_SOCKET, APR_POLLIN|APR_POLLOUT, 0, { sock }, app_ctx };
apr_pollset_add(pollset, &pfd);

apr_pollfd_t::rtnevents doesn't have any meaning when the apr_pollfd_t object is used for apr_pollset_add() or apr_pollset_remove(). It does have meaning when the object is returned from apr_pollset_poll(). apr_pollset_poll() prototype declaration is as follows:

apr_pollfd_t对象在被用于apr_pollset_add()或apr_pollset_remove()时,apr_pollfd_t::rtnevents没有任何意义。当apr_pollfd_t由apr_pollset_poll()返回时才有意义。apr_pollset_poll()的原型声明如下:

/* excerpted from apr_poll.h */
APR_DECLARE(apr_status_t) apr_pollset_poll(apr_pollset_t *pollset, apr_interval_time_t timeout, apr_int32_t *num, const apr_pollfd_t **descriptors);

The first argument is that we pass our pollset object. The second argument is timeout value. If the timeout is zero, it implies no timeout. The third and fourth argument are result arguments. By these result arguments, we get the number of active sockets, which are ready to read or write, and we get an array of the active socket objects. The array is apr_pollfd_t's array. We can know whether the sockets are ready to read/write by checking apr_pollfd_t::rtnevents's values. Here is a pseudo code.

第一个参数我们传入pollset对象。第二个参数是超时等待时间。如果它设置为零,意味着没有超时。第三个和第四个参数是结果参数,通过结果参数我们可以得到可以进行读写的socket个数,还可以通过一个数组得到他们。这个数组是一个apr_pollfd_t类型的数组。我们通过检测apr_pollfd_t::rtnevents的值来获得他们是可读还是可写。以下是示例代码:

/* pseudo code to show how apr_pollset_poll() works */
const apr_pollfd_t *ret_pfd; /* returned pollset */
rv = apr_pollset_poll(pollset, DEF_POLL_TIMEOUT, &num, &ret_pfd);
if (rv == APR_SUCCESS) {
    /* num and ret_pfd are result-arguments.
     * ret_pfd is an array of apr_pollfd_t, and the array size is num */
    for (int i = 0; i < num; i++) {
        if (ret_pfd[i].rtnevents & APR_POLLIN) {
            printf("socket %p is ready to read\n", ret_pfd[i].desc.s);
        } else if (ret_pfd[i].rtnevents & APR_POLLOUT) {
            printf("socket %p is ready to write\n", ret_pfd[i].desc.s);
        }
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值