apr-tutorial-13.html
来自「跨平台windowsunixlinux的c语言编程解决方案」· HTML 代码 · 共 442 行 · 第 1/3 页
HTML
442 行
...SNIP.../* non-blocking socket. We can't expect that @ns inherits non-blocking mode from @lsock */apr_socket_opt_set(ns, APR_SO_NONBLOCK, 1);apr_socket_timeout_set(ns, 0);</PRE></CODE></BLOCKQUOTE></P><H3>non-blocking apr_socket_connect()</H3><P>apr_socket_connect() is a bit different from other APIs on blocking/non-blocking mode. It has three modes, blocking-with-system-timeout, blocking-with-timeout, and non-blocking. Unlike other APIs, apr_socket_connect() never blocks forever. The default mode is blocking-with-system-timeout. The timeout value depends on OS, and it is relatively longer, e.g. over one minute. In my opinion, it is not good to use blocking-with-system-timeout mode for real applications, because it is uncontrollable. We have to set either blocking-with-timeout or non-blocking to the mode.</P><P>To make blocking-with-timeout sockets, we have to set it to 'APR_SO_NONBLOCK==1(on) and timeout>0'. As you see, this is not same as above. On Unix, we have no problem to specify 'APR_SO_NONBLOCK==0(off) and timeout>0'. Unfortunatelly, we have a problem on Windows. Setting the mode to 'APR_SO_NONBLOCK==0(off) and timeout>0' causes blocking-with-system-timeout sockets on Windows.</P><P>Conclusion:If we want blocking-with-timeout socket without portability issues, we should write code as follows:</P><P><BLOCKQUOTE><CODE><PRE>/* pseudo code: blocking-with-timeout apr_socket_connect() */apr_socket_opt_set(sock, APR_SO_NONBLOCK, 1);apr_socket_timeout_set(sock, positive_timeout);apr_socket_connect(sock, sa);/* still blocking-with-timeout for other operations, apr_socket_send()/apr_socket_recv() */apr_socket_opt_set(sock, APR_SO_NONBLOCK, 0);apr_socket_timeout_set(sock, positive_timeout);apr_socket_send(sock, ...);apr_socket_recv(sock, ...);</PRE></CODE></BLOCKQUOTE></P><P>Blocking-with-timeout apr_socket_connect() returns APR_SUCCESS if the connection has been established successfully. Otherwise, it returns an error value. For example, APR_TIMEUP, APR_ECONNREFUSED, or APR_EHOSTUNREACH. If error value is APR_ECONNREFUSED, the server process's listen(2) backlog is beyond the limit. Please see apr_socket_listen() description above.</P><P>For mulplexing model programming, we need non-blocking sockets with apr_socket_connect(). To make non-blocking socket for apr_socket_connect(), set it to 'APR_SO_NONBLOCK==1(on) and timeout==0' as usual. Non-blocking apr_socket_connect() returns APR_EINPROGRESS unless the connection is established. In general, apr_socket_connect() can't establish the connection immediately. Accordingly, non-blocking apr_socket_connect() usually returns APR_EINPROGRESS except that the connection is established between local processes, which run on the same host. In that case, apr_socket_connect() could return APR_SUCCESS even if it is non-blocking. For multiplex processing, we want to know when the connection is established. If we know that, we can call apr_socket_connect() and expect it to return APR_SUCCESS. Unfortunatelly, there is a portability issue on checking readiness to call apr_socket_connect().</P><P>My suggestion is following logic. I assume that most of applications will send some bytes after apr_socket_connect() has established the connection.</P><P><BLOCKQUOTE><CODE><PRE>/* pseudo code: check readiness for non-blocking apr_socket_connect() by apr_pollset_poll() *//* XXX this is not the best way, but works on almost all platforms *//* set non-blocking to the socket */apr_socket_opt_set(sock, APR_SO_NONBLOCK, 1);apr_socket_timeout_set(sock, 0);rv = apr_socket_connect(sock, sa); /* this returns immediately */if (rv == APR_SUCCESS) { /* special case: the server might run on the same host */ /* ready to write */} else if (APR_STATUS_IS_EINPROGRESS(rv)) { /* usual case */ /* add the socket to pollset to check APR_POLLOUT(writable) */ pfd.reqevents = APR_POLLOUT; apr_pollset_add(pollset, &pfd); /* we should specify timeout to apr_pollset_poll(). */ apr_pollset_poll(pollset, positive_timeout, &num, &ret_pfd); /* go to [1] or [2] based on pollset's results */}/* [1] when the socket is ready to write */call apr_socket_send(). /* you don't need to call apr_socket_connect(), but you can call it (no side-effects) *//* [2] when the socket is not ready to write by the timeout * we guess the socket can't establish the connection */apr_socket_close(sock);</PRE></CODE></BLOCKQUOTE></P><H3>Detection of the remote host closed the socket</H3><P>Detecting whether socket is unavailable is important for real applications. In general, there are three scenarios when we lost TCP session. The remote process has closed socket, the remote process has crashed(terminated), or the remote host has crashed. Except the last case, we maybe receive FIN packet and we know we've lost out TCP session. From API perspective, we can know it by that apr_socket_recv() returns APR_EOF. The following example shows how to detect whether the remote process has closed the session.</P><P><BLOCKQUOTE><CODE><PRE>/* pseudo code to detect we lost TCP session, i.e. the remote process has closed the socket or process terminated */apr_status_t rv;rv = apr_socket_recv(sock, ..., &len); /* we assume @sock is blocking */if (APR_STATUS_IS_TIMEUP(rv)) { /* we might be here, if @sock is blocking-with-timeout */} else if (APR_STATUS_IS_EOF(rv) || len == 0) { /* we lost TCP session. * XXX On Windows, rv would equal to APR_SUCCESS and len==0 in this case. So, we should check @len in addition to APR_EOF check */}</PRE></CODE></BLOCKQUOTE></P><P>When we use non-blocking socket, we can know we lost TCP session by that socket is readable and apr_socket_recv() returns APR_EOF.</P><P><BLOCKQUOTE><CODE><PRE>/* pseudo code to detect we lost TCP session. We use non-blocking socket */apr_status_t rv;rv = apr_pollset_poll(pollset, DEF_POLL_TIMEOUT, &num, &ret_pfd);if (rv == APR_SUCCESS) { for (int i = 0; i < num; i++) { if (ret_pfd[i].rtnevents & APR_POLLIN) { rv = apr_socket_recv(ret_pfd[i].desc.s, ..., &len); if (APR_STATUS_IS_EAGAIN(rv)) { /* we have no data to read. we should keep polling the socket */ } else if (APR_STATUS_IS_EOF(rv) || len == 0) { /* we lost TCP session. * XXX On Windows, rv would equal to APR_SUCCESS and len==0 in this case. So, we should check @len in addition to APR_EOF check */ } else { /* we got data */ } } }}</PRE></CODE></BLOCKQUOTE></P><H3>IP address from hostname</H3><P>We call apr_sockaddr_info_get() with the first argument, a target hostname, and we can get apr_sockaddr_t objet. Then, we call apr_sockaddr_ip_get() with the apr_sockaddr_t object. The following prototype declaration is excerpted from apr_network_io.h.</P><P>/* excerpted from apr_network_io.h */<BLOCKQUOTE><CODE><PRE>APR_DECLARE(apr_status_t) apr_sockaddr_ip_get(char **addr, apr_sockaddr_t *sockaddr);</PRE></CODE></BLOCKQUOTE></P><H3>Hostname from IP address</H3><P>We call apr_sockaddr_info_get() with the first argument, a target IP address, and we can get apr_sockaddr_t object. Then, we call apr_getnameinfo() with the apr_sockaddr_t object. Note that apr_sockaddr_t::hostname will be implicitly modified by apr_getnameinfo().</P><P>/* excerpted from apr_network_io.h */<BLOCKQUOTE><CODE><PRE>APR_DECLARE(apr_status_t) apr_getnameinfo(char **hostname, apr_sockaddr_t *sa, apr_int32_t flags);</PRE></CODE></BLOCKQUOTE></P><HR><A HREF="apr-tutorial-14.html">Next</A><A HREF="apr-tutorial-12.html">Previous</A><A HREF="apr-tutorial.html#toc13">Contents</A></BODY></HTML>
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?