サーチ…
前書き
IOはブロッキング/非ブロッキングと同期/非同期である可能性があります。 POSIX APIは、同期非ブロッキングAPI(同じ関数、 O_NONBLOCK
フラグとIO多重化呼び出しでオープンされたファイル記述子)と非同期API( aio_
始まる関数)を提供します。
同期APIは、通常、「1スレッド/プロセス/ fd」スタイルで使用されます。これはリソースにとって恐ろしいことです。ノンブロッキングAPIを使用すると、1つのスレッド内のfdsのセットを操作できます。
世論調査
この例では、接続されたソケットのペアを作成し、4つの文字列を1つから別のものに送信し、受信した文字列をコンソールに出力します。私たちがsendを呼び出す回数は、私たちがrecvを呼び出した回数と同じではないかもしれないことに注意してください
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>
#include <poll.h>
#define BUFSIZE 512
int main()
{
#define CKERR(msg) {if(ret < 0) { perror(msg); \
close(sockp[0]); close(sockp[1]); exit(EXIT_FAILURE); } }
const char* strs_to_write[] = {"hello ", "from ", "other ", "side "};
int sockp[2] = {-1, -1};
ssize_t ret = socketpair (AF_UNIX, SOCK_STREAM, 0, sockp);
CKERR("Socket pair creation error")
struct pollfd pfds[2];
for(int i=0; i<2; ++i) {
pfds[i] = (struct pollfd){sockp[i], POLLIN|POLLOUT, 0};
fcntl(sockp[i], F_SETFL|O_NONBLOCK); // nonblocking fds are
// literally mandatory for IO multiplexing; non-portable
}
char buf[BUFSIZE];
size_t snt = 0, msgs = sizeof(strs_to_write)/sizeof(char*);
while(1) {
int ret = poll(pfds,
2 /*length of pollfd array*/,
5 /*milliseconds to wait*/);
CKERR("Poll error")
if (pfds[0].revents & POLLOUT && snt < msgs) {
// Checking POLLOUT before writing to ensure there is space
// available in socket's kernel buffer to write, otherwise we
// may face EWOULDBLOCK / EAGAIN error
ssize_t ret = send(sockp[0], strs_to_write[snt], strlen(strs_to_write[snt]), 0);
if(++snt >= msgs)
close(sockp[0]);
CKERR("send error")
if (ret == 0) {
puts("Connection closed");
break;
}
if (ret > 0) {
// assuming that all bytes were written
// if ret != %sent bytes number%, send other bytes later
}
}
if (pfds[1].revents & POLLIN) {
// There is something to read
ssize_t ret = recv(sockp[1], buf, BUFSIZE, 0);
CKERR("receive error")
if (ret == 0) {
puts("Connection closed");
break;
}
if (ret > 0) {
printf("received str: %.*s\n", (int)ret, buf);
}
}
}
close(sockp[1]);
return EXIT_SUCCESS;
}
選択
Selectは、I / O多重化を行うもう1つの方法です。その利点の1つは、winsock APIに存在することです。さらに、Linuxでは、select()はスリープしていない時間を反映するようにタイムアウトを変更します。他のほとんどの実装はこれをしません。 (POSIX.1ではどちらの動作も許可されています)。
pollとselectの両方にppollとpselectの選択肢があり、イベントの待機中に着信信号を処理できます。そして、それらの両方は、ファイルディスクリプタ(百以上)の膨大な量と遅くなるので、プラットフォーム固有例えば呼び出し、選択するのが賢明だろうepoll
Linux上やkqueue
FreeBSD上では。または非同期API(POSIXのに切り替えるaio
例えばまたはIO完了ポートのような特定の何か)。
コールの選択には、次のプロトタイプがあります。
int select(int nfds, fd_set *readfds, fd_set *writefds,
fd_set *exceptfds, struct timeval *timeout);
fd_set
は、ファイルディスクリプタのビットマスク配列です。
nfds
は、set + 1内のすべてのファイル記述子の最大数です。
選択して作業するスニペット:
fd_set active_fd_set, read_fd_set;
FD_ZERO (&active_fd_set); // set fd_set to zeros
FD_SET (sock, &active_fd_set); // add sock to the set
// # define FD_SETSIZE sock + 1
while (1) {
/* Block until input arrives on one or more active sockets. */
read_fd_set = active_fd_set; // read_fd_set gets overriden each time
if (select (FD_SETSIZE, &read_fd_set, NULL, NULL, NULL) < 0) {
// handle error
}
// Service all file descriptors with input pending.
for (i = 0; i < FD_SETSIZE; ++i) {
if (FD_ISSET (i, &read_fd_set)) {
// there is data for i
}
}
ほとんどのPOSIX実装では、ディスク上のファイルに関連するファイル記述子がブロックされていることに注意してください。このファイルがwritefds
に設定されていても、ファイルに書き込むと、すべてのバイトがディスクにダンプされなくなるまでブロックされます