日本免费全黄少妇一区二区三区-高清无码一区二区三区四区-欧美中文字幕日韩在线观看-国产福利诱惑在线网站-国产中文字幕一区在线-亚洲欧美精品日韩一区-久久国产精品国产精品国产-国产精久久久久久一区二区三区-欧美亚洲国产精品久久久久

無法連接到internet怎么解決 無法連接到internet( 二 )


當然還有個聰明的辦法,我們可以每 accept 一個客戶端連接后,將這個文件描述符(connfd)放到一個數(shù)組里 。
fdlist.add(connfd);
然后弄一個新的線程去不斷遍歷這個數(shù)組,調用每一個元素的非阻塞 read 方法 。
while(1) {
  for(fd <– fdlist) {
    if(read(fd) != -1) {
      doSomeThing();
    }
  }
}
這樣,我們就成功用一個線程處理了多個客戶端連接 。
你是不是覺得這有些多路復用的意思?
但這和我們用多線程去將阻塞 IO 改造成看起來是非阻塞 IO 一樣,這種遍歷方式也只是我們用戶自己想出的小把戲,每次遍歷遇到 read 返回 -1 時仍然是一次浪費資源的系統(tǒng)調用 。
在 while 循環(huán)里做系統(tǒng)調用,就好比你做分布式項目時在 while 里做 rpc 請求一樣,是不劃算的 。
所以,還是得懇請操作系統(tǒng)老大,提供給我們一個有這樣效果的函數(shù),我們將一批文件描述符通過一次系統(tǒng)調用傳給內核,由內核層去遍歷,才能真正解決這個問題 。
select
select 是操作系統(tǒng)提供的系統(tǒng)調用函數(shù),通過它,我們可以把一個文件描述符的數(shù)組發(fā)給操作系統(tǒng),讓操作系統(tǒng)去遍歷,確定哪個文件描述符可以讀寫,然后告訴我們去處理:
select系統(tǒng)調用的函數(shù)定義如下 。
int select(
    int nfds,
    fd_set *readfds,
    fd_set *writefds,
    fd_set *exceptfds,
    struct timeval *timeout);
// nfds:監(jiān)控的文件描述符集里最大文件描述符加1
// readfds:監(jiān)控有讀數(shù)據(jù)到達文件描述符集合,傳入傳出參數(shù)
// writefds:監(jiān)控寫數(shù)據(jù)到達文件描述符集合,傳入傳出參數(shù)
// exceptfds:監(jiān)控異常發(fā)生達文件描述符集合, 傳入傳出參數(shù)
// timeout:定時阻塞監(jiān)控時間,3種情況
//  1.NULL,永遠等下去
//  2.設置timeval,等待固定時間
//  3.設置timeval里時間均為0,檢查描述字后立即返回,輪詢
服務端代碼,這樣來寫 。
首先一個線程不斷接受客戶端連接,并把 socket 文件描述符放到一個 list 里 。
while(1) {
  connfd = accept(listenfd);
  fcntl(connfd, F_SETFL, O_NONBLOCK);
  fdlist.add(connfd);
}
然后,另一個線程不再自己遍歷,而是調用 select,將這批文件描述符 list 交給操作系統(tǒng)去遍歷 。
while(1) {
  // 把一堆文件描述符 list 傳給 select 函數(shù)
  // 有已就緒的文件描述符就返回,nready 表示有多少個就緒的
  nready = select(list);
  …
}
不過,當 select 函數(shù)返回后,用戶依然需要遍歷剛剛提交給操作系統(tǒng)的 list 。
只不過,操作系統(tǒng)會將準備就緒的文件描述符做上標識,用戶層將不會再有無意義的系統(tǒng)調用開銷 。
while(1) {
  nready = select(list);
  // 用戶層依然要遍歷,只不過少了很多無效的系統(tǒng)調用
  for(fd <– fdlist) {
    if(fd != -1) {
      // 只讀已就緒的文件描述符
      read(fd, buf);
      // 總共只有 nready 個已就緒描述符,不用過多遍歷

推薦閱讀