自己編寫簡單的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的數據。
爲什麼在接收過程中會發生交錯的超時?誰能解釋這種現象?
[UPDATE]
1.上傳14M的文件到服務器只需要8秒
2.使用與1相同的文件)時,服務器需要近30秒的所有數據發送到客戶端。
3.客戶端使用的所有套接字都被阻塞。服務器使用的所有套接字都是非阻塞的。
關於#2,我認爲超時是#2需要更多時間的原因,我想知道爲什麼當客戶端忙於接收數據時會有這麼多超時。
[UPDATE2]
感謝來自@Duck,@ebrob,@EJP,@ja_mesa的意見,我會做更多的調查今天 然後更新這個帖子。
關於爲什麼我在服務器線程中發送每個循環512字節,這是因爲我發現服務器線程發送數據的速度比接收它們的客戶端線程快得多。我很困惑,爲什麼超時發生在客戶端線程。
爲什麼'GetFilePacketsCount()'與服務器端的'CurrPacket'有任何關係?不是512字節的緩衝區長度有點任意嗎?另外,在服務器端,似乎你會得到一大堆'EAGAIN',但是當你正確處理它們時應該沒問題。也許在EAGAIN上睡某種東西會是個好主意?等待它看起來像GetPacketData消耗數據,所以你可能通過多次調用一個'EAGAIN'來創建空隙? – 2013-07-01 14:06:49
你如何聲明/處理'rds'?我想你需要FD_SET/FD_ZERO每次循環,在你打電話之前select(...) – 2013-07-01 14:08:57
@ebyrob謝謝,我更新了源代碼。令我感到驚訝的是,在客戶端線程忙於接收數據期間,選擇調用發生超時,並且服務器線程報告了數千個EAGAIN失敗! – Steve