這篇文章給大家分享的是有關(guān)Node.js中網(wǎng)絡(luò)與流的示例分析的內(nèi)容。小編覺得挺實(shí)用的,因此分享給大家做個(gè)參考,一起跟隨小編過來看看吧。

成都創(chuàng)新互聯(lián)專注于企業(yè)全網(wǎng)營銷推廣、網(wǎng)站重做改版、潁州網(wǎng)站定制設(shè)計(jì)、自適應(yīng)品牌網(wǎng)站建設(shè)、H5高端網(wǎng)站建設(shè)、電子商務(wù)商城網(wǎng)站建設(shè)、集團(tuán)公司官網(wǎng)建設(shè)、外貿(mào)網(wǎng)站建設(shè)、高端網(wǎng)站制作、響應(yīng)式網(wǎng)頁設(shè)計(jì)等建站業(yè)務(wù),價(jià)格優(yōu)惠性價(jià)比高,為潁州等各大城市提供網(wǎng)站開發(fā)制作服務(wù)。
涉及的知識點(diǎn)
libuv 中網(wǎng)絡(luò)的實(shí)現(xiàn)
libuv 解決 accept (EMFILE錯誤)
BSD 套接字
SOCKADDR_IN
UNIX 域協(xié)議使用! 在進(jìn)程間傳遞“文件描述符”
libuv 異步使用 BSD 套接字 的例子
libuv 中的網(wǎng)絡(luò)和直接使用 BSD 套接字接口沒有什么不同,有些事情更簡單,都是無阻塞的,但概念都是一樣的。此外,libuv 還提供了一些實(shí)用的函數(shù)來抽象出那些煩人的、重復(fù)的、低級的任務(wù),比如使用BSD套接字結(jié)構(gòu)設(shè)置套接字、DNS查詢以及調(diào)整各種套接字參數(shù)。
int main() {
loop = uv_default_loop();
uv_tcp_t server;
uv_tcp_init(loop, &server);
uv_ip4_addr("0.0.0.0", DEFAULT_PORT, &addr);
uv_tcp_bind(&server, (const struct sockaddr*)&addr, 0);
int r = uv_listen((uv_stream_t*) &server, DEFAULT_BACKLOG, on_new_connection);
if (r) {
fprintf(stderr, "Listen error %s\n", uv_strerror(r));
return 1;
}
return uv_run(loop, UV_RUN_DEFAULT);
}
void on_new_connection(uv_stream_t *server, int status) {
if (status < 0) {
fprintf(stderr, "New connection error %s\n", uv_strerror(status));
// error!
return;
}
uv_tcp_t *client = (uv_tcp_t*) malloc(sizeof(uv_tcp_t));
uv_tcp_init(loop, client);
if (uv_accept(server, (uv_stream_t*) client) == 0) {
uv_read_start((uv_stream_t*) client, alloc_buffer, echo_read);
}這是一個(gè)正常同步使用 BSD 套接字 的例子。
作為參照可以發(fā)現(xiàn)主要有如下幾步
首先調(diào)用 socket() 為通訊創(chuàng)建一個(gè)端點(diǎn),為套接字返回一個(gè)文件描述符。
接著調(diào)用 bind() 為一個(gè)套接字分配地址。當(dāng)使用 socket() 創(chuàng)建套接字后,只賦予其所使用的協(xié)議,并未分配地址。在接受其它主機(jī)的連接前,必須先調(diào)用 bind() 為套接字分配一個(gè)地址。
當(dāng) socket 和一個(gè)地址綁定之后,再調(diào)用 listen() 函數(shù)會開始監(jiān)聽可能的連接請求。
最后調(diào)用 accept, 當(dāng)應(yīng)用程序監(jiān)聽來自其他主機(jī)的面對數(shù)據(jù)流的連接時(shí),通過事件(比如Unix select()系統(tǒng)調(diào)用)通知它。必須用 accept()函數(shù)初始化連接。 accept() 為每個(gè)連接創(chuàng)立新的套接字并從監(jiān)聽隊(duì)列中移除這個(gè)連接。
int main(void)
{
struct sockaddr_in stSockAddr;
int SocketFD = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
if(-1 == SocketFD)
{
perror("can not create socket");
exit(EXIT_FAILURE);
}
memset(&stSockAddr, 0, sizeof(struct sockaddr_in));
stSockAddr.sin_family = AF_INET;
stSockAddr.sin_port = htons(1100);
stSockAddr.sin_addr.s_addr = INADDR_ANY;
if(-1 == bind(SocketFD,(const struct sockaddr *)&stSockAddr, sizeof(struct sockaddr_in)))
{
perror("error bind failed");
close(SocketFD);
exit(EXIT_FAILURE);
}
if(-1 == listen(SocketFD, 10))
{
perror("error listen failed");
close(SocketFD);
exit(EXIT_FAILURE);
}
for(;;)
{
int ConnectFD = accept(SocketFD, NULL, NULL);
if(0 > ConnectFD)
{
perror("error accept failed");
close(SocketFD);
exit(EXIT_FAILURE);
}
/* perform read write operations ... */
shutdown(ConnectFD, SHUT_RDWR);
close(ConnectFD);
}
close(SocketFD);
return 0;
}main > uv_tcp_init
1、對 domain 進(jìn)行了驗(yàn)證, 需要是下面3種的一種
AF_INET 表示 IPv4 網(wǎng)絡(luò)協(xié)議
AF_INET6 表示 IPv6
AF_UNSPEC 表示適用于指定主機(jī)名和服務(wù)名且適合任何協(xié)議族的地址
2、tcp 也是一種流, 調(diào)用 uv__stream_init 對流數(shù)據(jù)進(jìn)行初始化
int uv_tcp_init(uv_loop_t* loop, uv_tcp_t* tcp) {
return uv_tcp_init_ex(loop, tcp, AF_UNSPEC);
}
int uv_tcp_init_ex(uv_loop_t* loop, uv_tcp_t* tcp, unsigned int flags) {
int domain;
/* Use the lower 8 bits for the domain */
domain = flags & 0xFF;
if (domain != AF_INET && domain != AF_INET6 && domain != AF_UNSPEC)
return UV_EINVAL;
if (flags & ~0xFF)
return UV_EINVAL;
uv__stream_init(loop, (uv_stream_t*)tcp, UV_TCP);
...
return 0;
}main > uv_tcp_init > uv__stream_init
流的初始化函數(shù)使用的地方還是特別多的, 也特別重要。下述 i/o 的完整實(shí)現(xiàn)參考 【libuv 源碼學(xué)習(xí)筆記】線程池與i/o
1、對流會被調(diào)用的回調(diào)函數(shù)等進(jìn)行一個(gè)初始化
如 read_cb 函數(shù), 在本例子中 on_new_connection > uv_read_start 函數(shù)就會真實(shí)的設(shè)置該 read_cb 為用戶傳入的參數(shù) echo_read, 其被調(diào)用時(shí)機(jī)是該 stream 上設(shè)置的 io_watcher.fd 有數(shù)據(jù)寫入時(shí), 在事件循環(huán)階段被 epoll 捕獲后。
alloc_cb 函數(shù)的調(diào)用過程同 read_cb, alloc 類型函數(shù)一般是設(shè)置當(dāng)前需要讀取的內(nèi)容長度, 在流數(shù)據(jù)傳輸時(shí)通常首先會寫入本次傳輸數(shù)據(jù)的長度, 然后是具體的內(nèi)容, 主要是為了接收方能夠合理的申請內(nèi)存進(jìn)行存儲。如 grpc, thread-loader 中都有詳細(xì)的應(yīng)用。
close_cb 函數(shù)被調(diào)用在 stream 數(shù)據(jù)結(jié)束時(shí)或者出錯時(shí)。
connection_cb 函數(shù)如本例子 tcp 流, 當(dāng) accept 接收到新連接時(shí)被調(diào)用。本例子中即為 on_new_connection
connect_req 結(jié)構(gòu)主要用于 tcp 客戶端相關(guān)連接回調(diào)等數(shù)據(jù)的掛載使用。
shutdown_req 結(jié)構(gòu)主要用于流 destroy 時(shí)回調(diào)等數(shù)據(jù)的掛載使用。
accepted_fd 當(dāng) accept 接收到新連接時(shí), 存儲 accept(SocketFD, NULL, NULL) 返回的 ConnectFD。
queued_fds 用于保存等待處理的連接, 其主要用于 node cluster 集群 的實(shí)現(xiàn)。
// queued_fds 1. 當(dāng)收到其他進(jìn)程通過 ipc 寫入的數(shù)據(jù)時(shí), 調(diào)用 uv__stream_recv_cmsg 函數(shù) 2. uv__stream_recv_cmsg 函數(shù)讀取到進(jìn)程傳遞過來的 fd 引用, 調(diào)用 uv__stream_queue_fd 函數(shù)保存。 3. queued_fds 被消費(fèi)主要在 src/stream_wrap.cc LibuvStreamWrap::OnUvRead > AcceptHandle 函數(shù)中。
2、其中專門為 loop->emfile_fd 通過 uv__open_cloexec 方法創(chuàng)建一個(gè)指向空文件(/dev/null)的 idlefd 文件描述符, 追蹤發(fā)現(xiàn)原來是解決 accept (EMFILE錯誤), 下面我們講 uv__accept 的時(shí)候再細(xì)說這個(gè) loop->emfile_fd 的妙用。
accept處理連接時(shí),若出現(xiàn) EMFILE 錯誤不進(jìn)行處理,則內(nèi)核間隔性嘗試連接,導(dǎo)致整個(gè)網(wǎng)絡(luò)設(shè)計(jì)程序崩潰
3、調(diào)用 uv__io_init 初始化的該 stream 的 i/o 觀察者的回調(diào)函數(shù)為 uv__stream_io
void uv__stream_init(uv_loop_t* loop,
uv_stream_t* stream,
uv_handle_type type) {
int err;
uv__handle_init(loop, (uv_handle_t*)stream, type);
stream->read_cb = NULL;
stream->alloc_cb = NULL;
stream->close_cb = NULL;
stream->connection_cb = NULL;
stream->connect_req = NULL;
stream->shutdown_req = NULL;
stream->accepted_fd = -1;
stream->queued_fds = NULL;
stream->delayed_error = 0;
QUEUE_INIT(&stream->write_queue);
QUEUE_INIT(&stream->write_completed_queue);
stream->write_queue_size = 0;
if (loop->emfile_fd == -1) {
err = uv__open_cloexec("/dev/null", O_RDONLY);
if (err < 0)
/* In the rare case that "/dev/null" isn't mounted open "/"
* instead.
*/
err = uv__open_cloexec("/", O_RDONLY);
if (err >= 0)
loop->emfile_fd = err;
}
#if defined(__APPLE__)
stream->select = NULL;
#endif /* defined(__APPLE_) */
uv__io_init(&stream->io_watcher, uv__stream_io, -1);
}main > uv_tcp_init > uv__stream_init > uv__open_cloexec
同步調(diào)用 open 方法拿到了 fd, 也許你會問為啥不像 【libuv 源碼學(xué)習(xí)筆記】線程池與i/o 中調(diào)用 uv_fs_open 異步獲取 fd, 其實(shí) libuv 中并不全部是異步的實(shí)現(xiàn), 比如當(dāng)前的例子啟動 tcp 服務(wù)前的一些初始化, 而不是用戶請求過程中發(fā)生的任務(wù), 同步也是能接受的。
int uv__open_cloexec(const char* path, int flags) {
#if defined(O_CLOEXEC)
int fd;
fd = open(path, flags | O_CLOEXEC);
if (fd == -1)
return UV__ERR(errno);
return fd;
#else /* O_CLOEXEC */
int err;
int fd;
fd = open(path, flags);
if (fd == -1)
return UV__ERR(errno);
err = uv__cloexec(fd, 1);
if (err) {
uv__close(fd);
return err;
}
return fd;
#endif /* O_CLOEXEC */
}main > uv_tcp_init > uv__stream_init > uv__stream_io
雙工流的 i/o 觀察者回調(diào)函數(shù), 如調(diào)用的 stream->connect_req 函數(shù), 其值是例子中 uv_listen 函數(shù)的最后一個(gè)參數(shù) on_new_connection。
當(dāng)發(fā)生 POLLIN | POLLERR | POLLHUP 事件時(shí): 該 fd 有可讀數(shù)據(jù)時(shí)調(diào)用 uv__read 函數(shù)
當(dāng)發(fā)生 POLLOUT | POLLERR | POLLHUP 事件時(shí): 該 fd 有可讀數(shù)據(jù)時(shí)調(diào)用 uv__write 函數(shù)
static void uv__stream_io(uv_loop_t* loop, uv__io_t* w, unsigned int events) {
uv_stream_t* stream;
stream = container_of(w, uv_stream_t, io_watcher);
assert(stream->type == UV_TCP ||
stream->type == UV_NAMED_PIPE ||
stream->type == UV_TTY);
assert(!(stream->flags & UV_HANDLE_CLOSING));
if (stream->connect_req) {
uv__stream_connect(stream);
return;
}
assert(uv__stream_fd(stream) >= 0);
if (events & (POLLIN | POLLERR | POLLHUP))
uv__read(stream);
if (uv__stream_fd(stream) == -1)
return; /* read_cb closed stream. */
if ((events & POLLHUP) &&
(stream->flags & UV_HANDLE_READING) &&
(stream->flags & UV_HANDLE_READ_PARTIAL) &&
!(stream->flags & UV_HANDLE_READ_EOF)) {
uv_buf_t buf = { NULL, 0 };
uv__stream_eof(stream, &buf);
}
if (uv__stream_fd(stream) == -1)
return; /* read_cb closed stream. */
if (events & (POLLOUT | POLLERR | POLLHUP)) {
uv__write(stream);
uv__write_callbacks(stream);
/* Write queue drained. */
if (QUEUE_EMPTY(&stream->write_queue))
uv__drain(stream);
}
}main > uv_ip4_addr
uv_ip4_addr 用于將人類可讀的 IP 地址、端口對轉(zhuǎn)換為 BSD 套接字 API 所需的 sockaddr_in 結(jié)構(gòu)。
int uv_ip4_addr(const char* ip, int port, struct sockaddr_in* addr) {
memset(addr, 0, sizeof(*addr));
addr->sin_family = AF_INET;
addr->sin_port = htons(port);
#ifdef SIN6_LEN
addr->sin_len = sizeof(*addr);
#endif
return uv_inet_pton(AF_INET, ip, &(addr->sin_addr.s_addr));
}main > uv_tcp_bind
從 uv_ip4_addr 函數(shù)的實(shí)現(xiàn), 其實(shí)是在 addr 的 sin_family 上面設(shè)置值為 AF_INET, 但在 uv_tcp_bind 函數(shù)里面卻是從 addr 的 sa_family屬性上面取的值, 這讓 c 初學(xué)者的我又陷入了一陣思考 ...
sockaddr_in 和 sockaddr 是并列的結(jié)構(gòu),指向 sockaddr_in 的結(jié)構(gòu)體的指針也可以指向 sockaddr 的結(jié)構(gòu)體,并代替它。也就是說,你可以使用 sockaddr_in 建立你所需要的信息,然后用 memset 函數(shù)初始化就可以了memset((char*)&mysock,0,sizeof(mysock));//初始化
原來是這樣, 這里通過強(qiáng)制指針類型轉(zhuǎn)換 const struct sockaddr* addr 達(dá)到的目的, 函數(shù)的最后調(diào)用了 uv__tcp_bind 函數(shù)。
int uv_tcp_bind(uv_tcp_t* handle,
const struct sockaddr* addr,
unsigned int flags) {
unsigned int addrlen;
if (handle->type != UV_TCP)
return UV_EINVAL;
if (addr->sa_family == AF_INET)
addrlen = sizeof(struct sockaddr_in);
else if (addr->sa_family == AF_INET6)
addrlen = sizeof(struct sockaddr_in6);
else
return UV_EINVAL;
return uv__tcp_bind(handle, addr, addrlen, flags);
}main > uv_tcp_bind > uv__tcp_bind
調(diào)用 maybe_new_socket, 如果當(dāng)前未設(shè)置 socketfd, 則調(diào)用 new_socket 獲取
調(diào)用 setsockopt 用于為指定的套接字設(shè)定一個(gè)特定的套接字選項(xiàng)
調(diào)用 bind 為一個(gè)套接字分配地址。當(dāng)使用socket()創(chuàng)建套接字后,只賦予其所使用的協(xié)議,并未分配地址。
int uv__tcp_bind(uv_tcp_t* tcp,
const struct sockaddr* addr,
unsigned int addrlen,
unsigned int flags) {
int err;
int on;
/* Cannot set IPv6-only mode on non-IPv6 socket. */
if ((flags & UV_TCP_IPV6ONLY) && addr->sa_family != AF_INET6)
return UV_EINVAL;
err = maybe_new_socket(tcp, addr->sa_family, 0);
if (err)
return err;
on = 1;
if (setsockopt(tcp->io_watcher.fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)))
return UV__ERR(errno);
...
errno = 0;
if (bind(tcp->io_watcher.fd, addr, addrlen) && errno != EADDRINUSE) {
if (errno == EAFNOSUPPORT)
return UV_EINVAL;
return UV__ERR(errno);
}
...
}main > uv_tcp_bind > uv__tcp_bind > maybe_new_socket > new_socket
通過 uv__socket 其本質(zhì)調(diào)用 socket 獲取到 sockfd
調(diào)用 uv__stream_open 設(shè)置 stream i/o 觀察的 fd 為步驟1 拿到的 sockfd
static int new_socket(uv_tcp_t* handle, int domain, unsigned long flags) {
struct sockaddr_storage saddr;
socklen_t slen;
int sockfd;
int err;
err = uv__socket(domain, SOCK_STREAM, 0);
if (err < 0)
return err;
sockfd = err;
err = uv__stream_open((uv_stream_t*) handle, sockfd, flags);
...
return 0;
}main > uv_tcp_bind > uv__tcp_bind > maybe_new_socket > new_socket > uv__stream_open
主要用于設(shè)置 stream->io_watcher.fd 為參數(shù)傳入的 fd。
int uv__stream_open(uv_stream_t* stream, int fd, int flags) {
#if defined(__APPLE__)
int enable;
#endif
if (!(stream->io_watcher.fd == -1 || stream->io_watcher.fd == fd))
return UV_EBUSY;
assert(fd >= 0);
stream->flags |= flags;
if (stream->type == UV_TCP) {
if ((stream->flags & UV_HANDLE_TCP_NODELAY) && uv__tcp_nodelay(fd, 1))
return UV__ERR(errno);
/* TODO Use delay the user passed in. */
if ((stream->flags & UV_HANDLE_TCP_KEEPALIVE) &&
uv__tcp_keepalive(fd, 1, 60)) {
return UV__ERR(errno);
}
}
#if defined(__APPLE__)
enable = 1;
if (setsockopt(fd, SOL_SOCKET, SO_OOBINLINE, &enable, sizeof(enable)) &&
errno != ENOTSOCK &&
errno != EINVAL) {
return UV__ERR(errno);
}
#endif
stream->io_watcher.fd = fd;
return 0;
}main > uv_listen
主要調(diào)用了 uv_tcp_listen 函數(shù)。
int uv_listen(uv_stream_t* stream, int backlog, uv_connection_cb cb) {
int err;
err = ERROR_INVALID_PARAMETER;
switch (stream->type) {
case UV_TCP:
err = uv_tcp_listen((uv_tcp_t*)stream, backlog, cb);
break;
case UV_NAMED_PIPE:
err = uv_pipe_listen((uv_pipe_t*)stream, backlog, cb);
break;
default:
assert(0);
}
return uv_translate_sys_error(err);
}main > uv_listen > uv_tcp_listen
調(diào)用 listen 開始監(jiān)聽可能的連接請求
掛載例子中傳入的回調(diào) on_new_connection
暴力改寫 i/o 觀察者的回調(diào), 在上面的 uv__stream_init 函數(shù)中, 通過 uv__io_init 設(shè)置了 i/o 觀察者的回調(diào)為 uv__stream_io, 作為普通的雙工流是適用的, 這里 tcp 流直接通過 tcp->io_watcher.cb = uv__server_io 賦值語句設(shè)置 i/o 觀察者回調(diào)為 uv__server_io
調(diào)用 uv__io_start 注冊 i/o 觀察者, 開始監(jiān)聽工作。
int uv_tcp_listen(uv_tcp_t* tcp, int backlog, uv_connection_cb cb) {
...
if (listen(tcp->io_watcher.fd, backlog))
return UV__ERR(errno);
tcp->connection_cb = cb;
tcp->flags |= UV_HANDLE_BOUND;
/* Start listening for connections. */
tcp->io_watcher.cb = uv__server_io;
uv__io_start(tcp->loop, &tcp->io_watcher, POLLIN);
return 0;
}main > uv_listen > uv_tcp_listen > uv__server_io
tcp 流的 i/o 觀察者回調(diào)函數(shù)
調(diào)用 uv__accept, 拿到該連接的 ConnectFD
此時(shí)如果出現(xiàn)了上面 uv__stream_init 時(shí)說的 accept (EMFILE錯誤), 則調(diào)用 uv__emfile_trick 函數(shù)
把步驟1拿到的 ConnectFD 掛載在了 stream->accepted_fd 上面
調(diào)用例子中傳入的回調(diào) on_new_connection
void uv__server_io(uv_loop_t* loop, uv__io_t* w, unsigned int events) {
...
while (uv__stream_fd(stream) != -1) {
assert(stream->accepted_fd == -1);
err = uv__accept(uv__stream_fd(stream));
if (err < 0) {
if (err == UV_EAGAIN || err == UV__ERR(EWOULDBLOCK))
return; /* Not an error. */
if (err == UV_ECONNABORTED)
continue; /* Ignore. Nothing we can do about that. */
if (err == UV_EMFILE || err == UV_ENFILE) {
err = uv__emfile_trick(loop, uv__stream_fd(stream));
if (err == UV_EAGAIN || err == UV__ERR(EWOULDBLOCK))
break;
}
stream->connection_cb(stream, err);
continue;
}
UV_DEC_BACKLOG(w)
stream->accepted_fd = err;
stream->connection_cb(stream, 0);
...
}main > uv_listen > uv_tcp_listen > uv__server_io > uv__emfile_trick
在上面的 uv__stream_init 函數(shù)中, 我們發(fā)現(xiàn) loop 的 emfile_fd 屬性上通過 uv__open_cloexec 方法創(chuàng)建一個(gè)指向空文件(/dev/null)的 idlefd 文件描述符。
當(dāng)出現(xiàn) accept (EMFILE錯誤)即文件描述符用盡時(shí)的錯誤時(shí)
首先將 loop->emfile_fd 文件描述符, 使其能 accept 新連接, 然后我們新連接將其關(guān)閉,以使其低于EMFILE的限制。接下來,我們接受所有等待的連接并關(guān)閉它們以向客戶發(fā)出信號,告訴他們我們已經(jīng)超載了--我們確實(shí)超載了,但是我們?nèi)栽诶^續(xù)工作。
static int uv__emfile_trick(uv_loop_t* loop, int accept_fd) {
int err;
int emfile_fd;
if (loop->emfile_fd == -1)
return UV_EMFILE;
uv__close(loop->emfile_fd);
loop->emfile_fd = -1;
do {
err = uv__accept(accept_fd);
if (err >= 0)
uv__close(err);
} while (err >= 0 || err == UV_EINTR);
emfile_fd = uv__open_cloexec("/", O_RDONLY);
if (emfile_fd >= 0)
loop->emfile_fd = emfile_fd;
return err;
}當(dāng)收到一個(gè)新連接, 例子中的 on_new_connection 函數(shù)被調(diào)用
通過 uv_tcp_init 初始化了一個(gè) tcp 客戶端流
調(diào)用 uv_accept 函數(shù)
void on_new_connection(uv_stream_t *server, int status) {
if (status < 0) {
fprintf(stderr, "New connection error %s\n", uv_strerror(status));
// error!
return;
}
uv_tcp_t *client = (uv_tcp_t*) malloc(sizeof(uv_tcp_t));
uv_tcp_init(loop, client);
if (uv_accept(server, (uv_stream_t*) client) == 0) {
uv_read_start((uv_stream_t*) client, alloc_buffer, echo_read);
}on_new_connection > uv_accept
根據(jù)不同的協(xié)議調(diào)用不同的方法, 該例子 tcp 調(diào)用 uv__stream_open 方法
uv__stream_open 設(shè)置給初始化完成的 client 流設(shè)置了 i/o 觀察者的 fd。該 fd 即是 uv__server_io 中提到的 ConnectFD 。
int uv_accept(uv_stream_t* server, uv_stream_t* client) {
int err;
assert(server->loop == client->loop);
if (server->accepted_fd == -1)
return UV_EAGAIN;
switch (client->type) {
case UV_NAMED_PIPE:
case UV_TCP:
err = uv__stream_open(client,
server->accepted_fd,
UV_HANDLE_READABLE | UV_HANDLE_WRITABLE);
if (err) {
/* TODO handle error */
uv__close(server->accepted_fd);
goto done;
}
break;
case UV_UDP:
err = uv_udp_open((uv_udp_t*) client, server->accepted_fd);
if (err) {
uv__close(server->accepted_fd);
goto done;
}
break;
default:
return UV_EINVAL;
}
client->flags |= UV_HANDLE_BOUND;
done:
/* Process queued fds */
if (server->queued_fds != NULL) {
uv__stream_queued_fds_t* queued_fds;
queued_fds = server->queued_fds;
/* Read first */
server->accepted_fd = queued_fds->fds[0];
/* All read, free */
assert(queued_fds->offset > 0);
if (--queued_fds->offset == 0) {
uv__free(queued_fds);
server->queued_fds = NULL;
} else {
/* Shift rest */
memmove(queued_fds->fds,
queued_fds->fds + 1,
queued_fds->offset * sizeof(*queued_fds->fds));
}
} else {
server->accepted_fd = -1;
if (err == 0)
uv__io_start(server->loop, &server->io_watcher, POLLIN);
}
return err;
}on_new_connection > uv_read_start
開啟一個(gè)流的監(jiān)聽工作
掛載回調(diào)函數(shù) read_cb 為例子中的 echo_read, 當(dāng)流有數(shù)據(jù)寫入時(shí)被調(diào)用
掛載回調(diào)函數(shù) alloc_cb 為例子中的 alloc_buffer
調(diào)用 uv__io_start 函數(shù), 這可是老朋友了, 通常用在 uv__io_init 初始化 i/o 觀察者后面, 用于注冊 i/o 觀察者。
uv_read_start 主要是調(diào)用了 uv__read_start 函數(shù)。開始了普通流的 i/o 過程。
初始化 i/o 觀察者在 uv_tcp_init > uv_tcp_init_ex > uv__stream_init > uv__io_init 設(shè)置其觀察者回調(diào)函數(shù)為 uv__stream_io
注冊 i/o 觀察者為 uv__io_start 開始監(jiān)聽工作。
int uv__read_start(uv_stream_t* stream,
uv_alloc_cb alloc_cb,
uv_read_cb read_cb) {
assert(stream->type == UV_TCP || stream->type == UV_NAMED_PIPE ||
stream->type == UV_TTY);
/* The UV_HANDLE_READING flag is irrelevant of the state of the tcp - it just
* expresses the desired state of the user.
*/
stream->flags |= UV_HANDLE_READING;
/* TODO: try to do the read inline? */
/* TODO: keep track of tcp state. If we've gotten a EOF then we should
* not start the IO watcher.
*/
assert(uv__stream_fd(stream) >= 0);
assert(alloc_cb);
stream->read_cb = read_cb;
stream->alloc_cb = alloc_cb;
uv__io_start(stream->loop, &stream->io_watcher, POLLIN);
uv__handle_start(stream);
uv__stream_osx_interrupt_select(stream);
return 0;
}感謝各位的閱讀!關(guān)于“Node.js中網(wǎng)絡(luò)與流的示例分析”這篇文章就分享到這里了,希望以上內(nèi)容可以對大家有一定的幫助,讓大家可以學(xué)到更多知識,如果覺得文章不錯,可以把它分享出去讓更多的人看到吧!
網(wǎng)站欄目:Node.js中網(wǎng)絡(luò)與流的示例分析
文章分享:http://chinadenli.net/article32/ihposc.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供自適應(yīng)網(wǎng)站、軟件開發(fā)、虛擬主機(jī)、做網(wǎng)站、建站公司、網(wǎng)站策劃
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來源: 創(chuàng)新互聯(lián)