- bind成功時返回0,失敗時返回-1並設置errno。其中,兩種常見的errno是EACCES和EADDRINUSE,他們的含義分別是:
- EACCES:被綁定的地址是受保護的地址,僅有超級用戶能夠訪問
- EADDRINUSE:被綁定的地址正在使用中。
- listen的backlog參數表示:處於徹底鏈接狀態的socket的上限。
- accept只是從監聽隊列中取出鏈接,而無論鏈接處於何種狀態。(ESSTABLISHED或者CLOSE_WAIT)
- connect失敗時返回-1並設置errno。其中,最多見的兩種errno是ECONNREFUSED和ETIMEDOUT。
- ECONNREFUSED:目標端口不存在,鏈接被拒絕。
- ETIMEDOUT:鏈接超時。
-
close並不是當即關閉一個鏈接,而是將fd的引用數減1。html
重點來了:
- 對於監聽Socket來講,在listen調用前設置這些經常使用的Socket選項,那麼Accept返回的鏈接Socket將繼承這些選項:SO_KEEPALIVE,SO_LINGER,SO_RCVBUF,SO_REVLOWAT,SO_SNDBUF,SO_SNDLOWAT,TCP_NODELAY。
- 對於客戶端Socket來講,上述的選項應該在調用Connect函數以前設置,由於Connect調用成功返回以後,TCP三次握手已經完成。
- 下面我將詳細介紹這些套接字選項:
- SO_REUSEADDR:服務器程序能夠經過設置Socket選項來強制使用陷入TIME_WAIT狀態的鏈接佔用的Socket Address
- SO_REUSPORT:容許在一個端口上啓動一個服務的多個實例,只要每一個實例綁定不一樣的本地IP地址便可。
- SO_RCVBUF 和 SO_SNDBUF:表示TCP接受緩衝區與發送緩衝區的大小。TCP接受緩衝區的大小最好設置爲MSS(1460)的偶數倍.
- SO_RCVLOWAT 和 SO_SNDLOWAT:表示接受緩衝區與發送緩衝區的低水平位標記。默認狀況下,均爲1。
- SO_LINGER:能夠經過該選項控制Close系統調用的不一樣的行爲。默認狀況下,當咱們使用Close系統調用來關閉一個Socket時,Slose當即返回,TCP模塊負責將該Socket發送緩衝區中的數據發送給對方。
- SO_KEPPALIVE:發送週期性保活報文維持鏈接。
- TCP_NODELAY:禁止Nagle算法,即在鏈接上會出現不少的小的數據塊,在局域網環境下能夠開啓。
- 零拷貝函數:
#include<sys/sendfile.h> ssize_t sendfile(int out_fd, int in_fd, off_t* offset, size_t count);
- 零拷貝函數:
#include<fcntl.h> ssize_t splice(int in_fd, loff_t* off_in, int out_fd, loff_t* off_out, size_t len, unsigned int flags); //使用splice實現零拷貝反射服務器 int pipefd[2]; int ret = pipe(pipefd); ret = splice(connectfd, NULL, pipefd[1], NULL, 65536, SPLICE_F_MORE | SPLICE_F_MOVE); ret = splice(pipefd[0], NULL, connectfd, NULL, 65536, SPLICE_F_MORE | SPLICE_F_MOVE); close(connectfd);
- 針對非阻塞IO執行的系統調用老是當即返回,而無論事情是否已經發生。若是事情沒有當即發生,這些系統調用就當即返回-1,就和出錯返回同樣,此時咱們必須根據返回時設置的errno來判斷具體發生了什麼狀況。對Accept,Send,Recv而言,事件發生未發生時errno一般被設置爲EAGAIN(意味着「再來一次」)或者EWOULDBLOCK(意味着「指望阻塞」);對於Connect而言,errno則被設置爲EINPEOCESS(意味着「在處理中」)。
- IO複用函數自己是阻塞的,他們能夠提升程序效率的緣由在於他們具備同事監聽多個IO事件的能力。
- 服務器程序一般須要處理三類事件:IO事件,定時器事件,信號事件。
- 兩種事件處理模式:Reactor和Proator
- Reactor
- 主線程往Epoll內核事件表中註冊可讀事件
- 主線程等待可讀事件發生
- 可讀事件發生時,主線程將可讀事件放入請求隊列
- 主線程將睡眠在請求隊列中的工做線程喚醒,處理可讀事件。處理完畢以後,向Epoll註冊可寫事件
- 主線程等待可寫事件發生
- 可寫事件發生時,主線程將可寫事件放入請求隊列
- 如此往復循環
- Proator
- 主線程向內核註冊讀完成事件,完成時,經過信號通知應用程序
- 主線程繼續處理其餘邏輯
- 主線程收到讀完成事件後,將讀到的數據封裝成一個事件對象送入工做線程。工做線程處理完畢以後,向內核註冊讀完成事件,並告訴內核用戶緩衝區的位置
- 主線程繼續處理其餘邏輯
- 主線程收到讀完成事件,作善後處理
- 如此往復循環
- Reactor
- 三個IO複用機制
- select:每次事件發生以後,以前註冊事件都會被修改,須要用FD_ISSET進行判斷,而後從新註冊。
- poll:同select,使用輪詢機制
- epoll:epoll對文件描述符的操做有兩種模式:LT(電平觸發)和ET(邊沿觸發)。LT模式是默認的工做模式。當設置EPOLLET時,epoll會以ET模式來操做文件描述符,ET是高效工做模式。
- ET:當epoll_wait檢測到其上有事件發生並將這次的事件通知給應用程序後,應用程序必須馬上處理該事件,由於後續的epoll_wait不會再次嚮應用程序通知這一事件。下降了epoll事件被觸發的次數,所以效率高於LT。
- LT:epoll_wait檢測到有事件發生並將這次的事件通知應用程序後,應用程序能夠不當即處理該事件。這樣,當應用程序下一次調用epoll_wait的時候,epoll_wait還會再次嚮應用通知該事件,直到該事件被處理。
-
EPOLLONESHOT:對於註冊EPOLLONESHOT事件的文件描述符,操做系統最多觸發其上註冊的一個事件(可讀,可寫或者異常),而且只觸發一次,除非咱們使用epoll_ctl函數重置該文件描述符上註冊的EPOLLONESHOT事件。
```算法include<sys/select.h>
int select(int nfds, fd_set* readfds, fd_set* writefds, fd_set* exceptfds, struct timeval* timeout);編程
FD_ZERO(fd_set* fdset);
FD_SET(int fd, fd_set* fdset);
FD_CLR(int fd,fd_set* fdset);
int FD_ISSET(int fd,fd_set* fdset);服務器
struct timeval
{
long tv_sec; //秒數
long tv_usec; //微秒數
}markdown
include<poll.h>
int poll(struct pollfd* fds, nfds_t nfds, int timeout);網絡
struct pollfd
{
int fd;
short event; //註冊的事件
short revent; //實際發生的事件,內核填充,記得每次判斷後歸零
}socket
POLLIN:數據可讀
POLLOUT:數據可寫
POLLREHUP:TCP鏈接被對方關閉,或者對方關閉了寫操做
POLLERR:錯誤
POLLHUP:掛起
POLLNVAL:文件描述符沒有打開函數
include<sys/epoll.h>
int epoll_create(int size);
int epoll_ctl(int epollfd, int op, int fd, struct epoll_event* event);
int epoll_wait(int epfd, struct epoll_event* events, int maxevents, int timeout);post
op參數的類型:
EPOLL_CTL_ADD:向事件註冊表中註冊fd事件
EPOLL_CTL_MOD:修改fd上註冊的事件
EPOLL_CTL_DEL:刪除fd上註冊的事件性能
struct epoll_event
{
_uint32_t events;
epoll_data_t data;
};
union epoll_data
{
void* ptr;
int fd; //指定事件所從屬的描述符
uint32_t u32;
uint64_t u64;
} epoll_data_t;
- 服務器端處理信號的方式:
void sig_handler(int sig)
{
int save_errno = errno;
int msg = sig;
send(pipefd[1], (char*)&msg, 1, 0);
errno = save_errno;
}
void addsig(int sig)
{
struct sigaction sa;
memset(&sa, '\0', sizeof(sa));
sa.sa_handleer = sig_handler;
sa.sa_flags |= SA_RESTART;
sigfillset(&sa.sa_mask);
assrt(sigaction(sig, &sa, NULL) != -1);
}
```
- 與網絡編程相關的信號:
- SIGHUP:掛起進程的控制終端時,SIGHUP信號會被觸發。
- SIGPIPE:往一個關閉的管道或者Socket中寫入數據。默認狀態是結束進程。
- SIGURG:帶外數據到達。
- SIGCHLD:子進程狀態發生變化,使用wait進行處理
- SIGTERM:終止進程。kill命令默認發送該信號。
- SIGINT:鍵盤輸入以中斷進程。