【ネットワーク】(八)I/O多重化--Select(二)
第7篇の文章の中でいくつかのIO模型を紹介して、select関数を使ってクライアントのコードを改善して、この文章はそれを使ってサービス側のコードを改善します!
select関数で実装されたサーバプログラムは、同時サーバと呼ばれ、タスクをキューに並べて処理しているため、これらのイベントを並列に処理できません.
1、selectイベント発生条件
0.1読み取り可能なイベントセットのインタフェースバッファにはデータが読み取り可能である. 接続のリード半分が閉じる、すなわち、FINセグメントが受信されると、読み取り可能であり、読み取り動作は0 に戻る.リスニングセットインタフェースであれば、完了したキューが空でない場合、読み取り可能である.新しい接続要求 がある.インタフェースでgetsockopt関数でSO_を指定できるエラーが発生しました.ERRORオプションは、 を取得するために使用される
0.2書き込み可能なイベントセットインタフェース送信バッファは、データ を収容する空間を有する.接続の書き込みの半分が閉じる、すなわちRSTセグメントが受信された後に書き込み可能であり、write動作を再び呼び出すことでSIGPIPE信号の生成 をもたらす.インタフェースでgetsockopt関数でSO_を指定できるエラーが発生しました.ERRORオプションは、 を取得するために使用される
0.3例外イベントソケットインタフェースは帯域外データ が存在する.
2、select同時サーバー実現
select関数を使用して、すべてのイベントを単一プロセスで処理し、同時サーバを実現します.
クライアント・プログラムは「第7編」と同じで、コンパイル・コマンドは次のとおりです.
サービス・エンド・フル・プログラム
コンパイルコマンド:gcc-Wall-g-std=c 99 server.c -o server
select関数で実装されたサーバプログラムは、同時サーバと呼ばれ、タスクをキューに並べて処理しているため、これらのイベントを並列に処理できません.
1、selectイベント発生条件
0.1読み取り可能なイベント
0.2書き込み可能なイベント
0.3例外イベント
2、select同時サーバー実現
select関数を使用して、すべてのイベントを単一プロセスで処理し、同時サーバを実現します.
クライアント・プログラムは「第7編」と同じで、コンパイル・コマンドは次のとおりです.
gcc -Wall -g -std=gnu99 client.c -o client
サービス・エンド・フル・プログラム
コンパイルコマンド:gcc-Wall-g-std=c 99 server.c -o server
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define handle_error(msg) \
do{perror(msg);exit(EXIT_FAILURE);}while(0)
ssize_t readn(int fd, void *buf, size_t count)
{
if((fd < 0) || (buf == NULL) || (count < 0))
return -1;
size_t nleft = count; //
ssize_t nread = 0; //
char *pbuf = (char*)buf;
while(nleft > 0)
{
if((nread = read(fd, pbuf, nleft)) < 0)
{
if(errno == EINTR)
continue;
return -1;
}
else if (nread == 0)
return count - nleft;
pbuf += nread;
nleft -= nread;
}
return count;
}
ssize_t writen(int fd, const void *buf, size_t count)
{
if((fd < 0) || (buf == NULL) || (count < 0))
return -1;
size_t nleft = count; //
ssize_t nwritten = 0; //
char *pbuf = (char*)buf;
while (nleft > 0)
{
if((nwritten = write(fd, pbuf, nleft)) < 0)
{
if(errno == EINTR)
continue;
return -1;
}
else if(nwritten == 0)
continue;
pbuf += nwritten;
nleft -= nwritten;
}
return count;
}
// recv ,
ssize_t recv_peek(int sockfd, void *buf, size_t len)
{
while(true)
{
int iret = recv(sockfd, buf, len, MSG_PEEK);
if(iret == -1 && errno == EINTR) // ,
continue;
return iret;
}
}
//
, ,
ssize_t recvline(int sockfd, void *buf, size_t maxlen)
{
int iret = 0;
int nread = 0; //
char *pbuf = (char*)buf;
int nleft = maxlen; //
while(true)
{
iret = recv_peek(sockfd, pbuf, nleft);
if(iret < 0)
return iret; //
else if(iret == 0)
return iret; //
nread = iret;
if(nread > nleft) //
exit(EXIT_FAILURE);
for(int i = 0; i < nread; i++)
{
if(pbuf[i] == '
')
{
iret = readn(sockfd, pbuf, i+1); //
if(iret != i+1)
exit(EXIT_FAILURE); // i+1 ,
return iret; //
}
}
//
, ,
nleft -= nread;
iret = readn(sockfd, pbuf, nread);
if(iret != nread)
exit(EXIT_FAILURE);
pbuf += nread;
}
return -1;
}
int main(void)
{
int sk_fd = socket(AF_INET, SOCK_STREAM , IPPROTO_TCP);
if(sk_fd < 0)
handle_error("socket");
// REUSEADDR, TIME_WAIT ,
int on = 1;
if(setsockopt(sk_fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0)
{
close(sk_fd);
handle_error("setsockopt");
}
struct sockaddr_in sr_addr;
memset(&sr_addr,0,sizeof(sr_addr));
sr_addr.sin_family = AF_INET;
sr_addr.sin_port = htons(5188);
sr_addr.sin_addr.s_addr = htonl(INADDR_ANY);
//sr_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
//inet_aton("127.0.0.1",&sr_addr.sin_addr);
if(bind(sk_fd, (struct sockaddr*)&sr_addr, sizeof(sr_addr)) < 0)
{
close(sk_fd);
handle_error("bind");
}
//
if(listen(sk_fd, SOMAXCONN) < 0) // SOMAXCONN
{
close(sk_fd);
handle_error("listen");
}
// select
fd_set readset;
fd_set allset;
FD_ZERO(&readset);
FD_ZERO(&allset);
FD_SET(sk_fd, &allset); // ,
int maxfd = sk_fd; //
int maxfd_last = maxfd; // maxfd ,
int nready = 0;
int maxindex = 0; //client_sk
int maxindex_last = 0; //maxindex
int client_sk[FD_SETSIZE]; //
for(int i = 0; i < FD_SETSIZE; i++)
{
client_sk[i] = -1;
}
while (true)
{
readset = allset;
nready = select(maxfd + 1, &readset, NULL, NULL, NULL);
if(nready == -1)
{
if(errno == EINTR) //
continue;
handle_error("select");
}
if(nready == 0) //time out
continue;
if(FD_ISSET(sk_fd, &readset)) //
{
// accept
struct sockaddr_in cl_addr;
socklen_t cl_length = sizeof(cl_addr);
memset(&cl_addr,0,sizeof(cl_addr));
int ac_sk = accept(sk_fd, (struct sockaddr *)&cl_addr, &cl_length);
if(ac_sk < 0)
{
if(errno == EINTR)
continue;
handle_error("accept");
}
//
FD_SET(ac_sk, &allset);
if(ac_sk > maxfd)
{
maxfd_last = maxfd; //
maxfd = ac_sk; //
}
int i;
for(i = 0; i < FD_SETSIZE; i++)
{
if(client_sk[i] < 0)
{
client_sk[i] = ac_sk;
if(maxindex < i)
{
maxindex_last = maxindex; //
maxindex = i; //
}
break;
}
}
if(i == FD_SETSIZE) //client_sk ,
{
fprintf(stderr, "Client too many!");
exit(EXIT_FAILURE);
}
printf("Connect ip = %s\tport = %d
",inet_ntoa(cl_addr.sin_addr),ntohs(cl_addr.sin_port));
if(--nready <= 0) //
continue;
}
//
for(int i = 0; i <= maxindex; i++)
{
int conn_sk = client_sk[i];
if(FD_ISSET(conn_sk, &readset))
{
//
char recvbuf[1024];
memset(recvbuf,0,sizeof(recvbuf));
int iret = recvline(conn_sk,recvbuf,sizeof(recvbuf)); //
if(iret == -1)
handle_error("read");
else if(iret == 0)
{
// ,
if(conn_sk == maxfd)
{
maxfd = maxfd_last; //
}
if(i == maxindex)
{
maxindex = maxindex_last; //
}
client_sk[i] = -1;
FD_CLR(conn_sk, &allset);
printf("Client was closed!
");
close(conn_sk);
}
fputs(recvbuf,stdout);
writen(conn_sk, recvbuf, strlen(recvbuf)); //
if(--nready <= 0) //
break;
}
}
}
for(int i = 0; i <= maxindex; i++)
{
close(client_sk[i]);
}
close(sk_fd);
return 0;
}