I/O Multiplexing: select and poll
I/O Multiplexing: select and poll
I/O Multiplexing: select and poll
You also want an ePaper? Increase the reach of your titles
YUMPU automatically turns print PDFs into web optimized ePapers that Google loves.
I/O <strong>Multiplexing</strong>: <strong>select</strong> <strong>and</strong> <strong>poll</strong><br />
•Introduction<br />
•I/O models<br />
•<strong>select</strong> function<br />
•Rewrite str_cli function<br />
•Supporting batch input with shutdown function<br />
•Rewrite concurrent TCP echo server with <strong>select</strong><br />
•p<strong>select</strong> function: avoiding signal loss in race condition<br />
•<strong>poll</strong> function: <strong>poll</strong>ing more specific conditions than<br />
<strong>select</strong><br />
•Rewrite concurrent TCP echo server with <strong>poll</strong><br />
1<br />
Some materials in these slides are taken from Prof. Ying-Dar Lin with his permission of usage
Introduction<br />
•I/O multiplexing: to be notified, by kernel, if one or<br />
more I/O conditions are ready.<br />
•Scenarios in networking applications:<br />
–a client h<strong>and</strong>ling multiple descriptors (stdio/socket)<br />
–a client h<strong>and</strong>ling multiple sockets<br />
–a TCP server h<strong>and</strong>ling a listening socket <strong>and</strong> its<br />
connected sockets ( 兩 件 事 都 自 己 來 )<br />
–a server h<strong>and</strong>ling both TCP <strong>and</strong> UDP<br />
–a server h<strong>and</strong>ling multiple services <strong>and</strong> protocols<br />
2
I/O 動 作 如 何 進 行 <br />
• 一 般 process 無 法 直 接 對 I/O 裝 置 下 命 令 , 必 須<br />
透 過 system call 請 求 kernel 幫 忙 進 行 I/O 動 作<br />
•kernel 會 對 每 個 I/O 裝 置 維 護 一 個 buffer<br />
wait<br />
copy<br />
I/O Device Kernel’s buffer Process<br />
• 對 輸 入 而 言 , 等 待 (wait) 資 料 輸 入 至 buffer 需 要<br />
時 間 , 從 buffer 搬 移 (copy) 資 料 也 需 要 時 間 。<br />
• 根 據 等 待 模 式 不 同 ,I/O 動 作 可 分 為 五 種 模 式<br />
3
Five I/O Models<br />
•blocking I/O: blocked all the way<br />
•nonblocking I/O: if no data in buffer, immediate<br />
returns EWOULDBLOCK<br />
•I/O multiplexing (<strong>select</strong> <strong>and</strong> <strong>poll</strong>): blocked<br />
separately in wait <strong>and</strong> copy<br />
•signal driven I/O (SIGIO): nonblocked in wait<br />
but blocked in copy (signaled when I/O can be<br />
initiated)<br />
•asynchronous I/O (aio_): nonblocked all the way<br />
(signaled when I/O is complete)<br />
4
Comparison of Five I/O Models<br />
blocking nonblocking I/O multiplexing signal-driven asynchronous<br />
initiate check check establish initiate<br />
check SIGIO specify<br />
check h<strong>and</strong>ler signal/h<strong>and</strong>ler<br />
check<br />
check<br />
check ready notification<br />
initiate initiate<br />
blocked<br />
blocked<br />
blocked<br />
wait for data copy data<br />
blocked<br />
blocked<br />
complete complete complete complete notification<br />
synchronous I/O<br />
asynchronous I/O<br />
5
I/O <strong>Multiplexing</strong>: 使 用 <strong>select</strong><br />
• 本 章 要 做 的 是 I/O <strong>Multiplexing</strong>( 第 三 種 I/O<br />
model): 使 用 <strong>select</strong> system call<br />
•<strong>select</strong> 要 求 kernel 測 試 某 裝 置 是 否 滿 足<br />
我 們 設 定 的 條 件 。 若 滿 足 則 return, 否 則<br />
等 待 至 此 條 件 滿 足 時 為 止 (<strong>select</strong> 會 被<br />
block)<br />
•<strong>select</strong> 呼 叫 可 以 指 定 等 待 的 時 間 上 限<br />
•<strong>select</strong> 呼 叫 可 以 指 定 測 試 多 個 I/O 裝 置<br />
6
對 輸 入 裝 置 使 用 <strong>select</strong><br />
• 對 輸 入 裝 置 而 言 , 可 以 使 用 <strong>select</strong> 要 求<br />
kernel 測 試 某 裝 置 是 否 ready for reading<br />
• 當 此 裝 置 的 buffer 中 已 有 相 當 數 量 ( 可 設<br />
定 ) 的 輸 入 資 料 時 , 此 裝 置 即 是 ready for<br />
reading<br />
•<strong>select</strong> return 後 , 由 我 們 的 程 式 自 行 呼<br />
叫 其 它 的 system call 將 buffer 中 的 資 料 搬<br />
回 來 。<br />
7
對 輸 出 裝 置 使 用 <strong>select</strong><br />
• 對 輸 出 裝 置 而 言 , 可 以 使 用 <strong>select</strong> 要 求<br />
kernel 測 試 某 裝 置 是 否 ready for writing<br />
• 當 此 裝 置 的 buffer 中 已 有 相 當 空 間 ( 可 設<br />
定 ) 可 放 置 輸 出 資 料 時 , 此 裝 置 即 是 ready<br />
for writing<br />
•<strong>select</strong> return 後 , 由 我 們 的 程 式 自 行 呼<br />
叫 其 它 的 system call 將 欲 輸 出 資 料 搬 入<br />
buffer 中 。<br />
8
<strong>select</strong> Function<br />
#include <br />
#include <br />
int <strong>select</strong> (int maxfdp1, fd_set *readset, fd_set *writeset, fd_set *exceptset,<br />
const struct timeval *timeout);<br />
returns: positive count of ready descriptors, 0 on timeout, -1 on error<br />
struct timeval { (null: wait forever; 0: do not wait)<br />
long tv_sec; /*second */<br />
long tv_usec; /* microsecond */<br />
};<br />
readset, writeset, <strong>and</strong> exceptset specify the descriptors that<br />
we want the kernel to test for reading, writing, <strong>and</strong><br />
exception conditions, respectively.<br />
maxfdp1 is the maximum descriptor to be tested plus one.<br />
9
<strong>select</strong> Fucntion<br />
OR<br />
•We can call <strong>select</strong> <strong>and</strong> tell the kernel to<br />
return only when<br />
–any descriptor in {1, 4, 5} is ready for<br />
reading (buffer 中 已 有 資 料 )<br />
–any descriptor in {2, 7} is ready for writing<br />
–any descriptor in {1, 4} has an exception<br />
condition pending<br />
–after 10.2 seconds have elapsed<br />
Buffer<br />
中 已 有<br />
空 間<br />
裝 置 有 例<br />
外 狀 況<br />
10
Specifying Descriptor Values<br />
•We need to declare variables of data type fd_set <strong>and</strong><br />
use macros to manipulate these variables.<br />
fd_set --- implementation dependent<br />
four macros: void FD_ZERO(fd_set *fdset);<br />
void FD_SET(int fd, fd_set *fdset);<br />
void FD_CLR(int fd, fd_set *fdset);<br />
int FD_ISSET(int fd, fd_set *fdset);<br />
fd_set rset;<br />
FD_ZERO(&rset);<br />
FD_SET(1, &rset);<br />
FD_SET(4, &rset);<br />
FD_SET(5, &rset);<br />
maxfdp1 = 6<br />
Turn on bits for<br />
descriptors 1, 4, <strong>and</strong> 5<br />
11
Socket Ready Conditions for <strong>select</strong><br />
Condition readable writeable Exception<br />
enough data to read x<br />
read-half closed x<br />
new connection ready x<br />
writing space available<br />
x<br />
write-half closed<br />
x<br />
pending error x x<br />
TCP out-of-b<strong>and</strong> data<br />
x<br />
Low-water mark (enough data/space to read/write in<br />
socket receive/send buffer): default is 1/2048, may be set by<br />
SO_RCVLOWAT/SO_SNDLOWAT socket option<br />
Maximum number of descriptors for <strong>select</strong><br />
Redefine FD_SETSIZE <strong>and</strong> recompile kernel<br />
12
Low-Water Mark ( 低 水 位 )<br />
• 對 socket receive buffer 而 言<br />
– 如 收 到 data 量 不 足 low-water mark, socket is<br />
not ready for reading<br />
–Default = 1 byte<br />
• 對 socket send buffer 而 言<br />
– 如 可 用 空 間 (available space) 不 足 low-water<br />
mark, socket is not ready for writing<br />
–Default = 2048 byte<br />
13
用 <strong>select</strong> 改 寫 str_cli<br />
• 上 一 章 的 str_cli 用 兩 個 system calls 分 別<br />
取 得 input<br />
–fgets 用 於 stdin 的 user input<br />
–readline 用 於 socket input<br />
均 為 Blocking I/O<br />
• 這 樣 沒 有 辦 法 同 時 等 待 兩 個 inputs。 因 此<br />
當 client 等 在 fgets 時 , 無 法 同 時 取 得<br />
socket input 進 來 的 資 料<br />
• 本 章 改 用 <strong>select</strong> 來 同 時 等 待 兩 個 inputs<br />
14
Ch. 5<br />
兩 個 版 本 的 比 較<br />
Ch. 6<br />
return<br />
呼 叫 fgets 從 stdin<br />
讀 取 使 用 者 的 輸 入<br />
yes<br />
EOF<br />
呼 叫 writen 將 輸 入<br />
資 料 輸 出 至 socket<br />
呼 叫 readline 從<br />
socket 讀 取 資 料<br />
no<br />
呼 叫 fputs 將 讀 取 的<br />
資 料 輸 出 至 stdout<br />
socket is<br />
readable<br />
呼 叫 readline 從<br />
socket 讀 取 資 料<br />
呼 叫 fputs 將 讀 取 的<br />
資 料 輸 出 至 stdout<br />
呼 叫 <strong>select</strong> 同 時 等<br />
待 socket 及 stdin<br />
return<br />
stdin is<br />
readable<br />
呼 叫 fgets 從 stdin<br />
讀 取 使 用 者 的 輸 入<br />
yes<br />
EOF<br />
呼 叫 writen 將 輸 入<br />
資 料 輸 出 至 socket<br />
15<br />
no
Rewrite str_cli Function with <strong>select</strong><br />
data or EOF<br />
TCP<br />
stdin<br />
error<br />
client<br />
socket<br />
EOF<br />
<strong>select</strong> for readability<br />
on either stdin or socket<br />
用 <strong>select</strong> 同 時 等 待 stdio 及<br />
socket input. 當 任 一 裝 置<br />
ready for reading 時 <strong>select</strong><br />
會 return<br />
當 <strong>select</strong> return 時 怎 知 道 是 哪<br />
些 裝 置 ready for reading 呢 <br />
RST data FIN<br />
用 FD_ISSET 測 試 傳 回 值<br />
readset, writeset, 和 exceptset 是 雙 向 變 數<br />
可 得 知 那 些 裝 置 是 ready<br />
(value-result argemnets)<br />
16
Rewrite str_cli Function with <strong>select</strong><br />
#include<br />
"unp.h"<br />
<strong>select</strong>/strcli<strong>select</strong>01.c<br />
void<br />
str_cli(FILE *fp, int sockfd)<br />
{<br />
int maxfdp1;<br />
fd_set rset;<br />
char sendline[MAXLINE], recvline[MAXLINE];<br />
FD_ZERO(&rset);<br />
for ( ; ; ) {<br />
放 在<br />
loop 內<br />
Why<br />
傳 回 檔 案 指 標 fp<br />
的 descriptor no<br />
FD_SET(fileno(fp), &rset);<br />
FD_SET(sockfd, &rset);<br />
maxfdp1 = max(fileno(fp), sockfd) + 1;<br />
Select(maxfdp1, &rset, NULL, NULL, NULL);<br />
只 測 試 read<br />
17
}<br />
}<br />
if (FD_ISSET(sockfd, &rset)) { /* socket is readable */<br />
}<br />
if (Readline(sockfd, recvline, MAXLINE) == 0)<br />
err_quit("str_cli: server terminated prematurely");<br />
Fputs(recvline, stdout);<br />
if (FD_ISSET(fileno(fp), &rset)) { /* input is readable */<br />
}<br />
用 FD_ISSET 測 試<br />
裝 置 是 否 ready<br />
用 FD_ISSET 測 試<br />
裝 置 是 否 ready<br />
if (Fgets(sendline, MAXLINE, fp) == NULL)<br />
return; /* all done */<br />
Writen(sockfd, sendline, strlen(sendline));<br />
<strong>select</strong>/strcli<strong>select</strong>01.c<br />
User 按 了<br />
Ctrl+D<br />
18
使 用 <strong>select</strong> 函 數 常 犯 的 兩 個 錯 誤<br />
‧ 忘 記 maxfdp1 是 descriptor 的 最 大 值 加 1<br />
‧ 忘 記 readset, writeset, 和 exceptset 是 雙 向<br />
變 數<br />
–<strong>select</strong> return 時 會 改 變 它 們 的 值<br />
– 因 此 再 次 呼 叫 <strong>select</strong> 時 別 忘 了 要 重 新 設 定<br />
這 些 變 數 的 內 容<br />
19
Redirecting Input on UNIX<br />
Systems<br />
•St<strong>and</strong>ard I/O - keyboard <strong>and</strong> screen<br />
•Input Redirection symbol (
Stop-<strong>and</strong>-Wait (Interactive) Mode<br />
• 原 來 版 本 的 str_cli 強 制 輪 流 處 理 stdin 輸 入<br />
與 socket 輸 入<br />
client<br />
keyin<br />
keyin<br />
socket<br />
in<br />
提 早 輸 入 並 不<br />
會 提 早 處 理<br />
socket<br />
in<br />
keyin<br />
不 管 多 晚 輸<br />
入 都 會 等 待<br />
socket<br />
in<br />
server<br />
time<br />
21
Batch Mode<br />
• 在 新 版 str_cli 使 用 input redirection<br />
client<br />
不 需<br />
等 待<br />
server<br />
tcpcli04 206.62.226.35 < file1.txt<br />
lines<br />
…<br />
EOF<br />
…<br />
…<br />
time<br />
檔 案 最 後 的 EOF<br />
會 使 fgets 傳 回<br />
NULL,str_cli<br />
會 因 此 return<br />
main 會 close 掉<br />
TCP connection,<br />
導 致 server 尚 未 回<br />
傳 的 資 料 遺 失<br />
將 stdin 重 導<br />
至 file1.txt<br />
就 算 不<br />
用 輸 入<br />
重 導 向<br />
也 有 此<br />
問 題 ,<br />
只 是 較<br />
不 嚴 重<br />
22
Solution: UseShutdown Instead<br />
ofClose<br />
•In str_cli, close write-half of TCP<br />
connection, by shutdown, while leaving<br />
read-half open.<br />
先 關 client 至 server 方 向 的 connection<br />
server 至 client 方 向 的 connection 暫 時 不 要 關<br />
等 到 server 的 資 料 全 部 送 回 來<br />
後 再 關 掉 server 至 client 方 向 的<br />
connection<br />
23
Function shutdown<br />
#include <br />
int shutdown(int sockfd, int howto);<br />
howto: SHUT_RD, SHUT_WR, SHUT_RDWR<br />
returns: 0 if OK, -1 on error<br />
•initiates TCP normal termination<br />
regardless of descriptor’s reference<br />
count<br />
•<strong>select</strong>ively closes one direction of the<br />
connection (SHUT_RD or SHUT_WR)<br />
24
Rewrite str_cli with <strong>select</strong> <strong>and</strong> shutdown<br />
#include<br />
"unp.h"<br />
<strong>select</strong>/strcli<strong>select</strong>02.c<br />
void<br />
str_cli(FILE *fp, int sockfd)<br />
{<br />
int<br />
fd_set rset;<br />
char<br />
maxfdp1, stdineof;<br />
判 斷 client 至 server 方 向 連<br />
結 是 否 已 斷 的 旗 標 變 數<br />
sendline[MAXLINE], recvline[MAXLINE];<br />
stdineof = 0;<br />
FD_ZERO(&rset);<br />
當 client 至 server 方 向 連 結<br />
for ( ; ; ) {<br />
未 斷 時 才 要 test stdin<br />
if (stdineof == 0)<br />
FD_SET(fileno(fp), &rset);<br />
FD_SET(sockfd, &rset);<br />
maxfdp1 = max(fileno(fp), sockfd) + 1;<br />
Select(maxfdp1, &rset, NULL, NULL, NULL);<br />
25
}<br />
client 至 server<br />
方 向 連 結 已 斷<br />
}<br />
if (FD_ISSET(sockfd, &rset)) { /* socket is readable */<br />
if (Readline(sockfd, recvline, MAXLINE) == 0) {<br />
if (stdineof == 1)<br />
return; /* normal termination */<br />
else<br />
err_quit("str_cli: server terminated prematurely");<br />
}<br />
Fputs(recvline, stdout);<br />
}<br />
if (FD_ISSET(fileno(fp), &rset)) { /* input is readable */<br />
if (Fgets(sendline, MAXLINE, fp) == NULL) {<br />
stdineof = 1;<br />
Shutdown(sockfd, SHUT_WR); /* send FIN */<br />
}<br />
FD_CLR(fileno(fp), &rset);<br />
continue;<br />
}<br />
Writen(sockfd, sendline, strlen(sendline));<br />
<strong>select</strong>/strcli<strong>select</strong>02.c<br />
只 斷 client 至<br />
server 方 向 連 結<br />
26
Concurrent TCP Echo Server with <strong>select</strong><br />
•A single server process using <strong>select</strong> to h<strong>and</strong>le<br />
any number of clients ( 不 fork child process)<br />
•Need to keep track of the clients by client[ ]<br />
(client descriptor array) <strong>and</strong> rset (read descriptor set)<br />
client[ ]<br />
[0] -1<br />
[1] 5<br />
[2] -1<br />
[FD_SETSIZE-1] -1<br />
maxi<br />
not<br />
used<br />
rset<br />
儲 存 已 open socket 的 descriptor<br />
stdin/stdout/stderr<br />
listening socket<br />
terminated client<br />
fd0 fd1 fd2 fd3 fd4 fd5<br />
0 0 0 1 0 1<br />
maxfd +1 = 6<br />
用 於 呼 叫 <strong>select</strong><br />
existing client<br />
27
Rewrite Concurrent TCP Echo Server with <strong>select</strong><br />
Initialization<br />
#include "unp.h"<br />
int main(int argc, char **argv)<br />
tcpcliserv/tcpserv<strong>select</strong>01.c<br />
{<br />
int<br />
i, maxi, maxfd, listenfd, connfd, sockfd;<br />
int<br />
nready, client[FD_SETSIZE];<br />
ssize_t n;<br />
fd_set<br />
rset, allset;<br />
char<br />
line[MAXLINE];<br />
socklen_t<br />
clilen;<br />
struct sockaddr_in cliaddr, servaddr;<br />
listenfd = Socket(AF_INET, SOCK_STREAM, 0);<br />
bzero(&servaddr, sizeof(servaddr));<br />
servaddr.sin_family = AF_INET;<br />
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);<br />
servaddr.sin_port = htons(SERV_PORT);<br />
28
Initialization (cont.)<br />
tcpcliserv/tcpserv<strong>select</strong>01.c<br />
Bind(listenfd, (SA *) &servaddr, sizeof(servaddr));<br />
Listen(listenfd, LISTENQ);<br />
maxfd = listenfd;/* initialize */<br />
maxi = -1;/* index into client[] array */<br />
for (i = 0; i < FD_SETSIZE; i++)<br />
client[i] = -1;/* -1 indicates available entry */<br />
FD_ZERO(&allset);<br />
FD_SET(listenfd, &allset);<br />
測 試 listening socket 是<br />
否 ready for reading<br />
29
Loop<br />
for ( ; ; ) {<br />
<strong>select</strong> 的 傳 回 值 是 ready 的 裝 置 數 目<br />
rset = allset; /* structure assignment */<br />
nready = Select(maxfd+1, &rset, NULL, NULL, NULL);<br />
if (FD_ISSET(listenfd, &rset)) { /* new client connection */<br />
新<br />
連<br />
結<br />
放 入 第 一 個<br />
找 到 的 空 格<br />
如 果 沒 有 其<br />
它 ready 的<br />
descriptor<br />
}<br />
clilen = sizeof(cliaddr);<br />
connfd = Accept(listenfd, (SA *) &cliaddr, &clilen);<br />
for (i = 0; i < FD_SETSIZE; i++)<br />
if (client[i] < 0) {<br />
client[i] = connfd; /* save descriptor */<br />
break;<br />
}<br />
if (i == FD_SETSIZE)<br />
err_quit("too many clients");<br />
tcpcliserv/tcpserv<strong>select</strong>01.c<br />
加 進 <strong>select</strong> 要 test<br />
的 descriptor set<br />
不 會 block<br />
FD_SET(connfd, &allset); /* add new descriptor to set */<br />
if (connfd > maxfd)<br />
maxfd = connfd; /* for <strong>select</strong> */<br />
if (i > maxi)<br />
maxi = i; /* max index in client[] array */<br />
if (--nready
Loop (cont.)<br />
tcpcliserv/tcpserv<strong>select</strong>01.c<br />
}<br />
}<br />
}<br />
for (i = 0; i
前 頁 程 式 的 潛 在 問 題<br />
•<strong>select</strong> 在 socket 的 input buffer 有 資<br />
料 可 讀 時 即 會 return 此 socket 已 ready<br />
for reading<br />
•Readline 被 設 計 成 要 讀 到 以 換 行 字 元<br />
結 束 的 一 列 文 字 ( 或 是 socket 被 結 束 掉 ) 才<br />
會 return<br />
• 有 心 人 士 可 能 利 用 這 項 弱 點 進 行 攻 擊<br />
32
Denial of Service Attacks<br />
• 問 題 出 在 Server 程 式 可 能 block 在 Readline 中<br />
•Attack scenario:<br />
–a malicious client sends 1 byte of data (other<br />
than a newline) <strong>and</strong> sleep<br />
–server hangs until the malicious client either<br />
sends a newline or terminates<br />
–(server 要 讀 到 newline, Readline 才 能 return)<br />
33
Solutions to the Attack<br />
•When a server is h<strong>and</strong>ling multiple<br />
clients the server can never block in a<br />
function call related to a single client<br />
•Possible solutions:<br />
–Use nonblocking I/O (Ch. 15)<br />
–separate thread/process for each client<br />
–timeout on I/O operations (Sec. 13.2)<br />
34
<strong>poll</strong> Function: <strong>poll</strong>ing more specific<br />
conditions than <strong>select</strong><br />
結 構 陣 列 儲 存 位 址<br />
陣 列 中 的 資 料 數 目<br />
微 秒<br />
#include <br />
int <strong>poll</strong> (struct <strong>poll</strong>fd *fdarray, unsigned long ndfs, int timeout);<br />
returns: count of ready descriptors, 0 on timeout, -1 on error<br />
每 個 descriptor 要 test 的 條 件 是 用 <strong>poll</strong>fd 結 構 來 表 示<br />
這 些 <strong>poll</strong>fd 結 構 集 合 成 一 個 陣 列<br />
struct <strong>poll</strong>fd { 要 測 的 file descriptor number; -1 表 此 結 構 無 效<br />
int fd; /* a descriptor to <strong>poll</strong> */<br />
short events; /* events of interested fd, value argument */<br />
short revents; /* events that occurred on fd, result argument */<br />
};<br />
要 測 試 的 條 件<br />
真 正 測 到 的 事 件<br />
35
<strong>poll</strong> 函 數 中 的 事 件 設 定<br />
•events 與 revents 由 下 列 bit flag 組 成<br />
Constant events revents Description<br />
POLLIN x x normal or priority b<strong>and</strong> to read<br />
POLLRDNORM x x normal data to read<br />
POLLRDBAND x x priority b<strong>and</strong> data to read<br />
POLLPRI x x high-priority data to read<br />
POLLOUT x x normal data can be written<br />
POLLWRNORM x x normal data can be written<br />
POLLWRBAND x x priority b<strong>and</strong> data can be written<br />
POLLERR x an error has occurred<br />
POLLHUP x hangup has occurred<br />
POLLNVAL x descriptor is not an open file<br />
36
設 定 events 與 測 試 revents<br />
‧ 設 定 events: 使 用 bitwise OR<br />
struct <strong>poll</strong>fd client[OPEN_MAX];<br />
…<br />
client[0].events = POLLRDNORM | POLLRDBAND;<br />
‧ 測 試 revents: 使 用 bitwise AND<br />
struct <strong>poll</strong>fd client[OPEN_MAX];<br />
…<br />
if (client[0].revents & POLLRDNORM)<br />
…<br />
37
Three Classes of Data Identified<br />
by <strong>poll</strong><br />
•normal, priority b<strong>and</strong>, <strong>and</strong> high priority<br />
normal priority b<strong>and</strong> high priority<br />
All regular TCP data x<br />
All UDP data<br />
x<br />
TCP’s out-of-b<strong>and</strong> data<br />
x<br />
Half-closed TCP x<br />
Error for TCP<br />
x (or POLLERR)<br />
New connection x x<br />
38
Concurrent TCP Echo Server with <strong>poll</strong><br />
•When using <strong>select</strong>, the server maintains array client[ ]<br />
<strong>and</strong> descriptor set rset. When using <strong>poll</strong>, the server<br />
maintains array client of <strong>poll</strong>fd structure.<br />
•Program flow:<br />
–allocate array of <strong>poll</strong>fd structures<br />
–initialize (listening socket: first entry in client)<br />
(set POLLRDNORM in events)<br />
–call <strong>poll</strong>; check for new connection<br />
(check, in revents, <strong>and</strong> set, in events, POLLRDNORM)<br />
–check for data on an existing connection<br />
(check POLLRDNORM or POLLERR in revents)<br />
39
Rewrite Concurrent TCP Echo Server<br />
with <strong>poll</strong><br />
Initialization<br />
tcpcliserv/tcpserv<strong>poll</strong>01.c<br />
#include "unp.h"<br />
#include /* for OPEN_MAX */<br />
int main(int argc, char **argv)<br />
{<br />
int<br />
i, maxi, listenfd, connfd, sockfd;<br />
int<br />
nready;<br />
ssize_t n;<br />
char<br />
line[MAXLINE];<br />
結 構 陣 列<br />
socklen_t clilen;<br />
struct <strong>poll</strong>fd client[OPEN_MAX];<br />
struct sockaddr_in cliaddr, servaddr;<br />
listenfd = Socket(AF_INET, SOCK_STREAM, 0);<br />
bzero(&servaddr, sizeof(servaddr));<br />
servaddr.sin_family = AF_INET;<br />
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);<br />
servaddr.sin_port = htons(SERV_PORT);<br />
40
tcpcliserv/tcpserv<strong>poll</strong>01.c<br />
Initialization (cont.)<br />
Bind(listenfd, (SA *) &servaddr, sizeof(servaddr));<br />
Listen(listenfd, LISTENQ);<br />
client[0].fd = listenfd;<br />
client[0].events = POLLRDNORM;<br />
for (i = 1; i < OPEN_MAX; i++)<br />
第 0 個 元 素 放<br />
listening socket<br />
client[i].fd = -1; /* -1 indicates available entry */<br />
maxi = 0; /* max index into client[] array */<br />
41
Loop<br />
for ( ; ; ) {<br />
nready = Poll(client, maxi+1, INFTIM);<br />
放 入 第 一 個<br />
找 到 的 空 格<br />
if (client[0].revents & POLLRDNORM) { /* new client connection */<br />
clilen = sizeof(cliaddr);<br />
connfd = Accept(listenfd, (SA *) &cliaddr, &clilen);<br />
for (i = 1; i < OPEN_MAX; i++)<br />
if (client[i].fd < 0) {<br />
client[i].fd = connfd; /* save descriptor */<br />
break;<br />
}<br />
}<br />
bitwise AND<br />
tcpcliserv/tcpserv<strong>poll</strong>01.c<br />
wait forever<br />
不 會 block<br />
if (i == OPEN_MAX)<br />
for data socket<br />
err_quit("too many clients");<br />
client[i].events = POLLRDNORM;<br />
if (i > maxi)<br />
maxi = i; /* max index in client[] array */<br />
if (--nready
tcpcliserv/tcpserv<strong>poll</strong>01.c<br />
for (i = 1; i
Conclusion<br />
• 本 章 介 紹 如 何 用 I/O <strong>Multiplexing</strong> 來 取 代<br />
blocking I/O, 以 同 時 等 待 兩 個 以 上 的 I/O<br />
裝 置<br />
•I/O <strong>Multiplexing</strong> 可 用 <strong>select</strong> 或 <strong>poll</strong> 來<br />
達 成<br />
• 為 何 不 使 用 nonblocking I/O 呢 就 算<br />
Buffer 中 沒 資 料 也 可 立 即 return 耶 …<br />
return 後 要 做 啥 <br />
44