kqueueから期待される動作なのか、それとも私が間違っているのかわからないという問題が発生しています。
単一のソケットファイル記述子に対して、kqueueを使用して個別のイベントをインストールする必要があります。個別のイベントは、(sockfd、EVFILT_READ)と(sockfd、EVFILT_WRITE)のイベントです。ただし、後でkqueueからイベントをプルすると、kqueueから2つのイベントが取得されますが、両方のイベントに(event.filter&EVFILT_READ)と(event.filter&EVFILT_WRITE)が含まれています。
それらを分離する必要があります。そうしないと、どのイベントが実際に準備ができているか、読み取りまたは書き込みができるかわかりません。
これは、問題を説明するためにまとめたサンプルコードです。これは、各イベントで両方のフィルターフラグを取得する理由を理解するための単なるテストコードであることに注意してください。
int main(int argc, char ** argv) {
//listening_sockfd = setup listening socket
//setup kqueue and vars
int kq = kqueue();
int res = 0;
int bufres = 0;
struct timespec ts = {0};
struct timespec * tsp = &ts;
struct kevent ke2[2];
//install read for listening socket
struct kevent ke1;
bzero(&ke1,sizeof(ke1));
ke1.ident = listening_sockfd;
ke1.flags = EV_ADD;
ke1.filter = EVFILT_READ;
res = kevent(kq,&ke1,1,NULL,0,tsp);
while(1) {
new_client:
//wait for a client to connect
res = kevent(kq,NULL,0,&ke1,1,NULL);
if(res < 0) printf("%s\n", strerror(errno));
if(res > 0) {
//accept the client
int clientfd = accept(skinfo.sockfd,NULL,NULL);
//install read events for client
bzero(&ke1,sizeof(ke1));
ke1.ident = clientfd;
ke1.flags = EV_ADD;
ke1.filter = EVFILT_READ;
res = kevent(kq,&ke1,1,NULL,0,tsp);
if(res < 0) printf("%s\n",strerror(errno));
while(1) {
//wait for readable content from the client
bzero(&ke1,sizeof(ke1));
res = kevent(kq,NULL,0,&ke1,1,NULL);
if(res < 0) printf("%s\n",strerror(errno));
if(res > 0) {
//check for client disconnect
if(ke1.flags & EV_EOF) {
close(clientfd);
goto new_client;
}
//now install write events for client
bzero(&ke1,sizeof(ke1));
ke1.ident = clientfd;
ke1.flags = EV_ADD;
ke1.filter = EVFILT_WRITE;
res = kevent(kq,&ke1,1,NULL,0,tsp);
if(res < 0) printf("%s\n",strerror(errno));
//now wait for it to be writable - this will return
//immediately because the socket is writable.
res = kevent(kq,NULL,0,ke2,2,NULL);
if(res < 0) printf("%s\n",strerror(errno));
if(res >= 0) {
char buf[128];
printf("res: %i\n",res);
//we have two events from kqueue because I installed
//two (ident,filter) pairs.
int i = 0;
for(i; i<2; i++) {
printf("ident: %i\n",ke2[i].ident);
printf("filter[%i] %lu\n",i,ke2[i].filter);
printf("data: %lu\n",ke2[i].data);
//QUESTION: Why does EVFILT_READ && EVFILT_WRITE come
//back in the same event when I installed two seperate
//(ident,filter) pairs?
if(ke2[i].filter & EVFILT_READ) printf("EVFILT_READ\n");
if(ke2[i].filter & EVFILT_WRITE) printf("EVFILT_WRITE\n");
if(ke2[i].filter & EVFILT_READ) {
printf("readable!\n");
bufres = read(clientfd,buf,128);
}
if(ke2[i].filter & EVFILT_WRITE) {
printf("writable\n");
//shut off write events to stop infinite loop
//because the socket is writable
bzero(&ke1,sizeof(ke1));
ke1.ident = clientfd;
ke1.flags = EV_DELETE;
ke1.filter = EVFILT_WRITE;
res = kevent(kq,&ke1,1,NULL,0,tsp);
write(clientfd,buf,bufres);
}
}
}
}
}
}
}
}
私はこれを理解することができませんでした。別々のイベントをインストールしたときに、各kqueueイベントにEVFILT_READとEVFILT_WRITEの両方が含まれるのはなぜですか?
これが私に引き起こしている本当の問題は、これらのイベントは常にEVFILT_READを報告するため、読み取りが利用可能であると常に考えているコードの副作用がありますが、実際には存在しないため、read()の呼び出しは失敗し、その他の結果。このコードはこれらの結果を示していないことに注意してください。これは私が書いた問題のサンプルにすぎないので、これを理解して実際のコードを続行します。
何か案は?
PSprintfからの出力は次のとおりです。
res: 2
ident: 5
filter[0] 4294967295
data: 5
EVFILT_READ
EVFILT_WRITE
readable!
writable
ident: 5
filter[1] 4294967294
data: 146988
EVFILT_READ
EVFILT_WRITE
readable!
writable