1:linux網絡API分爲:socker地址API,socker基礎API,網絡信息APIreact
1,socker地址API:包含IP地址和端口(ip, port)。表示TCP通訊的一端。linux
2,socker基礎API:建立/命名/監聽socker,接收/發起連接,讀寫數據,獲取地址信息,檢測帶外標記和讀取/設置socker選項。sys/socket.h數據庫
3,網絡信息API:主機名和IP地址的轉換,服務名和端口號的轉換。netdb.h編程
2:socket和API的函數 和 相關知識。緩存
1,函數。安全
1 IP地址轉換函數 <arpa/inet.h> in_addr_t inet_addr( const char* strptr ); // 格式轉換:十進制字符串IP -> 網絡字節序(大端)IP int inet_aton( const char* cp, struct in_addr* inp ); // 格式轉換:如上,但將數據保存到參數inp中 char* inet_ntoa( struct in_addr in ); // 格式轉換:網絡字節序(大端)IP -> 十進制字符串IP int inet_pton( int af, const char* src, void* dst ); // 格式轉換:字符串IP地址 -> 網絡字節序(大端)整數IP const char* inet_ntop( int af, const void* src, char* dst, socklen_t cnt ); 格式轉換:網絡字節序(大端)整數IP -> 字符串IP地址 // inet_ntoa函數不可重入。 // inet_ntop返回目標儲存單元的地址。 2 建立socket <sys/types.h> <sys/socket.h> int socket( int domain, int type, int protocol ); // 1 參數domain:使用的底層協議族。 // 2 參數type:指定服務類型。SOCK_STREAM(TCP) SOCK_UGRAM(UDP) // 3 參數protocol:指定具體協議。通常默認爲0.(前面參數已經肯定了協議) // 4 返回:socket文件描述符。失敗-1,設置errno 3 命名socket <sys/types.h> <sys/socket.h> int bind( int sockfd, const struct sockaddr* my_addr, socklen_t addrlen ); // 1 參數addrlen:socket地址長度。 // 2 返回:成功0,失敗-1,設置errno 4 監聽socket <sys/socket.h> int listen( int sockfd, int backlog ); // 1 參數sockfd:指定被監聽socket // 2 參數backlog:監聽隊列最大長度。典型值:5 5 接收連接。 <sys/types.h> <sys/socket.h> int accept( int sockfd, struct sockaddr* addr, socklen_t* addrlen ); // 1 參數sockfd:執行過listen的socket。 // 2 參數addr:被接受連接的遠程socket地址。 // 3 參數addrlen:地址長度。 // 4 返回:成功新的連接socket,失敗-1設置errno // 5 accept只從監聽隊列取出鏈接,並不檢測網絡狀態。 6 發起連接 <sys/types.h> <sys/socket.h> int connect( int sockfd, const struct sockaddr *serv_addr, socklen_t addrlen ); // 1 參數sockfd:由socket系統調用返回一個socket // 2 參數serv_addr:服務器監聽的socket地址。 // 3 返回:成功0,失敗-1設置errno 7 關閉鏈接 <unistd.h> int close( int fd ); // 1 不會當即關閉,只是將引用減一。只有引用爲0時纔會真正關閉。 <sys/socket.h> int shutdown( int sockfd, int howto ); // 1 當即關閉鏈接。 // 2 參數howto:決定函數的行爲。SHUT_RD/WR/RDWR:能夠分別控制讀寫關閉。 // 3 返回:成功0,失敗-1設errno 8 TCP數據讀寫。 <sys/types.h> <sys/socket.h> ssize_t recv( int sockfd, void* buf, size_t len, int flags ); // 返回:實際讀取到的數據長度。失敗-1errno ssize_t send( int sockfd, const void* buf, size_t len, int flags ); // 返回:實際寫入的數據長度。失敗-1reeno // 1 參數buf,len:緩衝區的地址和大小。 // 2 參數flags:數據收發額外控制。一般設0. 9 UDP數據讀寫。 <sys/types.h> <sys/socket.h> ssize_t recvfrom( int sockfd, void* buf, size_t len, int flags, struct sockaddr* src_addr, socklen_t *addrlen ); ssize_t sendto( int sockfd, const void* buf, size_t len, int flags, const struct sockaddr* dest_addr, socklen_t *addrlen ); // 1 參數src/dest_addr:肯定發送/接收端地址。(因UDP沒有鏈接概念) // 2 這兩個函數一樣能夠應用於TCP讀寫數據。 10 通用數據讀寫函數。 <sys/socket.h> ssize_t recvmsg( int sockfd, struct msghdr* msg, int flags ); ssize_t sendmsg( int sockfd, struct msghdr* msg, int flags ); struct msghdr{ void* msg_name; // socket 地址 socklen_t msg_namelen; // 地址長度 struct iovec* msg_iov; // 分散的內存塊 int msg_iovlen; // 分散內存塊的數量 void* msg_control; // 輔助數據地址 socklen_t msg_controllen; // 輔助數據大小 int msg_flags; // 保存函數flag參數 } 11 肯定下一個數據是否時帶外數據。 <sys/socket.h> int sockatmark( int sockfd ); // 返回:帶外1,不然0 12 獲取地址信息。 <sys/socket.h> int getsockname( int sockfd, struct sockaddr* address, socklen_t* address_len ); // 本端 int getpeername( int sockfd, struct sockaddr* address, socklen_t* address_len ); // 遠端 // 1 返回:成功0,失敗-1errno 13 獲取/設置socket選項 <sys/socket.h> int getsockopt( int sockfd, int level, int option_name, void* option_value, socklen_t* restrict option_len ); int setsockopt( int sockfd, int level, int option_name, const void* option_value, socklen_t* restrict option_len ); // 1 參數level:具體協議選項。 // 2 返回:成功0,失敗-1errno。 // 此後爲網絡信息API 14 獲取主機完整信息。 <netdb.h> struct hostent* gethostbyname( const char* name ); // 經過主機名稱獲取 struct hostent* gethostbyaddr( const void* addr, size_t len, int type ); //經過IP地址獲取 // 1 參數type:IP地址的類型。 // 2 返回:主機信息。 15 獲取服務的完整信息。 <netdb.h> struct servent* getservbyname( const char* name, const char* proto ); // 經過名字獲取 struct servent* getservbyport( int port, const char* proto ); // 經過端口號獲取 // 1 參數proto:肯定服務類型:UDP/TCP 16 獲取主機和服務的綜合函數。 int getaddrinfo( const char* hostname, const char* service, const struct addrinfo* hints, struct addrinfo** result ); int getnameinfo( const struct sockaddr* sockaddr, socklen_t addrlen, char* host, socklen_t hostlen, char* serv, socklen_t servlen, int flags );
2,88-94在完成第一版IM時,須要進行測試。服務器
3:高級IO(高編已由函數省略)網絡
1,函數多線程
1 兩個文件描述符之間直接傳遞數據 ssize_t sendfile( int out_fd, int in_fd, off_t* offset, size_t count ); // 1 參數count:傳輸字節數 // 2 返回:成功返回傳輸字節數,失敗-1errno // 3 in_fd:必須時真實文件,out_fd:必須是socket 2 兩個文件描述符之間移動數據。零copy操做 ssize_t splice( int fd_in, loff_t* off_in, int fd_out, loff_t* off_out, size_t len, unsigned int flags ); // 具體須要再瞭解和使用。 3 兩個管道文件描述符之間複製數據。零copy操做 ssize_t tee( int fd_in, int fd_out, size_t len, unsigned int flags );
4:linux服務程序規範。併發
1,函數。
1 和日誌進程通訊。
<syslog.h>
void syslog( int priority, const char* message );
// 1 參數priority:設置值與日誌級別按位與。
2 設置日誌進程的輸出方式。
void openlog( const char* ident, int logopt, int facility );
// 1 參數ident:添加到日誌消息的日期和時間以後。
// 2 參數facility:用來修改syslog函數中的默認值。
3 設置日誌進程的掩碼。(設置屏蔽信息)
int setlogmask( int maskpri );
4 關閉日誌功能。
void closelog();
5 使進程稱爲後臺進程:
<unistd.h>
int daemon( int nochdir, int noclose );
// 1 參數nochdir:傳0,則工做目錄爲根目錄/。不然不變。
// 2 參數noclose:傳0,則被重定向爲/dev/null。
// 3 返回:成功0,失敗-1errno。
2,基本規範。
1)通常之後臺進程形式運行。後臺進程又稱爲守護進程。
2)一般有一套日誌系統。至少能輸出日誌到文件。
3)通常以某個專門的非root身份運行。
4)一般時可配置的。
5)啓動時,會生成一個PID文件並存入/var/run目錄中。
6)一般須要考慮系統資源和限制。
3,日誌進程:rsyslogd。
1)可以接收 用戶/內核 的日誌。
4,ps命令可查看進程、進程組和會話之間的關係。
5:高性能服務器框架。
1,函數。
2,零散細節。
1)客戶鏈接請求時隨即到達的異步事件,服務器須要使用某種IO模型來監聽。
2)服務器能夠解構成三個主要模塊:IO處理單元,邏輯單元,儲存單元。
3)服務器基本模塊的功能描述。
模塊 | 單個服務器程序 | 服務器機羣 |
IO處理單元 | 處理客戶鏈接,讀寫網絡數據 | 做爲接入服務器,實現負載均衡 |
邏輯單元 | 業務進程或線程 | 邏輯服務器 |
網絡儲存單元 | 本地數據庫、文件或緩存。 | 數據庫服務器 |
請求隊列 | 各單元之間的通訊方式。 | 各服務器之間的永久TCP鏈接 |
4)IO服用函數自己時阻塞的,它們能提升程序效率的緣由:它們具備同時監聽多個IO事件的能力。
5)同步IO嚮應用程序通知的是IO就緒事件,異步IO嚮應用程序通知的是IO完成事件。
6)服務器程序一般須要處理三類事件:IO事件,信號和定時事件。
7)若是程序是計算密集型, 併發沒有任何優點。但若是是IO密集型,併發就會有意義。由於IO速度比CPU緩慢。
8)併發模式中,同步指程序徹底按照代碼序列順序執行(用於處理客戶邏輯),異步指程序執行須要由系統事件來驅動(用於處理IO)。
9)主線程向工做線程發送scoket最簡單的方式:往管道中寫數據。
10)池是一組資源的集合。這組資源在服務器啓動之初就被徹底建立好並初始化。常見的有:內存池,線程池,進程池和鏈接池。
11)高性能服務器應該避免沒必要要的數據複製。(避免數據複製,共享內存是最塊的方式,但不必定是最好的。)
12)併發程序必須考慮上下文切換的開銷。
13)儘可能減小鎖的開銷。
3,P2P模式:peer to peer。
1)P2P模式時,主機之間很難相互發現。
4,reactoer模式。
1)它要求主線程(IO處理單元)只負責監聽文件描述符是否有事件發生,有的話就當即將該事件通知工做線程(邏輯單元)。
2)特色:主線程只肯定有請求,工做線程針對具體事件類型針對處理。
5,proactor模式:它將全部IO操做都交給主線程和內核來處理。工做線程僅僅負責業務邏輯。。
6,半同步/半反應堆模式:主線程用來監聽IO,併發送請求到請求隊列。而後工做線程競爭搶奪處理權限。
1)主線程和工做線程共享請求隊列。須要鎖來保證讀寫,消耗CPU
2)每一個工做線程同一時間只能處理一個客戶請求。
7,leader/follewer模式:leader監聽scoket。當有新請求時,leader去處理請求,並從follower中推選新leader。
1)須要包含幾個組件:句柄集,線程集,事件處理器,具體事件處理器。
6:IO複用
1,函數。
1 epoll系列:linux特有的IO複用函數。
<sys/epoll.h>
int epoll_create( int size ); // 建立一個描述符(標識事件表)
int epoll_ctl( int epfd, int op, int fd, struct epoll_event *event ); // 對 事件表 進行操做(添加,修改,刪除註冊事件)。
int epoll_wait( int epfd, struct epoll_event* events, int maxevents, int timeout ); // 等待事件。
2
2,零散細節。
1)IO複用雖然能同時監聽多個文件描述符,但它自己是阻塞的。
2)epoll系列有兩種模式:LT和ET。ET是相對高效的模式。
3)ET模式的文件描述符都應該是非阻塞的。(若是是阻塞的,可能會一直阻塞下去)
4)EPOLLONESHOT事件:使事件只能觸發一次。若須要再次觸發,須要重置。能夠防止重複讀寫。
3,select,poll,epoll的區別
系統調用 | select | poll | epoll |
事件集合 | 用戶經過三個參數分別傳入可讀,可寫和異常事件。內核經過這些參數在線修改就緒事件。每次調用都會重讀 | 統一處理全部事件類型。須要一個事件集。用戶經過pollfd.event傳入事件。內核經過修改pollfd.event反饋就緒事件。 | 內核經過事件表直接管理全部事件。無需反覆傳入事件。epoll_wait僅用來反饋就緒事件(不須要多餘判斷) |
應用程序索引就緒文件描述符的時間複雜度 | n | n | 1 |
最大支持文件描述符數 | 通常有最大值限制 | 65535 | 65535 |
工做模式 | LT | LT | LT/ET |
內核實現和工做效率 | 輪詢方式檢測就緒事件 | 輪詢 | 回調方式檢測就緒事件。 |
7:信號。
1,函數
2,零散細節。
1)服務器程序必須處理(至少能忽略)一些常見信號。以免異常終止。
2)信號是一種異步事件:信號處理函數和程序的主循環是兩條不一樣的執行路線。因此信號須要儘快完成。典型的解決方案:信號僅提供信號,具體處理儘可能方在主循環中。
8:時間堆:將全部定時器超時時間最小的一個定時器的超時值做爲心搏間隔。
9:高性能IO框架庫。
1,常見框架:ACE,ASIO,Libevent。
2,linux服務器程序必須處理三類事件:IO,信號和定時事件。但須要考慮三個問題
1)統一事件源。須要統一管理三類事件。通常方法爲:利用IO複用系統調用來管理全部事件。
2)可移植性。
3)對併發編程的支持。
3,IO框架庫以庫函數的形式,封裝了較爲底層的系統調用,給應用程序提供了一組更便於使用的接口。
4,Libevent框架庫。
1)跨平臺支持,擁有統一事件源,線程安全,基於Reactor模式。
2)官方網站:libevent.org
5,P240,須要執行代碼確認。
10:多線程編程。
1,線程實現方式:徹底在用戶空間實現,徹底由內核調度和雙層調度。
1)徹底在用戶空間實現優勢:建立和調度線程無需內核的干預,速度很快。建立多各線程對系統性能沒有太大影響。但沒法在多CPU狀況下使用。
2)內核調度:和用戶空間徹底相反。
3)雙層調度:上兩種模式的混合體。
11:進程池和線程池。
1,線程池中的線程數量應該和CPU數差很少。
2,進程之間(有關聯進程)傳遞數據最簡單的方法是管道。
12:服務器調製,調試和測試。
1,linux平臺的一個優秀特性是內核微調:能夠經過修改文件的方式來調正內核參數。
2,調試方法:tcpdump抓包看數據,或者gdb。
3,linux對應用程序能打開的最大文件描述符數量有兩個層次的限制:用戶級限制和系統級限制。
4,gdb調試多進程的兩個簡單方法:
1)找到PID,而後使用attach添加到gdb中。
2)set follow-fork mode(parent or child)
5,gdb調試多線程的方法。
1)info threads:顯示當前可調試的全部線程。
2)threadID:調試ID線程
3)set scheduler-locking(off|on|step):off全部線程執行,on只有當前線程執行,step單步調試時,只有當前線程會執行。
6,壓力測試:IO複用,多線程,多進程併發編程等方法(不懂- -)。
13:經常使用工具。
1,tcpdump:經典的網絡抓包工具
2,lsof:列出當前系統打開的文件描述符的工具。
3,nc:用來快速構建網絡鏈接。
1)服務器方式運行時:監聽某個端口並接收客戶端鏈接。
2)客戶端方式運行時:能夠向服務器發起鏈接並收發數據。
3)因此能夠用來調試客戶端和服務器。
4,strace:測試服務器性能的工具。
1)跟蹤程序運行過程當中執行的系統調用和接收到的信號,並將系統調用名、參數、返回值及信號名數處到標準輸出或指定文件。
5,netstat:網絡信息統計工具。
1)能夠打印本地網卡接口上的所有鏈接、路由表信息、網卡接口信息。
6,ifstat:interface statistics。簡單的網絡流量檢測工具。
7,mpstat:multi-processor statistics。能實時監測多處理器系統上的每一個CPU的使用狀況。