這一節主要來說一下如何用select函式來改進我們前面的客戶端-伺服器模型。
前面我們在處理多客戶端模型時,每當連線乙個客戶端時,伺服器端就需要開闢乙個新的程序來處理新的客戶端,這樣就會耗費很大的記憶體資源。
而select函式允許程序指示核心等待多個事件中的任何乙個發生,並只有在有乙個或多個事件事件發生或經歷一段指定的時間後才喚醒它。或者說select具有管理多個i/o的能力,對於多個套介面,一旦某個套介面發生了我們所感興趣的事件,select函式返回,返回值為監測到的事件個數。而且由於select函式的引數是「值—結果」型的,因此我們也知道哪些套介面發生了事件,然後遍歷這些套介面並處理相關事件。
下面給出改進後的回射客戶-伺服器模型的**。
伺服器端:echosrv
#include #include #include #include #include #include #include #include #include #include #include #define err_exit(m) \
do \
while(0)
ssize_t readn(int fd, void* buf, size_t count)
if(nread == 0)
//表示對等方關閉,這裡直接返回
return count-nleft;
nleft -= nread;//每次讀取後剩餘的位元組數
bufp += nread;
} return count;
}ssize_t writen(int fd, void* buf, size_t count)
if(nwritten == 0)
//什麼都沒發生
continue;
nleft -= nwritten;//每次寫後剩餘要寫的位元組數
bufp += nwritten;
} return count;
}ssize_t recv_peek(int sockfd, void* buf, size_t len)
}ssize_t readline(int sockfd, void*buf, size_t maxline)
} //沒有遇到\n
if(nread > nleft)
exit(exit_failure);
//把讀到的資料nread個位元組從緩衝區中移走
nleft -= nread;
ret = readn(sockfd, bufp, nread);
if(ret != nread)
exit(exit_failure);
//繼續下一次的**,需偏移
bufp += nread;
} return -1;
}void echo_srv(int conn)
fputs(recvbuf, stdout);
writen(conn, recvbuf, strlen(recvbuf)); }}
void handle_sigchld(int sig)
int main(void)
else
//父程序進行accept
//不再需要連線套介面了,即conn(父子程序共享檔案描述符)
close(conn);
}//實現乙個回射客戶/伺服器模型
//即客戶端從標準輸入獲取資料,傳送給伺服器端,伺服器端再回射過去
*/ //改為用select實現
int client[fd_setsize];//select最多能處理的事件個數
int i;
for(i = 0; i < fd_setsize; ++i)
client[i] = -1;//初始化,-1表示空閒狀態
int maxi = 0; //最大不空閒位置
int nready;
int maxfd = listenfd;//3
fd_set rset;//定義乙個讀集合
fd_set allset;
fd_zero(&rset);
fd_zero(&allset);
fd_set(listenfd, &allset);//先把監聽套介面加進去
while(1)
if(nready == 0)
continue;
if(fd_isset(listenfd, &rset))
}if(i == fd_setsize)
printf("ip=%s port=%d\n", inet_ntoa(peeraddr.sin_addr), ntohs(peeraddr.sin_port));
fd_set(conn, &allset);
if(conn > maxfd)
maxfd = conn;
if(--nready <= 0)
continue;
} //已連線套介面也可能產生事件
for(i = 0; i < fd_setsize; ++i)
; int ret = readline(conn, recvbuf, 1024);
if(ret == -1)
err_exit("readline error");
if(ret == 0)
fputs(recvbuf, stdout);
writen(conn, recvbuf, strlen(recvbuf));
if(--nready <= 0)
break;
}} }
return 0;
}
客戶端:echocli.c
#include #include #include #include #include #include #include #include #include #include #define err_exit(m) \
do \
while(0)
ssize_t readn(int fd, void* buf, size_t count)
if(nread == 0)//對等方關閉
return count-nleft;
nleft -= nread;
bufp += nread;
} return count;
}ssize_t writen(int fd, void* buf, size_t count)
if(nwritten == 0)
continue;
nleft -= nwritten;
bufp += nwritten;
} return count;
}ssize_t recv_peek(int sockfd, void* buf, size_t len)
}ssize_t readline(int sockfd, void*buf, size_t maxline)
} //沒有遇到\n
if(nread > nleft)
exit(exit_failure);
nleft -= nread;
ret = readn(sockfd, bufp, nread);
if(ret != nread)
exit(exit_failure);
//繼續下一次**
bufp += nread;
} return -1;
}void echo_cli(int sock)
; char recvbuf[1024] = ;
while(fgets(sendbuf, sizeof(sendbuf), stdin) != null)
//顯示出來
fputs(recvbuf, stdout);
//這裡需要清空緩衝區
memset(sendbuf, 0, sizeof(sendbuf));
memset(recvbuf, 0, sizeof(recvbuf));
} close(sock);
*/ //該為select用法
fd_set rset;
fd_zero(&rset);
//檢測標準輸入是否產生了可讀事件
int nready;
int maxfd;
int fd_stdin = fileno(stdin);//標準輸入
char recvbuf[1024] = ;
char sendbuf[1024] = ;
//有兩個檔案描述符,fd_stdin和sock
if(fd_stdin > sock)
maxfd = fd_stdin;
else
maxfd = sock;
while(1)
//顯示出來
fputs(recvbuf, stdout);
memset(recvbuf, 0, sizeof(recvbuf));
} if(fd_isset(fd_stdin, &rset))
}close(sock);
}void handle_sigpipe(int sig)
int main(void)
說明:fd_setsize是系統指定的select所能監聽的事件的最大值,一般為1024。
回射客戶 伺服器模型(1)
最近在學習socket程式設計,根據自己的學習過程及學習筆記,下面來梳理一下如何實現乙個簡單的回射客戶 伺服器模型,也藉此來熟悉一下socket bind listen accept connect這些函式的使用。簡單的回射客戶 伺服器模型 下面先看一下乙個客戶 伺服器模型的框架圖。可以看到,伺服器...
system v訊息佇列實現回射客戶 伺服器
可能存在死鎖狀態 當客戶端傳送資料給伺服器,伺服器回射訊息時,訊息佇列被多個客戶端同時傳送的大量訊息填滿,伺服器無法回射,形成死鎖 server.c include include include include include include include include define err ...
Socket實現「回射客戶端 伺服器」功能
tcp客戶 伺服器模型 伺服器端 回射客戶 伺服器應用程式 功能 客戶端輸入字元,傳送給伺服器,伺服器不對該字串做任何處理,又反回客戶端 伺服器端函式 include include include include includeint main struct sockaddr in servadd...