Unix域协议
Unix域協議
?? ?socket套接字也可以實現一臺主機上的不同進程間通信。
?? ?socket--->IPC手段
?? ?
?? ?socket實現進程間通信用到了另外一個協議:
?? ??? ?Unix域協議,簡稱AF_UNIX或AF_LOCAL
?? ??? ?
?? ??? ?Unix與協議提供了兩類套接字:
?? ??? ??? ?SOCK_STREAM?? ??? ?字節流套接字(類似TCP)
?? ??? ??? ?SOCK_DGRAM?? ??? ?數據報套接字(類似UDP)
?? ??? ?
?? ??? ?雖然UDP協議是不可靠的,但是Unix域數據報服務是可靠的。
?? ??? ?不會丟失消息,也不會傳遞出錯,因為本質上就是在同一臺主機上面進行通信。
?? ??? ?
?? ??? ?Unix域協議的區別:
?? ??? ??? ?1.?? ?和TCP/UDP相比,速度更快,數據不需要傳遞到主機外,也不需要封包和拆包的過程。
?? ??? ??? ?2.?? ?相對于IP協議來講,IP協議是通過IP地址+端口號標識客戶端和服務器
?? ??? ??? ??? ?Unix域協議是使用普通文件系統中的路徑名標識客戶端和服務器。
?? ??? ??? ??? ?
?? ??? ?Unix域協議大部分的編程流程和函數API結構都與TCP類似,
?? ??? ?只不過Unix域協議使用路徑名描述一個地址
?? ??? ?
?? ??? ?//
?? ??? ?#include <sys/un.h>
?? ??? ?define UNIX_PATH_MAX 108
?? ??? ?
?? ??? ?//Unix域協議的地址結構體
?? ??? ?struct sockaddr_un
?? ??? ?{
?? ??? ??? ?sa_familt_t sun_family;//AF_UNIX/AF_LOCAL
?? ??? ??? ?char sun_path[UNIX_PATH_MAX];//Unix域協議地址,是以'\0'為結尾的本地文件系統
?? ??? ??? ??? ??? ??? ??? ??? ?//以絕對路徑形式存在。“/home/china/long”
?? ??? ?};
?? ??? ?
?? ??? ?Unix域協議字節流套接字編程方法(類似于TCP編程):
?? ??? ??? ?Server:先創建套接字---> bind ---> 監聽 ---> 接收客戶端的連接請求 ---> 連接成功
?? ??? ??? ??? ??? ?---> read/write ---> close
?? ??? ??? ?
?? ??? ??? ?Client:?? ?先創建套接字 ---> connect ---> read/write ---> close
?? ??? ??? ?
?? ??? ??? ?
?? ??? ?做一個DNS客戶端向DNS服務器提交請求解析某一個某一個域名對應的IP地址。
?? ??? ?
五、IO模型
?? ?IO模型---->輸入輸出模型
?? ?
?? ?可以把IO(輸入輸出)理解為兩個步驟:
?? ?1.?? ?等待IO事件就緒。
?? ??? ?如果可以讀,我們等待緩沖區內有數據就可以讀,如果可以寫,我們就等到緩沖區內有空間
?? ??? ?可以寫。
?? ?2.?? ?第二步才是真正意義上的數據的遷移。
?? ??? ?涉及到內核態和用戶態的切換。
?? ??? ?
?? ?
?? ?Linux中五種IO模型:
?? ?1)?? ?阻塞IO
?? ??? ?讀:如果有數據(即使數據少于你要讀取的字節數),直接返回數據
?? ??? ??? ?如果沒有數據,則阻塞(等待IO事件就緒),直到有數據或者出錯
?? ??? ?
?? ??? ?寫:如果有空間(即使空間少于你要寫的字節數)可以寫,直接寫入
?? ??? ??? ?如果沒有空間讓你寫,則阻塞直到有空間寫或者出錯。
?? ??? ??? ?
?? ??? ?這種模型是最常見的,最簡單的,效率最低的一種IO模型,是默認的IO模型
?? ??? ?
?? ??? ??? ?read/write/sendto/recvform....都是屬于阻塞IO。
?? ?
?? ?2)?? ?非阻塞IO
?? ??? ?能讀(有數據)就讀,不能讀(沒有數據)就立即返回一個錯誤碼。
?? ??? ?能寫(有空間)就寫,不能寫(沒有空間)就立即返回一個錯誤碼。
?? ??? ?
?? ??? ?雖然這種不會阻塞,但是有一個缺點,這種方式相對于阻塞IO可能會更加效率低。
?? ??? ?會浪費掉大量的CPU。
?? ??? ?
?? ??? ?非阻塞IO采用的方式有點類似于輪詢,所以效率可能會更低。
?? ?
?? ?3)?? ?IO多路復用
?? ??? ?允許同時對多個IO事件進行控制,同時監控多個“文件描述符”。
?? ??? ?
?? ??? ?IO多路復用實現的機制是通過select/poll/epoll函數來實現的。
?? ??? ?
?? ??? ?
?? ?4)?? ?信號驅動IO
?? ??? ?如果有IO事件就緒了,就可以發一個信號給應用程序,進行數據的處理。
?? ??? ?相當于注冊了一個信號處理函數,當數據準備好了,就給我發一個信號
?? ??? ?當我捕捉到這個信號之后,就去我的信號處理函數中去獲取數據即可。
?? ??? ?所以信號驅動IO是一種非阻塞IO。
?? ??? ?
?? ?5)?? ?異步IO
?? ??? ?應用的較少。
?? ??? ?
?? ??? ?進程僅僅是發起對數據的請求,而數據的監控和遷移由別的進程來完成。
?? ??? ?
?? ??? ?從阻塞程度上來說:
?? ??? ??? ?阻塞IO > 非阻塞IO > 多路復用IO > 信號驅動IO > 異步IO
?? ??? ??? ?
?? ?1.?? ?文件的讀寫方式(阻塞/非阻塞)的改變
?? ??? ?文件的默認讀寫方式是阻塞。
?? ??? ?
?? ??? ?在open的時候加上O_NOBLOCK選項,則可以以非阻塞的形式打開文件。
?? ??? ?
?? ??? ?通過fcntl函數去改變一個已經打開的文件的文件性質的。
?? ??? ?
?? ??? ?NAME
?? ??? ??? ?fcntl - manipulate file descriptor
?? ??? ?SYNOPSIS
?? ??? ??? ?#include <unistd.h>
?? ??? ??? ?#include <fcntl.h>
?? ??? ??? ?int fcntl(int fd, int cmd, ... /* arg */ );
?? ??? ??? ?fcntl改變文件的狀態,具體的操作由命令號cmd來決定。
?? ??? ??? ?
?? ??? ??? ?fcntl有五種功能:
?? ??? ??? ?1.?? ?復制一個現有的文件描述符(cmd == F_DUPFD)
?? ??? ??? ??? ?讓多個文件描述符指向同一個文件。
?? ??? ??? ??? ?復制一個現有的文件描述符fd,新的文件描述符作為函數的返回值返回。
?? ??? ??? ??? ?比如:
?? ??? ??? ??? ??? ?int new_fd = fcntl(old_fd,F_DUPFD,4);
?? ??? ??? ??? ??? ?第三個參數表示新的文件描述符的最小值。
?? ??? ??? ??? ??? ?如果如果4被占用了,那么新的文件描述符就+1,直到找到一個沒有被占用。
?? ??? ??? ??? ??? ?
?? ??? ??? ?2.?? ?設置/獲取文件描述符標志(cmd == F_GETFD/F_SETFD)
?? ??? ??? ??? ?cmd == F_GETFD,第三個參數不需要設置
?? ??? ??? ??? ??? ?作用是取得與文件描述符關聯的狀態標志,目前與文件描述符關聯的
?? ??? ??? ??? ??? ?狀態標志只有一個close_on_exec(FD_CLOEXEC).
?? ??? ??? ??? ??? ?
?? ??? ??? ??? ??? ?FD_CLOEXEC的作用是:
?? ??? ??? ??? ??? ??? ?是一個進程所有的文件描述符的位圖標志,每一個比特代表一個打開的
?? ??? ??? ??? ??? ??? ?文件描述符,用于確定在調用exec時需要關閉的文件描述符。
?? ??
?? ??? ??? ??? ??? ??? ?如果對應的標志位置為1,則子進程在執行exec時該描述符將會被關閉
?? ??? ??? ??? ??? ??? ?如果對應的標志位置為0,則子進程在執行exec時該描述符將會維持被打開的狀態
?? ??? ??? ??? ??? ??? ?
?? ??? ??? ??? ?cmd == F_SETFD,第三個參數為新的文件描述符的狀態標記
?? ??? ??? ??? ??? ?
?? ??? ??? ?3.?? ?獲取/設置文件狀態標志(cmd == F_GETFL/F_SETFL)?? ?
?? ??? ??? ??? ?F_GETFL?? ?:
?? ??? ??? ??? ??? ?第三個參數不需要設置
?? ??? ??? ??? ??? ?
?? ??? ??? ??? ??? ?文件所有的狀態標記作為返回值返回
?? ??? ??? ??? ??? ?文件的狀態標記有:
?? ??? ??? ??? ??? ??? ?O_RDONLY/O_WRONLY/O_RDWR/O_APPEND/O_NOBLOCK.....
?? ??? ??? ??? ??? ?
?? ??? ??? ??? ??? ?這些標記,保存在一個struct file結構體的一個成員變量中:
?? ??? ??? ??? ??? ??? ?unsigned long f_flags;
?? ??? ??? ??? ??? ??? ?
?? ??? ??? ??? ??? ?是通過位域來實現,各個狀態只是變量中的某一位。
?? ??? ??? ??? ??? ?
?? ??? ??? ??? ??? ?
?? ??? ??? ??? ?F_SETFL:新的文件狀態標志按照第三個參數設置
?? ??? ??? ??? ??? ?例子:
?? ??? ??? ??? ??? ??? ?判斷一個打開的文件,是否為非阻塞
?? ??? ??? ??? ??? ??? ?unsigned long f_flags = fcntl(fd,F_GETFL);
?? ??? ??? ??? ??? ??? ?if(f_flags & O_NOBLOCK)
?? ??? ??? ??? ??? ??? ?{
?? ??? ??? ??? ??? ??? ??? ?//文件是阻塞的
?? ??? ??? ??? ??? ??? ?}
?? ??? ??? ??? ??? ??? ?else
?? ??? ??? ??? ??? ??? ?{
?? ??? ??? ??? ??? ??? ??? ?//文件是非阻塞的
?? ??? ??? ??? ??? ??? ?}
?? ??? ??? ??? ??? ?
?? ??? ??? ??? ??? ??? ?設置文件的阻塞方式
?? ??? ??? ??? ??? ??? ?unsigned long f_flags = fcntl(fd,F_GETFL);
?? ??? ??? ??? ??? ??? ?f_flags &= ~O_NOBLOCK;//去掉非阻塞標志,其他標志不動
?? ??? ??? ??? ??? ??? ?fcntl(fd,F_SETFL,f_flags);
?? ??? ??? ??? ??? ??? ?
?? ??? ??? ?4.?? ?獲取/設置文件的屬主進程
?? ??? ??? ?5.?? ?獲取/設置文件記錄鎖
?? ??? ??? ??? ?當一個進程正在修改或者讀取文件的時候,可以阻止另一個進程去訪問文件。
?? ??? ??? ?
?? ?3)?? ?多路復用IO
?? ??? ?用來實現對多個文件描述符進行IO“監聽”。
?? ??? ?一般用在網絡服務器中,可以并發的處理多個客戶端的連接和請求。
?? ??? ?
?? ??? ?1.?? ?select
?? ??? ??? ?實現原理:
?? ??? ??? ?1.?? ?將需要監聽的文件描述符放在一個監聽集合中,將這個集合拷貝到內核中去。
?? ??? ??? ?2.?? ?在內核中創建一個內核線程,有這個線程去輪詢所有的文件描述符,這個線程
?? ??? ??? ??? ?是處于內核空間,所有這個CPU占用的是內核的執行時間,而不是占用用戶的
?? ??? ??? ??? ?執行時間,當一個或者多個文件描述符就緒(可讀了,可寫了,出錯了)時,
?? ??? ??? ??? ?就將就緒的文件描述符集合拷貝到用戶空間中去。
?? ??? ??? ?3.?? ?用戶去處理就緒的文件描述符。
?? ?
?? ??? ? ? ? ? /* According to earlier standards */
?? ??? ??? ??? ?#include <sys/time.h>
?? ??? ??? ??? ?#include <sys/types.h>
?? ??? ??? ??? ?#include <unistd.h>
?? ??? ??? ??? ?int select(int nfds, fd_set *readfds, fd_set *writefds,fd_set *exceptfds, struct timeval *timeout);
?? ??? ??? ??? ?void FD_CLR(int fd, fd_set *set);
?? ??? ??? ??? ??? ?將fd指定的文件描述符從set指定的集合中移除。
?? ??? ??? ??? ?
?? ??? ??? ??? ?int ?FD_ISSET(int fd, fd_set *set);
?? ??? ??? ??? ??? ?判斷fd指定的文件描述符是否存在于set指定的集合中
?? ??? ??? ??? ?
?? ??? ??? ??? ?void FD_SET(int fd, fd_set *set);
?? ??? ??? ??? ??? ?把fd指定的文件描述符加入到set指定的集合中
?? ??? ??? ??? ?
?? ??? ??? ??? ?void FD_ZERO(fd_set *set);
?? ??? ??? ??? ??? ?把set指向的文件描述符集合清空
?? ??? ??? ??? ?select用類型fd_set來表示一個文件描述符的集合,因為你需要監聽多個文件描述符
?? ??? ??? ??? ?需要一個fd_set類型的集合來保存你所有需要監聽的文件描述符。
?? ?
?? ??? ??? ??? ?但是可能有的文件描述符是監聽可讀,有的文件描述符是監聽可寫,
?? ??? ??? ??? ?有的監聽是否出錯,所有,就有三個文件描述符集合分別存儲你要監聽的
?? ??? ??? ??? ?文件描述符。
?? ?
?? ??? ??? ??? ?readfds:監聽是否可讀的文件描述符集合
?? ??? ??? ??? ?writefds:監聽是否可寫的文件描述符集合
?? ??? ??? ??? ?exceptfds:監聽是否出錯的文件描述符集合
?? ??? ??? ??? ?
?? ??? ??? ??? ?參數:
?? ??? ??? ??? ??? ?nfds:指這個集合中所有文件描述符的范圍
?? ??? ??? ??? ??? ??? ?或者說監聽的文件描述符的個數。
?? ??? ??? ??? ??? ??? ?你需要監聽的所有文件描述符中的最大值+1
?? ??? ??? ??? ??? ??? ?內核是從0文件描述符開始輪詢,到你指定的最大的文件描述符。
?? ??? ??? ??? ?
?? ??? ??? ??? ??? ?readfds:監聽可讀的文件描述符集合
?? ??? ??? ??? ??? ??? ?返回的時候,里面保存的是所有的已經可讀的文件描述符
?? ??? ??? ??? ?
?? ??? ??? ??? ??? ?writefds:監聽可寫的文件描述符集合
?? ??? ??? ??? ??? ??? ?返回的時候,里面保存的是所有的已經可寫的文件描述符?? ?
?? ??? ??? ??? ??? ?exceptfds:監聽出錯的文件描述符集合
?? ??? ??? ??? ??? ??? ?返回的時候,里面保存的是所有的已經出錯的文件描述符
?? ??? ??? ??? ??? ??? ?上面三個集合,如果不需要監聽則可指定全部為NULL
?? ??? ??? ??? ??? ?
?? ??? ??? ??? ??? ??? ?select(max_fd,NULL,NULL,NULL,2);//sleep(2)
?? ??? ??? ??? ??? ??? ?select(nfds,&readfds,NULL,NULL,&timeout);
?? ??? ??? ??? ??? ?timeout:超時時間,在指定的時間內還沒有文件描述符就緒,也返回
?? ??? ??? ??? ??? ??? ?struct timeval
?? ??? ??? ??? ??? ??? ?{
?? ??? ??? ??? ??? ??? ??? ?long tv_sec;?? ?//秒
?? ??? ??? ??? ??? ??? ??? ?long tv_usec;?? ?//毫秒
?? ??? ??? ??? ??? ??? ?};
?? ??? ??? ??? ??? ??? ?
?? ??? ??? ??? ??? ??? ?等待分為三種情況:
?? ??? ??? ??? ??? ??? ??? ?1.?? ?如果把這個參數設置為NULL,表示一直等待下去,“死等”
?? ??? ??? ??? ??? ??? ??? ??? ?直到至少有一個文件描述符繼續。
?? ??? ??? ??? ??? ??? ??? ?
?? ??? ??? ??? ??? ??? ??? ?2.?? ?等待一段固定的時間(有timeval參數指定等待多久)。
?? ??? ??? ??? ??? ??? ??? ??? ?在等待的時間內,正常等待,一旦超時,還沒有文件描述符就緒的
?? ??? ??? ??? ??? ??? ??? ??? ?話,就立即返回。同時超時時間全部清0。
?? ??? ??? ??? ??? ??? ??? ??? ?struct timeval timeout;
?? ??? ??? ??? ??? ??? ??? ??? ?timeout.tv_sec = 2;
?? ??? ??? ??? ??? ??? ??? ??? ?timeout.tv_usec = 0;
?? ??? ??? ??? ??? ??? ??? ??? ?select(nfds,&readfds,NULL,NULL,&timeout);//只監聽是否可讀集合2秒
?? ??? ??? ??? ??? ??? ??? ??? ?//tv_sec == 0,tv_usec == 0
?? ??? ??? ??? ??? ??? ??? ?
?? ??? ??? ??? ??? ??? ??? ?3.?? ?根本不等待。
?? ??? ??? ??? ??? ??? ??? ??? ?輪詢一次后立即返回。
?? ??? ??? ??? ??? ??? ??? ??? ?該參數必須指定struct timeval中的成員變量的值都為0。
?? ??? ??? ??? ??? ??? ??? ?
?? ??? ??? ??? ??? ?返回值:
?? ??? ??? ??? ??? ??? ?> 0 ?? ?表示已經就緒的文件描述符的個數
?? ??? ??? ??? ??? ??? ??? ??? ?由于你監聽了多個文件描述符,至于你到底是哪些文件描述符就緒了
?? ??? ??? ??? ??? ??? ??? ??? ?你必須在select返回后,使用FD_ISSET一個一個的去測試。
?? ??? ??? ??? ??? ??? ?== 0 ?? ?超時了,等待的指定的時間到了。
?? ??? ??? ??? ??? ?
?? ??? ??? ??? ??? ??? ?< 0 ?? ?表示select函數出錯了,errno被設置。
?? ??? ??? ??? ??? ??? ?
?? ??? ??? ?簡單的偽代碼邏輯:
?? ??? ??? ??? ?
?? ??? ??? ??? ?fd_set readfds;//需要監聽可讀的文件描述符的集合
?? ??? ??? ??? ?int max_fd = 0;
?? ??? ??? ??? ?struct timeval timeout;
?? ??? ??? ??? ?
?? ??? ??? ??? ?while(1)
?? ??? ??? ??? ?{
?? ??? ??? ??? ??? ?//把所有需要監聽的文件描述符加入到集合
?? ??? ??? ??? ??? ?//select每次返回的時候,集合里面只保存了就緒的文件描述符所以你每次
?? ??? ??? ??? ??? ?//重新監聽前需要重新將你想要監聽的文件描述符加入到集合中去
?? ??? ??? ??? ??? ?//將文件描述符集合清空
?? ??? ??? ??? ??? ?FD_ZERO(&readfds);
?? ??? ??? ??? ??? ?while(...)//不斷的把你想要監聽的文件描述符加入到集合中去
?? ??? ??? ??? ??? ?{
?? ??? ??? ??? ??? ??? ?FD_SET(fd,&readfds);
?? ??? ??? ??? ??? ??? ?max_fd = (fd >= max_fd) ? fd : max_fd;//記錄所有加入集合的文件描述符的最大值
?? ??? ??? ??? ??? ?}
?? ??? ??? ??? ??? ?
?? ??? ??? ??? ??? ?timeout.tv_sec = 10;//等待10秒
?? ??? ??? ??? ??? ?timeout.tv_usec = 0;
?? ??? ??? ??? ??? ?
?? ??? ??? ??? ??? ?//使用select監聽指定的集合
?? ??? ??? ??? ??? ?int r = select(max_fd + 1,&readfds,NULL,NULL,&timeout);
?? ??? ??? ??? ??? ?if(r == 0)//超時了
?? ??? ??? ??? ??? ?{
?? ??? ??? ??? ??? ??? ?continue;
?? ??? ??? ??? ??? ?}
?? ??? ??? ??? ??? ?else if(r < 0)
?? ??? ??? ??? ??? ?{
?? ??? ??? ??? ??? ??? ?perror("select error");
?? ??? ??? ??? ??? ??? ?return -1;//continue;
?? ??? ??? ??? ??? ?}
?? ??? ??? ??? ??? ?
?? ??? ??? ??? ??? ?//r > 0 有就緒的文件描述符啦
?? ??? ??? ??? ??? ?while(...)//所有加入集合的文件描述符都需要遍歷
?? ??? ??? ??? ??? ?{
?? ??? ??? ??? ??? ??? ?if(FD_ISSET(fd,&readfds))
?? ??? ??? ??? ??? ??? ?{
?? ??? ??? ??? ??? ??? ??? ?//fd是可讀
?? ??? ??? ??? ??? ??? ??? ?//對fd進行讀
?? ??? ??? ??? ??? ??? ?}
?? ??? ??? ??? ??? ?}
?? ??? ??? ??? ?}
?? ??? ?2.?? ?poll
?? ??? ??? ?poll的原理基本上和select類似,poll是在內核中使用鏈表來存儲文件描述符集合
?? ??? ??? ?而select是使用數組去存儲(數組空間有限,所以最多監聽1024個文件描述符)。
?? ??? ??? ?poll監聽的文件描述符個數不限。
?? ??? ??? ?NAME
?? ??? ??? ??? ?poll, ppoll - wait for some event on a file descriptor
?? ??? ??? ??? ??? ?在文件描述符上等待一些事件
?? ??? ??? ?SYNOPSIS
?? ??? ??? ??? ?#include <poll.h>
?? ??? ??? ??? ?int poll(struct pollfd *fds, nfds_t nfds, int timeout);
?? ??? ??? ??? ??? ??? ?poll與select類似,但是poll是用結構體struct pollfd描述
?? ??? ??? ??? ??? ??? ?你要監聽的文件描述符以及你要監聽的事件。
?? ??? ??? ??? ??? ?struct pollfd {
?? ??? ??? ??? ??? ??? ?int ? fd; ? ? ? ? /* file descriptor */
?? ??? ??? ??? ??? ??? ??? ?//你要監聽的文件描述符
?? ??? ??? ??? ??? ??? ?
?? ??? ??? ??? ??? ??? ?short events; ? ? /* requested events */
?? ??? ??? ??? ??? ??? ??? ?//你要監聽的文件描述符的事件
?? ??? ??? ??? ??? ??? ??? ?//你所期待文件描述符發生的事件,事件碼使用位域來實現。
?? ??? ??? ??? ??? ??? ??? ?
?? ??? ??? ??? ??? ??? ??? ?//POLLIN:期待文件描述符可讀
?? ??? ??? ??? ??? ??? ??? ?//POLLOUT:期待文件描述符可寫
?? ??? ??? ??? ??? ??? ??? ?//POLLERR:期待文件描述符出錯
?? ??? ??? ??? ??? ??? ??? ?
?? ??? ??? ??? ??? ??? ??? ?如:
?? ??? ??? ??? ??? ??? ??? ??? ?POLLIN | POLLOUT 監聽是否可讀可寫
?? ??? ??? ??? ??? ??? ??? ?
?? ??? ??? ??? ??? ??? ?short revents; ? ?/* returned events */
?? ??? ??? ??? ??? ??? ??? ?//已經就緒的事件
?? ??? ??? ??? ??? ??? ??? ?如:是否已經可讀
?? ??? ??? ??? ??? ??? ??? ?if(revents & POLLIN)
?? ??? ??? ??? ??? ??? ??? ?{
?? ??? ??? ??? ??? ??? ??? ??? ?//可讀啦
?? ??? ??? ??? ??? ??? ??? ?}
?? ??? ??? ??? ??? ?};
?? ??? ??? ??? ??? ?
?? ??? ??? ??? ??? ?監聽一個文件描述符的需要一個這樣的結構體。
?? ??? ??? ??? ??? ?如果需要監聽多個文件描述符的話就需要多個這樣的結構體。
?? ??? ??? ??? ??? ?
?? ??? ??? ??? ??? ?fds:struct pollfd *的指針,指向你要監聽的所有的事件。
?? ??? ??? ??? ??? ??? ?實際上就是監聽結構體struct pollfd的數組
?? ??? ??? ??? ??? ?
?? ??? ??? ??? ??? ?nfds:表示上面那個數組中的元素個數
?? ??? ??? ??? ??? ?
?? ??? ??? ??? ??? ?timeout:超時時間,單位是ms
?? ??? ??? ??? ??? ?
?? ??? ??? ??? ??? ?返回值:
?? ??? ??? ??? ??? ??? ?> 0 ?? ?表示就緒的文件的個數
?? ??? ??? ??? ??? ??? ??? ?由于你監聽多個文件描述符,至于到底是哪些文件描述符就緒了、
?? ??? ??? ??? ??? ??? ??? ?你必須在poll返回后,一個一個的去測試每一個結構體中的
?? ??? ??? ??? ??? ??? ??? ?revents成員變量。
?? ??? ??? ??? ??? ??? ?
?? ??? ??? ??? ??? ??? ?= 0?? ?超時了
?? ??? ??? ??? ??? ??? ?< 0 出錯了
?? ??? ??? ??? ?#define _GNU_SOURCE ? ? ? ? /* See feature_test_macros(7) */
?? ??? ??? ??? ?#include <signal.h>
?? ??? ??? ??? ?#include <poll.h>
?? ??? ??? ??? ?int ppoll(struct pollfd *fds, nfds_t nfds,
?? ??? ??? ??? ? ? const struct timespec *tmo_p, const sigset_t *sigmask);
?? ??? ??? ??? ??? ?與poll類似,時間更加精確(微妙級),去屏蔽一些信號。
?? ??? ??? ??? ??? ?
?? ??? ??? ?簡單的偽代碼:
?? ??? ??? ??? ?while(1)
?? ??? ??? ??? ?{
?? ??? ??? ??? ??? ?struct pollfd fds[100];//需要監聽的事件集合數組
?? ??? ??? ??? ??? ?for(i = 0;i < 你需要監聽的文件描述符個數;i++)
?? ??? ??? ??? ??? ?{
?? ??? ??? ??? ??? ??? ?fds[i].fd = fd;
?? ??? ??? ??? ??? ??? ?fds[i].event = POLLIN | POLLOUT;//監聽fd是否可讀可寫
?? ??? ??? ??? ??? ??? ?fds[i].revents = 0;//初始化的時候成員變量沒有值
?? ??? ??? ??? ??? ?}
?? ??? ??? ??? ??? ?
?? ??? ??? ??? ??? ?//多路復用poll
?? ??? ??? ??? ??? ?int r = poll(fds,100,2000);
?? ??? ??? ??? ??? ?if(r < 0)
?? ??? ??? ??? ??? ?{
?? ??? ??? ??? ??? ??? ?perror("poll failed");
?? ??? ??? ??? ??? ??? ?return -1;//continue;
?? ??? ??? ??? ??? ?}
?? ??? ??? ??? ??? ?else if(r == 0)
?? ??? ??? ??? ??? ?{
?? ??? ??? ??? ??? ??? ?printf("timeout!\n");
?? ??? ??? ??? ??? ??? ?continue;
?? ??? ??? ??? ??? ?}
?? ??? ??? ??? ??? ?
?? ??? ??? ??? ??? ?//表示有文件描述符就緒了,就緒的文件描述符就保存在fds中
?? ??? ??? ??? ??? ?//但是你不知道是哪一個就緒了,所以需要測試
?? ??? ??? ??? ??? ?for(i = 0;i < 100;i++)
?? ??? ??? ??? ??? ?{
?? ??? ??? ??? ??? ??? ?if(fds[i].revents & POLLIN)
?? ??? ??? ??? ??? ??? ?{
?? ??? ??? ??? ??? ??? ??? ?//fds[i].fd這個文件描述符可讀了
?? ??? ??? ??? ??? ??? ??? ?//read
?? ??? ??? ??? ??? ??? ?}
?? ??? ??? ??? ??? ??? ?else if(fds[i].revents & POLLOUT)
?? ??? ??? ??? ??? ??? ?{
?? ??? ??? ??? ??? ??? ??? ?//fds[i].fd這個文件描述符可寫了
?? ??? ??? ??? ??? ??? ??? ?//write
?? ??? ??? ??? ??? ??? ?}?? ??? ??? ??? ??? ??? ?
?? ??? ??? ??? ??? ?}
?? ??? ??? ??? ?}
?? ??? ??? ??? ??? ?
?? ??? ?3.?? ?epoll
?? ??? ??? ?select和poll的效率都不高。
?? ??? ??? ?1.?? ?因為這些文件描述符每一次執行函數(select/poll)都會被拷貝兩次。
?? ??? ??? ?2.?? ?select和poll每次需要去遍歷所有的文件描述符才能確定哪些文件描述符就緒了。
?? ??? ??? ?
?? ??? ??? ?epoll不會隨著文件描述符的數量增加而降低效率。在返回的時候,只返回一個就緒隊列。
?? ??? ??? ?
?? ??? ??? ?epoll的實現接口函數,只有三個函數:
?? ??? ??? ??? ?1)?? ?創建epoll的句柄
?? ??? ??? ??? ??? ?SYNOPSIS
?? ??? ??? ??? ??? ?#include <sys/epoll.h>
?? ??? ??? ??? ??? ?int epoll_create(int size);
?? ??? ??? ??? ??? ?創建/打開一個監聽文件的集合,這個函數的返回值是一個文件描述符。
?? ??? ??? ??? ??? ?
?? ??? ??? ??? ??? ?size:用來告訴內核這個監聽的數量一共有多大。
?? ??? ??? ??? ??? ??? ?現在這個參數其實是被忽略的,意思就是你只要填>0的數
?? ??? ??? ??? ??? ??? ?就意味著監聽任意多個文件描述符。
?? ??? ??? ??? ??? ??? ?同時epoll本身也會占用一個文件描述符,使用完畢后需要關閉掉它。
?? ??? ??? ??? ??? ?
?? ??? ??? ??? ??? ?返回值:成功返回一個epoll的實例(本質就是一個文件描述符),失敗返回NULL
?? ??? ??? ??? ??? ??? ?同時errno被設置。
?? ??? ??? ??? ?2)?? ?將需要監聽的文件描述符加入到epoll句柄或者從句柄中刪除要監聽的文件描述符
?? ??? ??? ??? ??? ?#include <sys/epoll.h>
?? ??? ??? ??? ??? ?int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
?? ??? ??? ??? ??? ?對于指定的需要監聽的文件描述符,只需要加入一次即可。
?? ??? ??? ??? ??? ?會記錄到內核鏈表中去,不想select和poll每一次監聽都需要重新加入。
?? ??? ??? ??? ??? ?
?? ??? ??? ??? ??? ??? ?epfd:epoll的實例,表示你要操作哪一個epoll的文件描述符
?? ??? ??? ??? ??? ??? ?op:具體對epoll實例進行何種操作
?? ??? ??? ??? ??? ??? ??? ?EPOLL_CTL_ADD:增加fd指定的文件描述符到epfd所表示的實例中去
?? ??? ??? ??? ??? ??? ??? ?EPOLL_CTL_MOD:修改fd指定的文件描述符監聽的事件
?? ??? ??? ??? ??? ??? ??? ?EPOLL_CTL_DEL:從監聽集合中刪除fd指定的文件描述符
?? ??? ??? ??? ??? ??? ??? ?
?? ??? ??? ??? ??? ??? ?fd:要操作(ADD/MOD/DEL)的文件描述符
?? ??? ??? ?
?? ??? ??? ??? ??? ??? ?event:要監聽fd的哪一些事件
?? ??? ??? ??? ??? ??? ??? ?
?? ??? ??? ??? ??? ??? ??? ?typedef union epoll_data {
?? ??? ??? ??? ??? ??? ??? ??? ?void ? ? ? ?*ptr;
?? ??? ??? ??? ??? ??? ??? ??? ?int ? ? ? ? ?fd;
?? ??? ??? ??? ??? ??? ??? ??? ?uint32_t ? ? u32;
?? ??? ??? ??? ??? ??? ??? ??? ?uint64_t ? ? u64;
?? ??? ??? ??? ??? ??? ??? ?} epoll_data_t;
?? ??? ??? ??? ??? ??? ??? ?//事件描述結構體
?? ??? ??? ??? ??? ??? ??? ?struct epoll_event {
?? ??? ??? ??? ??? ??? ??? ??? ?uint32_t ?events; ? ? ?/* Epoll events */
?? ??? ??? ??? ??? ??? ??? ??? ??? ?//要監聽的事件,使用位域來實現,不同的事件占events不同的bit位
?? ??? ??? ??? ??? ??? ??? ??? ??? ?//主要要監聽的事件由:
?? ??? ??? ??? ??? ??? ??? ??? ??? ?//EPOLLIN : 監聽的事件為可讀事件
?? ??? ??? ??? ??? ??? ??? ??? ??? ?//EPOLLOUT : 監聽的事件為可寫事件
?? ??? ??? ??? ??? ??? ??? ??? ??? ?//EPOLLERR : 監聽的事件是否出錯
?? ??? ??? ??? ??? ??? ??? ??? ??? ?//EPOLLRDHUP : 監聽流式套接字(UDP)對方是否關閉
?? ??? ??? ??? ??? ??? ??? ??? ??? ?//EPOLLLET : Edge-Triggered
?? ??? ??? ??? ??? ??? ??? ??? ??? ?//邊緣觸發
?? ??? ??? ??? ??? ??? ??? ??? ??? ?//這個標志表示監聽的文件的數據有變化時,才會報告事件
?? ??? ??? ??? ??? ??? ??? ??? ??? ?//有兩種模式:
?? ??? ??? ??? ??? ??? ??? ??? ??? ?//LT:Level-Triggered 只要有數據,就會不停的上報可讀事件
?? ??? ??? ??? ??? ??? ??? ??? ??? ?//默認行為為LT。
?? ??? ??? ??? ??? ??? ??? ??? ??? ?//ET:Edge-Triggered 只有當有數據變化(數量)的時候,
?? ??? ??? ??? ??? ??? ??? ??? ??? ?//才會報告可讀事件
?? ??? ??? ??? ??? ??? ??? ??? ??? ?//LT:不停的上報可讀事件
?? ??? ??? ??? ??? ??? ??? ??? ??? ?//ET:只有在數據的數量發生變化的時候才會上報事件
?? ??? ??? ??? ??? ??? ??? ??? ??? ?
?? ??? ??? ??? ??? ??? ??? ??? ?epoll_data_t data; ? ? ? ?/* User data variable */
?? ??? ??? ??? ??? ??? ??? ??? ??? ?//用戶自定義的數據,用來保存用戶的一些數據
?? ??? ??? ??? ??? ??? ??? ?};
?? ??? ??? ?
?? ??? ??? ??? ??? ??
?? ??? ??? ??? ??? ??? ?成功返回0,失敗返回-1,同時errno被設置。
?? ??? ??? ??? ??? ??? ?
?? ??? ??? ??? ?3)?? ?等待監聽事件的發生
?? ??? ??? ??? ??? ? ? ? ? #include <sys/epoll.h>
?? ??? ??? ??? ?int epoll_wait(int epfd, struct epoll_event *events,int maxevents, int timeout);
?? ??? ??? ??? ??? ?epfd:需要監聽的epoll的實例。
?? ??? ??? ??? ??? ?events:指針,指向一個結構體數組,用來保存已經就緒的事件的信息。
?? ??? ??? ??? ??? ?maxevents:表示第二個參數結構體數組的最多可以保存多少個事件結構體。
?? ??? ??? ??? ??? ?timeout:超時時間,單位ms
?? ??? ??? ??? ??? ?
?? ??? ??? ??? ??? ?返回值:
?? ??? ??? ??? ??? ??? ?>0 ?? ?已經就緒的文件描述符的個數
?? ??? ??? ??? ??? ??? ??? ?已經就緒的事件的信息是直接保存在events指向的數組中
?? ??? ??? ??? ??? ??? ??? ?struct epoll_event結構體中有一個成員變量,可以保存用戶的數據?? ?
?? ??? ??? ??? ??? ??? ??? ?一般這個成員變量用來保存要監聽的文件描述符本身。
?? ??? ??? ??? ??? ??? ??? ?當函數返回時,直接去操作事件結構體就可以了,不需要輪詢所有的文件
?? ??? ??? ??? ??? ??? ??? ?描述符。
?? ??? ??? ??? ??? ??? ??? ?
?? ??? ??? ??? ??? ??? ?=0 ?? ?超時了
?? ??? ??? ??? ??? ??? ?<0 ?出錯,同時errno被設置。
?? ??? ??? ??? ??? ??? ?
?? ??? ??? ??? ?簡單的偽代碼:?? ?
?? ??? ??? ??? ??? ?int epollfd = epoll_create(10);
?? ??? ??? ??? ??? ?struct epoll_event ev;
?? ??? ??? ??? ??? ?//只需要添加一次即可
?? ??? ??? ??? ??? ?while(....)//可能有很多文件描述符需要添加
?? ??? ??? ??? ??? ?{
?? ??? ??? ??? ??? ??? ?ev.events = EPOLLIN;//記錄監聽的事件
?? ??? ??? ??? ??? ??? ?ev.data = fd;//記錄事件本身的文件描述符
?? ??? ??? ??? ??? ??? ?epoll_ctl(epollfd,EPOLL_CTL_ADD,&event);
?? ??? ??? ??? ??? ?}
?? ??? ??? ??? ??? ?
?? ??? ??? ??? ??? ?while(1)
?? ??? ??? ??? ??? ?{
?? ??? ??? ??? ??? ??? ?struct epoll_event e[10];//記錄監聽結果的結構體數組
?? ??? ??? ??? ??? ??? ?int r = epoll_wait(epollfd,e,10,2000);
?? ??? ??? ??? ??? ??? ?if(r == 0)
?? ??? ??? ??? ??? ??? ?{
?? ??? ??? ??? ??? ??? ??? ?printf("time out!\n");
?? ??? ??? ??? ??? ??? ??? ?continue;
?? ??? ??? ??? ??? ??? ?}
?? ??? ??? ??? ??? ??? ?else if(r < 0 )
?? ??? ??? ??? ??? ??? ?{
?? ??? ??? ??? ??? ??? ??? ?perror("epoll failed");
?? ??? ??? ??? ??? ??? ??? ?return -1;
?? ??? ??? ??? ??? ??? ?}
?? ??? ??? ??? ??? ??? ?
?? ??? ??? ??? ??? ??? ?//所有的就緒信息保存到了e數組中
?? ??? ??? ??? ??? ??? ?//不需要輪詢所有的文件描述符
?? ??? ??? ??? ??? ??? ?for(i = 0;i < r;i++)
?? ??? ??? ??? ??? ??? ?{
?? ??? ??? ??? ??? ??? ??? ?//e[i].events 就是表示就緒的事件
?? ??? ??? ??? ??? ??? ??? ?//e[i].data.fd 就是表示是哪一個文件描述符就緒了
?? ??? ??? ??? ??? ??? ??? ?if(e[i].events & EPOLLIN)
?? ??? ??? ??? ??? ??? ??? ?{
?? ??? ??? ??? ??? ??? ??? ??? ?//表示e[i].data.fd文件描述符可讀
?? ??? ??? ??? ??? ??? ??? ?}
?? ??? ??? ??? ??? ??? ??? ?else if(e[i].events & EPOLLOUT)
?? ??? ??? ??? ??? ??? ??? ?{
?? ??? ??? ??? ??? ??? ??? ??? ?//表示e[i].data.fd文件描述符可寫
?? ??? ??? ??? ??? ??? ??? ?}
?? ??? ??? ??? ??? ??? ?}
?? ??? ??? ??? ??? ?}
?? ??? ??? ??? ??? ?
?? ??? ??? ??? ?
?? ?項目:
?? ??? ?實現一個簡單的FTP服務器文件傳輸助手
?? ??? ??? ?功能:通過網絡傳輸,實現文件的跨設備傳輸,包含服務器和客戶端
?? ??? ??? ?服務器功能:
?? ??? ??? ??? ?等待客戶端的連接,支持多客戶端并發,根據客戶端發送過來的命令,執行
?? ??? ??? ??? ?相應的操作,并向客戶端發送其所需要的數據。
?? ??? ??? ?
?? ??? ??? ?客戶端功能:
?? ??? ??? ??? ?負責連接服務器后,發服務器發送命令(命令從鍵盤獲取),并等待服務器的
?? ??? ??? ??? ?相應,同時處理服務器回復的數據。
?? ??? ??? ??? ?
?? ??? ??? ?命令:
?? ??? ??? ??? ?1.?? ?ls?? ?
?? ??? ??? ??? ??? ?用來獲取服務器目錄下的文件信息(文件名)
?? ??? ??? ??? ?
?? ??? ??? ??? ?2.?? ?get file
?? ??? ??? ??? ??? ?用來從服務器中獲取指定的文件名的文件
?? ??? ??? ??? ??? ?如果服務器存在此文件,則服務器回復文件內容
?? ??? ??? ??? ??? ?如果服務器不存在該文件,則回復錯誤碼
?? ??? ??? ??? ?
?? ??? ??? ??? ?3.?? ?put file
?? ??? ??? ??? ??? ?用來上傳文件到服務器
?? ??? ??? ??? ??? ?如果服務器愿意接受文件,則后續發送文件數據
?? ??? ??? ??? ??? ?如果服務器不愿意接受數據,則不發送
?? ??? ??? ??? ??? ?
?? ??? ??? ??? ?4.?? ?bye
?? ??? ??? ??? ??? ?用來告訴服務器,客戶端將要斷開連接
?? ??? ??? ??? ??? ?發送bye之后,客戶端關閉套接字
?? ??? ??? ??? ??? ?服務器收到bye之后,關閉與客戶端連接的套接字,并結束該客戶端處理分支
詳情見ftp服務器
?
?
總結
- 上一篇: 王者荣耀冷却缩减上限多少(如何玩好《王者
- 下一篇: 文本复制程序