select
並不是把發生變化的檔案描述符單獨集中到一起,而是通過觀察作為監視物件的fd_set
函式的變化,因此不能避免對所有監視物件的迴圈語句。而且,監視物件變數會發生變化,在呼叫select
函式之前要複製並儲存原有資訊,並在每次呼叫時傳遞新的監視物件資訊。
傳遞新的監視物件資訊是資源消耗的主要原因,因為每次都是向作業系統傳遞監視物件資訊。對程式負擔很大。
「為何要向作業系統傳遞監視物件資訊呢?」
select
需要監視套接字變化,而套接字屬於作業系統所有,因此陷入核心態執行會導致很大的上下文切換負擔。
「僅向作業系統傳遞1次監視物件,監視範圍或內容發生變化時只通知發生變化的事項」,是不是很棒的想法???
linux
支援的方式是epoll
,windows
支援的方式是idcp
。
epoll 優點
epoll 伺服器端實現需要的3個函式
epoll_create
: 建立儲存epoll
檔案描述符的空間(在作業系統中申請)
epoll_ctl
: 向空間註冊(登出)檔案描述符
epoll_wait
: 等待檔案描述符發生變化
宣告足夠大的struct epoll_event
typedef
union epoll_data
epoll_data_t;
epoll_event
結構體陣列後,傳遞給epoll_wait
函式,發生變化的檔案描述符資訊將被填入該陣列。
呼叫該函式時建立的檔案描述符儲存空間稱為「#include
int epoll_create(int size); // 成功返回 epoll 檔案描述符,失敗返回 -1
~ size: epoll 例項的大小
epoll
例程」。函式返回的檔案描述符主要用於區分epoll
例程。
生成epoll
例程後,應在其內部註冊監視物件檔案描述符。
呼叫形式如下:#include
int epoll_ctl(int epfd, int op, int fd, struct epoll_event * event); // 成功返回0, 失敗返回-1
~ epdf: 用於註冊監視物件的epoll例程的檔案描述符
~ op: 用於指定監視物件的新增、刪除和更改等操作
~ fd: 需要註冊的監視物件檔案描述符
~ event: 監視物件的事件型別
epoll_ctl(a, epoll_ctl_add, b, c);
epoll 例程a中註冊檔案描述符b,主要目的是監視引數c中的事件
epoll_ctl(a, epoll_ctl_del, b, null);
從epoll例程a中刪除檔案描述符b
epoll_ctl_mod
:更改註冊的檔案描述符的關注事件發生情況
epoll_event
結構體用於儲存發生事件的檔案描述符集合。但也可以在epoll
例程中註冊檔案描述符時,用於註冊關注的事件。
上述**將struct epoll_event event;
...event.events = epollin; // 發生需要讀取資料的情況時
event.data.fd = sockfd;
epoll_ctl(epfd, epoll_ctl_add, sockfd, &event);
...
sockfd
註冊到epoll
例程epfd
中,並在需要讀取資料的情況下產生響應事件。
使用方式如下:#incude
int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout); // 成功返回發生事件的檔案描述符數,失敗返回 -1
~ epdf: epoll 例程檔案描述符
~ events: 儲存發生事件的檔案描述符集合的結構體位址值
~ maxevents: 第二個引數中儲存的最大事件數
~ timeout: 以毫秒為單位的等待時間,-1 表示一直等待
呼叫函式後,返回發生事件的檔案描述符數,同時在第二個引數指向的緩衝中儲存發生事件的檔案描述符集合。int event_cnt;
struct epoll_event * ep_events;
...ep_events = malloc(sizeof(struct epoll_event)*epoll_size);
...enent_cnt = epoll_wait(epfd, ep_events, epoll_size, -1);
...
#include
#include
#include
#include
#include
#include
#include
#define buf_size 100
#define epoll_size 50
void error_handling(char *bug);
int main(int argc, char *argv)
serv_sock = socket(pf_inet, sock_stream, 0); // 伺服器端套接字
memset(&serv_adr, 0, sizeof(serv_adr));
serv_adr.sin_family = af_inet;
serv_adr.sin_addr.s_addr = htonl(inaddr_any);
serv_adr.sin_port = htons(atoi(argv[1]));
if(bind(serv_sock, (struct sockaddr*)&serv_adr, sizeof(serv_adr)) == -1) // 繫結
error_handling("bind() error");
if(listen(serv_sock, 5) == -1) // 監聽
error_handling("listen() error");
epfd = epoll_create(epoll_size); // 建立epoll例程空間, 返回例程空間的檔案描述符
ep_events = malloc(sizeof(struct epoll_event) * epoll_size); // 申請空間給epoll_wait函式,發生事件的檔案描述符將寫入這個陣列中。陣列大小(50)和之前建立例程空間的大小相同
event.events = epollin;
event.data.fd = serv_sock;
epoll_ctl(epfd, epoll_ctl_add, serv_sock, &event); // 將檔案描述符serv_sock新增到epoll例程epfd中,監視serv_sock的epollin事件
while(1)
for(i=0; i// 這裡要區分是伺服器端套接字還是客戶端套接字發生事件
// 伺服器端發生事件那麼要呼叫 accept 函式生成新的客戶端套接字
// 客戶端發生事件那麼會是發生了讀寫請求,後面還會再區分一次
// epoll 例程監視所有的套接字,只能迴圈一遍才能發現發生事件的
// 套接字是不是伺服器端套接字
if(ep_events[i].data.fd == serv_sock) // 發生事件的是伺服器端套接字
else
// 發生事件的是客戶端套接字,但是還要區分是讀還是寫
else
// 讀取到了資料,將資料回顯
}} // end for
} // end while
close(serv_sock);
close(epfd); // epoll 例程空間也是檔案描述符
return0;}
void error_handling(char *buf)
TCP IP網路程式設計
tcp ip 是供已連線網際網路的計算機進行通訊的通訊協議。tcp ip 定義了裝置 並非只有計算機 如何連入網際網路,以及資料如何在它們之間傳輸的標準。ip internet protocol 網際網路協議。從這個名稱我們就可以知道ip協議的重要性。在現實生活中,我們進行貨物運輸時都是把貨物包裝成...
TCP IP網路程式設計
套接字 傳輸網路資料的軟體裝置。tcp的特點 可靠的 按序傳遞的 基於位元組的面向連線的資料傳輸方的協議。傳輸過程中資料不會消失,按序傳輸資料,傳輸的資料不存在資料邊界。udp的特點 不可靠的 無序的 以資料高速傳輸為目的的協議。強調快速傳輸而非傳輸順序,傳輸的資料可能丟失也可能損壞,傳輸的資料有資...
網路程式設計學習 tcp ip程式設計
tcp 不記錄訊息邊界 udp 記錄訊息邊界 用到的結構體 struct sockaddr un 例子 struct sockaddr un serveraddr serveraddr.sun family af unix server socket strcpy serveraddr.sun pa...