文章目錄
libuv系列文章
stream handle 的外部API
我們接着上一篇文章接着講解流操作的API。這些api接口我們會很經常使用到,比如在讀取tcp、udp、文件數據的時候,就會用到,同理寫數據的時候也會用到。
uv_shutdown()
關閉流的寫端口,它會等待未完成的寫操作,在關閉後通過uv_shutdown_cb指定的回調函數告知應用層。
注意了,它並不是關閉stream handle,只是關閉了寫入端。
參數:
- req:指定關閉的請求。
- stream:指定stream handle。
- cb:在關閉後告知應用層的回調函數。
int uv_shutdown(uv_shutdown_t* req, uv_stream_t* stream, uv_shutdown_cb cb) {
/* 校驗相關信息,只有 UV_TCP UV_TTY UV_NAMED_PIPE 類型的stream handle 纔可以用*/
assert(stream->type == UV_TCP ||
stream->type == UV_TTY ||
stream->type == UV_NAMED_PIPE);
if (!(stream->flags & UV_HANDLE_WRITABLE) ||
stream->flags & UV_HANDLE_SHUT ||
stream->flags & UV_HANDLE_SHUTTING ||
uv__is_closing(stream)) {
return UV_ENOTCONN;
}
assert(uv__stream_fd(stream) >= 0);
/* 初始化請求 */
uv__req_init(stream->loop, req, UV_SHUTDOWN);
req->handle = stream;
req->cb = cb;
stream->shutdown_req = req;
/* 設置關閉標誌位 */
stream->flags |= UV_HANDLE_SHUTTING;
/* 初始化io觀察者,將 io 觀察者加入到隊列中後,以便在事件循環的特定階段進行處理 */
uv__io_start(stream->loop, &stream->io_watcher, POLLOUT);
uv__stream_osx_interrupt_select(stream);
return 0;
}
uv_listen()
開始偵聽新來的連接,如果你學習過TCP協議,那麼對listen應該很熟悉,它就是用於監聽連接的請求的。
函數實現:
int uv_listen(uv_stream_t* stream, int backlog, uv_connection_cb cb) {
int err;
/* 根據類型處理,只有UV_TCP UV_NAMED_PIPE 纔可以 */
switch (stream->type) {
case UV_TCP:
/* tcp連接,調用tcp的listen去處理 */
err = uv_tcp_listen((uv_tcp_t*)stream, backlog, cb);
break;
case UV_NAMED_PIPE:
/* pipe連接,調用pipe的listen去處理 */
err = uv_pipe_listen((uv_pipe_t*)stream, backlog, cb);
break;
default:
err = UV_EINVAL;
}
if (err == 0)
uv__handle_start(stream);
return err;
}
參數:
-
stream:指定stream handle。
-
backlog:指定libuv監聽的最大的連接數。
-
cb:連接的回調函數,當接受到新來的連接時,調用 uv_connection_cb 回調函數。
注意,這個函數只有tcp或者pipe類型的stream handle纔可使用。
uv_accept()
調用用來配合 uv_listen() 接受新來的連接。一般來說會在 uv_connection_cb 的回調函數中去調用這個 uv_accept() 函數以接受連接,這與tcp協議的處理是非常像的。
注意:在調用這個函數前,客戶端句柄必須被初始化。
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:
/* 獲取文件描述符,並賦值給 client->io_watcher.fd */
err = uv__stream_open(client,
server->accepted_fd,
UV_HANDLE_READABLE | UV_HANDLE_WRITABLE);
if (err) {
/* 出現錯誤就關閉 */
uv__close(server->accepted_fd);
goto done;
}
break;
case UV_UDP:
/* 對於udp協議,通過uv_udp_open去獲取文件描述符 */
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:
/* 處理在排隊的連接請求 */
if (server->queued_fds != NULL) {
uv__stream_queued_fds_t* queued_fds;
queued_fds = server->queued_fds;
/* 處理第一個排隊的 */
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;
}
uv_read_start()
當連接成功後,可以調用uv_read_start()
函數去監聽流的讀取端,當有數據可讀的時候,將會調用uv_read_cb
指定的回調函數,遞交到用戶去處理這些數據。
- 函數原型
int uv_read_start(uv_stream_t* stream,
uv_alloc_cb alloc_cb,
uv_read_cb read_cb)
參數:
-
stream:指定stream handle。
-
alloc_cb:讀取數據時調用該函數分配內存空間。
-
read_cb:讀取成功後觸發異步回調。
函數源碼:
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);
if (stream->flags & UV_HANDLE_CLOSING)
return UV_EINVAL;
if (!(stream->flags & UV_HANDLE_READABLE))
return UV_ENOTCONN;
/* 設置標誌位,表示此時流正在被使用讀取 */
stream->flags |= UV_HANDLE_READING;
assert(uv__stream_fd(stream) >= 0);
assert(alloc_cb);
/* 註冊回調函數 */
stream->read_cb = read_cb;
stream->alloc_cb = alloc_cb;
/* 啓動io觀察者 */
uv__io_start(stream->loop, &stream->io_watcher, POLLIN);
uv__handle_start(stream);
uv__stream_osx_interrupt_select(stream);
return 0;
}
uv_read_stop()
與uv_read_start()
函數剛好相反,uv_read_stop()
函數是停止從流讀取數據。 uv_read_cb
回調函數將不再被調用。
參數:
- stream:指定stream handle。
int uv_read_stop(uv_stream_t* stream) {
/* 如果它未被設置爲 UV_HANDLE_READING 讀取佔用標誌位,則不需要停止 */
if (!(stream->flags & UV_HANDLE_READING))
return 0;
/* 取消設置 UV_HANDLE_READING 標誌位 */
stream->flags &= ~UV_HANDLE_READING;
/* 停止流讀取,關閉流io觀察者 */
uv__io_stop(stream->loop, &stream->io_watcher, POLLIN);
/* 當且僅當io觀察者不處於活躍時才關閉這個 stream handle */
if (!uv__io_active(&stream->io_watcher, POLLOUT))
uv__handle_stop(stream);
uv__stream_osx_interrupt_select(stream);
/* 取消註冊回調函數 */
stream->read_cb = NULL;
stream->alloc_cb = NULL;
return 0;
}
uv_write()
向stream handle 寫入數據,實際上是調用uv_write2()
這個函數。
參數:
-
req:請求。
-
handle:指定的stream handle
-
bufs:要寫入的buf數據。
-
nbufs:要寫入數據的大小。
-
cb:當寫操作完成後,調用的回調函數。
int uv_write(uv_write_t* req,
uv_stream_t* handle,
const uv_buf_t bufs[],
unsigned int nbufs,
uv_write_cb cb) {
return uv_write2(req, handle, bufs, nbufs, NULL, cb);
}
uv_write2()函數
擴展的寫函數,可用於在管道上發送數據。
int uv_write2(uv_write_t* req,
uv_stream_t* stream,
const uv_buf_t bufs[],
unsigned int nbufs,
uv_stream_t* send_handle,
uv_write_cb cb) {
int empty_queue;
/* 健壯性的判斷,只有tcp pipe tty類型的handle纔可用改函數 */
assert(nbufs > 0);
assert((stream->type == UV_TCP ||
stream->type == UV_NAMED_PIPE ||
stream->type == UV_TTY) &&
"uv_write (unix) does not yet support other types of streams");
if (uv__stream_fd(stream) < 0)
return UV_EBADF;
if (!(stream->flags & UV_HANDLE_WRITABLE))
return UV_EPIPE;
if (send_handle) {
if (stream->type != UV_NAMED_PIPE || !((uv_pipe_t*)stream)->ipc)
return UV_EINVAL;
if (uv__handle_fd((uv_handle_t*) send_handle) < 0)
return UV_EBADF;
#if defined(__CYGWIN__) || defined(__MSYS__)
/* Cygwin recvmsg always sets msg_controllen to zero, so we cannot send it.
See https://github.com/mirror/newlib-cygwin/blob/86fc4bf0/winsup/cygwin/fhandler_socket.cc#L1736-L1743 */
return UV_ENOSYS;
#endif
}
/* 即使write_queue爲空,write_queue_size> 0也合法;這意味着write_completed_queue中存在錯誤狀態請求,這些錯誤狀態請求將在以後修改write_queue_size */
empty_queue = (stream->write_queue_size == 0);
/* 初始化請求 */
uv__req_init(stream->loop, req, UV_WRITE);
/* 註冊回調函數,註冊請求的stream handle */
req->cb = cb;
req->handle = stream;
req->error = 0;
req->send_handle = send_handle;
QUEUE_INIT(&req->queue);
req->bufs = req->bufsml;
if (nbufs > ARRAY_SIZE(req->bufsml))
req->bufs = uv__malloc(nbufs * sizeof(bufs[0]));
if (req->bufs == NULL)
return UV_ENOMEM;
memcpy(req->bufs, bufs, nbufs * sizeof(bufs[0]));
req->nbufs = nbufs;
req->write_index = 0;
stream->write_queue_size += uv__count_bufs(bufs, nbufs);
/* 將請求追加到write_queue。 */
QUEUE_INSERT_TAIL(&stream->write_queue, &req->queue);
/* 如果此函數開始時隊列爲空,則應嘗試立即進行寫操作。否則,啓動write_watcher並等待fd變爲可寫狀態。 */
if (stream->connect_req) {
/* 仍在連接,什麼也不做. */
}
else if (empty_queue) {
uv__write(stream);
}
else {
/* 阻塞流永遠不會在隊列中有任何東西。 */
assert(!(stream->flags & UV_HANDLE_BLOCKING_WRITES));
uv__io_start(stream->loop, &stream->io_watcher, POLLOUT);
uv__stream_osx_interrupt_select(stream);
}
return 0;
}