PostgreSQL 中 libpq 相关代码解析之建立链接 (二)

上回说到 conn->status 被置为 CONNECTION_NEEDED,调用 PQconnectPoll() 函数实现链接的建立,本文将详细讲述一下 PQconnectdb() 流程下该函数的实现。

概览

CONNECTION_NEEDED 状态下,程序会依次从 host 列表中取出地址并尝试建立链接,若失败则取下一个 host 直至全部失败而异常退出,或者建立好了链接。

创建 socket

conn->sock = socket(addr_cur->ai_family, SOCK_STREAM, 0);

创建 TCP socket 存入 conn->sock,若失败则取下一个 host 尝试,若成功则继续。

设置 socket 选项

no delay of outgoing data for TCP sockets

设置 conn->sock 的 TCP 层的 TCP_NODELAY 属性为 1。

setsockopt(conn->sock, IPPROTO_TCP, TCP_NODELAY, (char *) &on, sizeof(on));

nonblock mode

首先取到 conn->sock 的当前状态,然后或操作设置非阻塞模式。

flags = fcntl(sock, F_GETFL);
fcntl(sock, F_SETFL, (flags | O_NONBLOCK));

close-on-exec

在 exec 新程序时,自动关闭已经打开的 socket。

fcntl(conn->sock, F_SETFD, FD_CLOEXEC);

keepalive

conn->keepalives 的默认值为 NULL,默认使用 keep alive 属性,该值只能置空或设为0、1。

setsockopt(conn->sock, SOL_SOCKET, SO_KEEPALIVE, (char *) &on, sizeof(on));

keepalive idle timer

conn->keepalives_idle 的默认值为 NULL,默认不设置该值。

conn->keepalives_idle 不为空且不为负数,则设置该属性。

setsockopt(conn->sock, IPPROTO_TCP, PG_TCP_KEEPALIVE_IDLE, (char *) &idle, sizeof(idle));

keepalive interval

conn->keepalives_interval 的默认值为 NULL,默认不设置该值。

conn->keepalives_interval 不为空且不为负数,则设置该属性。

setsockopt(conn->sock, IPPROTO_TCP, TCP_KEEPINTVL, (char *) &interval, sizeof(interval));

the count of lost keepalive packets that will trigger a connection break

设置 keepalive 包丢失几次会触发链接断开。

conn->keepalives_count 的默认值为 NULL,默认不设置该值。

conn->keepalives_count 不为空且不为负数,则设置该属性。

setsockopt(conn->sock, IPPROTO_TCP, TCP_KEEPCNT, (char *) &count, sizeof(count));

SIGPIPE 信号处理

代码里的注释很详细,摘录出来,简而言之就是优先使用 setsockopt(),次先使用 send 来设置忽略 SIGPIPE 信号,并且有 flag 来跟踪使用了哪种方式。

    /*----------
     * We have three methods of blocking SIGPIPE during
     * send() calls to this socket:
     *
     *  - setsockopt(sock, SO_NOSIGPIPE)
     *  - send(sock, ..., MSG_NOSIGNAL)
     *  - setting the signal mask to SIG_IGN during send()
     *
     * The third method requires three syscalls per send,
     * so we prefer either of the first two, but they are
     * less portable.  The state is tracked in the following
     * members of PGconn:
     *
     * conn->sigpipe_so      - we have set up SO_NOSIGPIPE
     * conn->sigpipe_flag    - we're specifying MSG_NOSIGNAL
     *
     * If we can use SO_NOSIGPIPE, then set sigpipe_so here
     * and we're done.  Otherwise, set sigpipe_flag so that
     * we will try MSG_NOSIGNAL on sends.  If we get an error
     * with MSG_NOSIGNAL, we'll clear that flag and revert to
     * signal masking.
     *----------
     */

设置 SO_NOSIGPIPE:

setsockopt(conn->sock, SOL_SOCKET, SO_NOSIGPIPE, (char *) &optval, sizeof(optval));

建立链接

使用 connect(conn->sock, addr_cur->ai_addr, addr_cur->ai_addrlen) 建立链接,结果可能成功,可能失败。

失败

若失败,errno 会被设值,在以下三种情况下该链接可能有效,还是可以抢救一下的,所以将 conn->status 设置为 CONNECTION_STARTED,以示“等待链接建立”;返回 PGRES_POLLING_WRITING

以下标识的说明摘自 Linux Manual page:

EINPROGRESS

The socket is nonblocking and the connection cannot be completed immediately. It is possible to select(2) or poll(2) for completion by selecting the socket for writing. After select(2) indicates writability, use getsockopt(2) to read the SO_ERROR option at level SOL_SOCKET to determine whether connect() completed successfully (SO_ERROR is zero) or unsuccessfully (SO_ERROR is one of the usual error codes listed here, explaining the reason for the failure).

EWOULDBLOCK

Operation would block.

EINTR

The system call was interrupted by a signal that was caught

成功

conn->status 被置为 CONNECTION_STARTED

检查 socket 状态

若状态不对则 fail。

getsockopt(conn->sock, SOL_SOCKET, SO_ERROR, (char *) &optval, &optlen)

获取本地 IP Port 信息

conn->laddr 保存得到的本地 IP 和 Port。

getsockname(conn->sock, (struct sockaddr *) &conn->laddr.addr, &conn->laddr.salen)

成功返回

conn->status 被置为 CONNECTION_MADE,返回 PGRES_POLLING_WRITING

相关推荐

PostgreSQL 中 libpq 相关代码解析之大纲

PostgreSQL 中 libpq 相关代码解析之建立链接 (一)

PostgreSQL 中 libpq 相关代码解析之建立链接 (二)

PostgreSQL 中 libpq 相关代码解析之建立链接 (三)

PostgreSQL 中 libpq 相关代码解析之流复制(四)

Advertisements

分类:默认目录, PostgreSQL

Tagged as:

4 replies

发表评论

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  更改 )

Google+ photo

You are commenting using your Google+ account. Log Out /  更改 )

Twitter picture

You are commenting using your Twitter account. Log Out /  更改 )

Facebook photo

You are commenting using your Facebook account. Log Out /  更改 )

w

Connecting to %s