TCP IP 網路程式設計(五)

2021-06-29 04:36:28 字數 4361 閱讀 9888

select並不是把發生變化的檔案描述符單獨集中到一起,而是通過觀察作為監視物件的fd_set函式的變化,因此不能避免對所有監視物件的迴圈語句。而且,監視物件變數會發生變化,在呼叫select函式之前要複製並儲存原有資訊,並在每次呼叫時傳遞新的監視物件資訊

傳遞新的監視物件資訊是資源消耗的主要原因,因為每次都是向作業系統傳遞監視物件資訊。對程式負擔很大。

「為何要向作業系統傳遞監視物件資訊呢?」

select需要監視套接字變化,而套接字屬於作業系統所有,因此陷入核心態執行會導致很大的上下文切換負擔。

「僅向作業系統傳遞1次監視物件,監視範圍或內容發生變化時只通知發生變化的事項」,是不是很棒的想法???

linux支援的方式是epollwindows支援的方式是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...