apr-tutorial-13.html

来自「跨平台windowsunixlinux的c语言编程解决方案」· HTML 代码 · 共 442 行 · 第 1/3 页

HTML
442
字号
<A HREF="../sample/pollset-sample.c">pollset-sample.c</A> */<BLOCKQUOTE><CODE><PRE>/* 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, &amp;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, &amp;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, &amp;pfd);/* we watch @sock(connected socket) to know whether it is ready to write(APR_POLLOUT) */pfd.reqevents = APR_POLLOUT;apr_pollset_add(pollset, &amp;pfd);</PRE></CODE></BLOCKQUOTE></P><P>We need to look at apr_pollfd_t's definition. It is in apr_poll.h as follows:</P><P>/* excerpted from apr_poll.h */<BLOCKQUOTE><CODE><PRE>/** Union of either an APR file or socket. */typedef union {    apr_file_t *f;              /**&lt; file */    apr_socket_t *s;            /**&lt; socket */} apr_descriptor;/** Poll descriptor set. */struct apr_pollfd_t {    apr_pool_t *p;              /**&lt; associated pool */    apr_datatype_e desc_type;   /**&lt; descriptor type */    apr_int16_t reqevents;      /**&lt; requested events */    apr_int16_t rtnevents;      /**&lt; returned events */    apr_descriptor desc;        /**&lt; @see apr_descriptor */    void *client_data;          /**&lt; allows app to associate context */};</PRE></CODE></BLOCKQUOTE></P><P>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.</P><P><BLOCKQUOTE><CODE><PRE>/* 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, &amp;pfd);</PRE></CODE></BLOCKQUOTE></P><P>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:</P><P>/* excerpted from apr_poll.h */<BLOCKQUOTE><CODE><PRE>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);</PRE></CODE></BLOCKQUOTE></P><P>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.</P><P><BLOCKQUOTE><CODE><PRE>/* 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, &amp;num, &amp;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 &lt; num; i++) {        if (ret_pfd[i].rtnevents &amp; APR_POLLIN) {            printf("socket %p is ready to read\n", ret_pfd[i].desc.s);        } else if (ret_pfd[i].rtnevents &amp; APR_POLLOUT) {            printf("socket %p is ready to write\n", ret_pfd[i].desc.s);        }    }}</PRE></CODE></BLOCKQUOTE></P><H2><A NAME="ss13.4">13.4</A> <A HREF="apr-tutorial.html#toc13.4">real network programming</A></H2><P>We have glanced network APIs in the previous sections. It doesn't seem so difficult. Nevertheless, real network programming is not so easy. In particular, it is hard to make your software efficient, secure and robust to various network errors. Here, I'll show you some hints for real programming.</P><H3>blocking vs. non-blocking socket</H3><P>There are mainly three modes with sockets, blocking-forever, blocking-with-timeout, and non-blocking. The mode is controlled by two APIs, apr_socket_opt_set() and apr_socket_timeout_set(). We have a portability issue on this. The table below shows it.</P><P><BR><CENTER><TABLE BORDER><TR><TD>APR_SO_NONBLOCK </TD><TD> timeout value to apr_socket_timeout_set()</TD><TD> mode </TD></TR><TR><TD>off(==0) </TD><TD> t == 0 </TD><TD> non-blocking </TD></TR><TR><TD>off(==0) </TD><TD> t &lt; 0 </TD><TD> blocking-forever </TD></TR><TR><TD>off(==0) </TD><TD> t > 0 </TD><TD> blocking-with-timeout </TD></TR><TR><TD>on(==1) </TD><TD> t == 0 </TD><TD> non-blocking </TD></TR><TR><TD>on(==1) </TD><TD> t &lt; 0 </TD><TD> blocking-forever </TD></TR><TR><TD>on(==1) </TD><TD> t > 0 </TD><TD> blocking-with-timeout</TD></TR></TABLE><CAPTION>Unix</CAPTION></CENTER><BR></P><P><BR><CENTER><TABLE BORDER><TR><TD>APR_SO_NONBLOCK </TD><TD> timeout value to apr_socket_timeout_set()</TD><TD> mode </TD></TR><TR><TD>off(==0) </TD><TD> t == 0 </TD><TD> blocking-forever</TD></TR><TR><TD>off(==0) </TD><TD> t &lt; 0 </TD><TD> blocking-forever</TD></TR><TR><TD>off(==0) </TD><TD> t > 0 </TD><TD> blocking-with-timeout </TD></TR><TR><TD>on(==1) </TD><TD> t == 0 </TD><TD> non-blocking</TD></TR><TR><TD>on(==1) </TD><TD> t &lt; 0 </TD><TD> non-blocking</TD></TR><TR><TD>on(==1) </TD><TD> t > 0 </TD><TD> non-blocking</TD></TR></TABLE><CAPTION>Windows</CAPTION></CENTER><BR></P><P>The default mode is APR_SO_NONBLOCK==0(off) and APR_SO_TIMEOUT==-1. Namely, default socket is blocking-forever on both Unix and Windows.</P><P>Conclusion (my recommendation):[a] When you want a non-blocking socket, set it to 'APR_SO_NONBLOCK==1(on) and timeout==0'.</P><P>[b] When you want a blocking-with-timeout socket, set it to 'APR_SO_NONBLOCK==0(off) and timeout>0'. Note that you must keep the order of calling the APIs. You must call apr_socket_opt_set(sock, APR_SO_NONBLOCK, 1) and then call apr_socket_timeout_set(sock, timeout). Otherwise, on Unix the socket becomes blocking-forever.</P><P>[c] When you want a blocking-forever socket, set it to 'APR_SO_NONBLOCK==0(off) and timeout&lt;0'. In my opinion, we merely need blocking-forever sockets for real application.</P><H3>non-blocking apr_socket_accept()</H3><P>We can control blocking/non-blocking mode for apr_socket_recv() and apr_socket_send() as I described above. How about other APIs?</P><P>We can ignore apr_socket_bind() and apr_socket_listen(). Because they never block. Simply, these APIs are always non-blocking mode. We have two more APIs to consider, apr_socket_accept() and apr_socket_connect().</P><P>Here, we consider apr_socket_accept(). It is almost same as apr_socket_recv()/apr_socket_send(). The mode follows the table in the previous section. Unlike apr_socket_recv()/apr_socket_send(), blocking-forever socket is useful for apr_socket_accept(). It is because that the program might be a server process and might have nothing to do until any client connects to. </P><P>If we write a mulplexing model code, we need non-blocking socket for listening socket. We check listening socket whether it is ready to read. Readiness to read indicates that any client has connected to the socket. After we know the readiness to read, we just call apr_socket_accept(). Please take a look at <A HREF="../sample/pollset-sample.c">pollset-sample.c</A> about this pattern. Note that you must not assume the newly connected socket, which is returned from apr_socket_accept(), inherits the mode from the listening socket. You should specify the mode for the connected socket explicitly. Please look at the following example.</P><P>/* excerpted from <A HREF="../sample/pollset-sample.c">pollset-sample.c</A>, but I omitted error checks */<BLOCKQUOTE><CODE><PRE>apr_socket_accept(&amp;ns, lsock, mp);

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?