ノンブロッキング ソケットの特性をテストするための簡単な C/S アプリケーションを作成しました。サーバーとクライアントに関する簡単な情報を以下に示します。
//On linux The server thread will send
//a file to the client using non-blocking socket
void *SendFileThread(void *param){
CFile* theFile = (CFile*) param;
int sockfd = theFile->GetSocket();
set_non_blocking(sockfd);
set_sock_sndbuf(sockfd, 1024 * 64); //set the send buffer to 64K
//get the total packets count of target file
int PacketCOunt = theFile->GetFilePacketsCount();
int CurrPacket = 0;
while (CurrPacket < PacketCount){
char buffer[512];
int len = 0;
//get packet data by packet no.
GetPacketData(currPacket, buffer, len);
//send_non_blocking_sock_data will loop and send
//data into buffer of sockfd until there is error
int ret = send_non_blocking_sock_data(sockfd, buffer, len);
if (ret < 0 && errno == EAGAIN){
continue;
} else if (ret < 0 || ret == 0 ){
break;
} else {
currPacket++;
}
......
}
}
//On windows, the client thread will do something like below
//to receive the file data sent by the server via block socket
void *RecvFileThread(void *param){
int sockfd = (int) param; //blocking socket
set_sock_rcvbuf(sockfd, 1024 * 256); //set the send buffer to 256
while (1){
struct timeval timeout;
timeout.tv_sec = 1;
timeout.tv_usec = 0;
fd_set rds;
FD_ZERO(&rds);
FD_SET(sockfd, &rds)'
//actually, the first parameter of select() is
//ignored on windows, though on linux this parameter
//should be (maximum socket value + 1)
int ret = select(sockfd + 1, &rds, NULL, NULL, &timeout );
if (ret == 0){
// log that timer expires
CLogger::log("RecvFileThread---Calling select() timeouts\n");
} else if (ret) {
//log the number of data it received
int ret = 0;
char buffer[1024 * 256];
int len = recv(sockfd, buffer, sizeof(buffer), 0);
// handle error
process_tcp_data(buffer, len);
} else {
//handle and break;
break;
}
}
}
私が驚いたのは、サーバー スレッドがソケット バッファがいっぱいであるために頻繁に失敗することです。たとえば、14M サイズのファイルを送信すると、errno = EAGAIN で 50000 回の失敗が報告されます。ただし、ロギングを介して、転送中に数十回のタイムアウトがあることがわかりました。フローは次のようになります。
- N 回目のループでは、select() が成功し、256K のデータを正常に読み取ります。
- (N+1) 番目のループで、select() がタイムアウトで失敗しました。
- (N+2) 番目のループで select() が成功し、256K のデータを正常に読み取ります。
受信中にタイムアウトがインターリーブされるのはなぜですか? 誰でもこの現象を説明できますか?
[更新]
1. 14M のファイルをサーバーにアップロードするのに 8 秒しかかかりません
2. 1) と同じファイルを使用すると、サーバーはすべてのデータをクライアントに送信するのに約 30 秒かかります。
3. クライアントが使用するすべてのソケットがブロックされています。サーバーが使用するすべてのソケットはノンブロッキングです。
#2 に関しては、#2 が #1 よりもはるかに時間がかかる理由はタイムアウトであると思います。また、クライアントがデータの受信でビジー状態のときに、なぜこれほど多くのタイムアウトが発生するのだろうかと思います。
[UPDATE2]
@Duck、@ebrob、@EJP、@ja_mesa からのコメントに感謝します。今日はさらに調査を行い、この投稿を更新します。
サーバー スレッドでループごとに 512 バイトを送信する理由については、サーバー スレッドがデータを受信するクライアント スレッドよりもはるかに高速にデータを送信することがわかったためです。クライアントスレッドでタイムアウトが発生した理由について、私は非常に混乱しています。