Linux網絡


*************基本概念***************
【1】計算機與網絡發展的7個階段
    1.    批處理(20世紀50年代)
        是指實現將用戶每一個數據裝入卡帶或者磁帶。並有計算機按照必定的順序讀取,
        是用戶索要執行的這些程序和數據可以一併批量獲得處理的方式。
    2.    分時系統(20世紀60年代)
        是指多個終端(包含鼠標、鍵盤、顯示器等輸入輸出設備組成,最初還包括打
        印機)與一臺計算機鏈接,容許多個用戶同時使用一臺計算機的系統。
        特性:多路性、獨佔性、交互性和及時性。
    3.計算機之間的通訊(20世紀70年代)
    4.    計算機網絡的產生(20世紀80年代)
    5.    互聯網的普及(20世紀90年代)
    6.    以互聯網技術爲中心的時代(2000年)
    7.    從「單純創建鏈接」到「安全創建鏈接」(2010年)
【2】網絡體系結構即指網絡的層次結構和每層所使用協議的集合
【3】
    1. 協議
        一組控制數據通訊的規則。
        三要素:語法(包括數據格式、編碼及信號電平等)、
                語義(包括用於協議和差錯處理的控制信息)、
                時序(包括速度匹配和排序)。
    2. 標準
        一致贊成的規則。
        分類:
        事實上的標準:實際狀況或者習慣
        合法標準:法律或者規章制度
    3. 標準化組織
        緩慢發展:
            ISO:國際標準化組織
            ITU-T:國際電聯-電信標準部
            ANSI:美國國家標準化局
            IEEE:電氣電子工程師協會(主要是以太網、局域網方面的)
            EIA:電子工業協會(物理傳輸標準、光釺傳輸)
        快速發展:
            論壇:幀中繼論壇、ATM論壇
            
        管理機構:FCC 聯邦通訊委員會

        Internet標準:RFC
        
【4】OSI開放系統互聯模型
    OSI模型是一個理想化的模型,還沒有有完整的實現

    應用層    應用程序:FTP、E-mail、Telnet
    表示層    數據格式定義、數據轉換/加密
    會話層    創建通訊進程的邏輯名字與物理名字之間的聯繫
    傳輸層    差錯處理/恢復,流量控制,提供可靠的數據傳輸
    網絡層    數據分組、路由選擇
    數據鏈路層  數據組成可發送、接收的幀
    物理層    傳輸物理信號、接口、信號形式、速率
    
【5】7層通訊
    (1)應用層:指定特定應用的協議(好比發送和接受文件的軟件按鈕,發送者輸入「早上好」並附上收件人,按下發送按鈕,接受者收到信息會將其存儲在硬盤或者非易失存儲器(數據不會由於斷電而丟失的一種存儲設備)上,這些都是在應用層上的)
    (2)表示層:設備固有數據格式和網絡標準數據格式的轉換(接受者和發送者若是使用的郵件客戶端不同,那麼就會出現問題,如何實現用戶之間的通訊,那麼就須要在表示層來起做用,使得在不一樣的客戶端上擁有相同的網絡格式)
    (3)會話層:通訊管理,負責創建或者斷開通訊鏈接(發送者一次性發送5份郵件,那麼接受者如何接受,是一次性接受全部的文件而後斷開鏈接仍是沒接受一次就斷開,而後在此進行,發送者同理)
    (4)傳輸層:管理兩個節點(互聯的網絡中斷)之間的數據傳輸。負責可靠傳輸(確保數據被可靠地傳送到目標地址)(確保發送者和接受者之間的通訊,會話層負責決定創建鏈接和斷開鏈接的時機,而傳輸層進行實際的創建和斷開處理)
    (5)網絡層:地址管理與路由選擇,做用:在網絡相互鏈接的環境中,將數據從發送端主機發送到接受端主機
    (6)數據鏈路層:互連設備之間傳送和識別數據幀
    (7)物理層:以「0」、「1」表明的電壓的高低、燈光的閃滅。界定鏈接器和網絡的規格。
【6】TCP/IP協議:傳輸控制/網際協議(Transfer Control Protocol/Internet Protocol) 又稱做網絡通信協議
    TCP/IP協議是Internet事實上的工業標準。
    傳輸控制/網際協議(Transfer Control Protocol/Internet Protocol) 又稱做網絡通信協議

    應用層  TFTP,HTTP,SNMP,FTP,SMTP,DNS,Telnet

    傳輸層    TCP,UDP

    網絡層    IP,ICMP,RIP,OSPF,BGP,IGMP

    物理層    SLIP,CSLIP,PPP,ARP,RARP,MTU  ISO2110,IEEE802.1,EEE802.2

    TCP(Transport Control Protocol)傳輸控制協議
    UDP(User Datagram Protocol)用戶數據報協議
    IP(Internetworking Protocol)網間協議
    HTTP(Hypertext Transfer Protocol) 超文本傳輸協議
    SMTP(Simple Mail Transfer Protocol)簡單郵件傳輸協議
    
【7】UDP和TCP
    共同點:同爲傳輸層協議
    不一樣點:
        TCP:有鏈接,可靠
        UDP:無鏈接,不保證可靠
    
    TCP:(全雙工)
        TCP(即傳輸控制協議):是一種面向鏈接的傳輸層協議,
        它能提供高可靠性通訊(即數據無誤、數據無丟失、數據
        無失序、數據無重複到達的通訊)
        適用狀況:
            適合於對傳輸質量要求較高,以及傳輸大量數據的通訊。
            在須要可靠數據傳輸的場合,一般使用TCP協議
            MSN/QQ等即時通信軟件的用戶登陸帳戶管理相關的功能一般採用TCP協議
            
            TCP「三次握手」
            
    UDP:(全雙工)
        UDP(User Datagram Protocol)用戶數據報協議,是不可靠
        的無鏈接的協議。在數據發送前,由於不須要進行鏈接,因此
        能夠進行高效率的數據傳輸。
        適用狀況:
            發送小尺寸數據(如對DNS服務器進行IP地址查詢時)
            在接收到數據,給出應答較困難的網絡中使用UDP。(如:無線網絡)
            適合於廣播/組播式通訊中。
            MSN/QQ/Skype等即時通信軟件的點對點文本通信以及音視頻通信一般採用UDP協議
            流媒體、VOD、VoIP、IPTV等網絡多媒體服務中一般採用UDP方式進行實時數據傳輸
【8】socket
    是一個編程接口
    是一種特殊的文件描述符 (everything in Unix is a file)
    並不只限於TCP/IP協議
    面向鏈接 (Transmission Control Protocol - TCP/IP)、
    無鏈接 (User Datagram Protocol -UDP 和 Inter-network Packet Exchange - IPX)
    
    在OSI模型中,主要位於會話層和傳輸層之間

    類型:
        流式套接字(SOCK_STREAM)
            提供了一個面向鏈接、可靠的數據傳輸服務,數據無差錯、無重複的發送且按發送順序接收。內設置流量控制,避免數據流淹沒慢的接收方。數據被看做是字節流,無長度限制。
        --------> TCP協議
        數據報套接字(SOCK_DGRAM)
            提供無鏈接服務。數據包以獨立數據包的形式被髮送,不提供無差錯保證,數據可能丟失或重複,順序發送,可能亂序接收。
        --------> UDP協議
        原始套接字(SOCK_RAW)
            能夠對較低層次協議如IP、ICMP直接訪問。    
【9】IP地址
    IP地址是Internet中主機的惟一標識
    Internet中的主機要與別的機器通訊必須具備一個IP地址
    IP地址爲32位(IPv4)或者128位(IPv6)
    每一個數據包都必須攜帶目的IP地址和源IP地址,路由器依靠此信息爲數據包選擇路由

    ipv4表示形式:點分十進制形式,如202.38.64.10,最後都會轉換爲一個32位的無符號整數。

    IP地址分類(基於ipv4地址前八位來區分)
    A類  0000 0000 - 0111 1111     0.x.x.x - 127.x.x.x
    B類  1000 0000 - 1011 1111     128.x.x.x - 191.x.x.x
    C類  1100 0000 - 1101 1111  192.x.x.x - 223.x.x.x
    D類  1110 0000 - 1110 1111     224.x.x.x - 239.x.x.x   表示組播地址
    E類  1111 0000 - 1111 1111  240.x.x.x - 255.x.x.x   屬於保留測試

    127.x.x.x 表示主機地址
    
    子網掩碼:表示連接的主機最大數
    A類 255.0.0.0
    B類 255.255.0.0
    C類 255.255.255.0
    
    192.168.2.x
        192.168.2.1 表示網關
        192.168.2.255 表示該網段下的廣播地址
    
    #include <arpa/inet.h>

    將點分十進制ip地址轉化爲網絡字節序的整型數據
    in_addr_t inet_addr(const char *cp);
    
    將網絡字節序的整型數據轉化爲點分十進制ip地址
    char *inet_ntoa(struct in_addr in);

    例子:
        inet_addr("192.168.2.189");
【10】端口號(vi /etc/services 查看端口號)
    爲了區分一臺主機接收到的數據包應該轉交給哪一個進程來進行處理,使用端口號來區別
    衆所周知端口:1~1023(1~255之間爲衆所周知端口,256~1023端口一般由UNIX系統佔用)
    已登記端口:1024~49151
    動態或私有端口:49152~65535

    通常使用:8888 9999 10000 10001
【11】字節序    
    不一樣類型CPU的主機中,內存存儲多字節整數序列有兩種方法,稱爲主機字節序(HBO):
    小端序(little-endian) - 低序字節存儲在低地址
    將低字節存儲在起始地址,稱爲「Little-Endian」字節序,Intel、AMD等採用的是這種方式;
    大端序(big-endian)- 高序字節存儲在低地址
    將高字節存儲在起始地址,稱爲「Big-Endian」字節序,由ARM、Motorola等所採用
        
    如何測試主機字節序:
        方法1:使用指針
        方法2:使用file命令,file a.out 其中LSB的L表明小端存儲
        方法3:共用體
    
    網絡中傳輸的數據必須按網絡字節序,即大端字節序
    
    #include <arpa/inet.h>
    
    將主機字節序轉化爲網絡字節序
    uint32_t htonl(uint32_t hostlong);
    uint16_t htons(uint16_t hostshort);
    
    將網絡字節序轉化爲主機字節序
    uint32_t ntohl(uint32_t netlong);
    uint16_t ntohs(uint16_t netshort);

    例子:
        htons(9999);
    
****************TCP網絡編程******************
【1】流程
        舉個例子:
            買個手機、買張卡
            手機和卡必須匹配
            將卡與手機綁定
            設置爲非飛行模式
            進行通訊
        服務器:server.c
            建立套接字 socket( )
            填充服務器的網絡信息結構體 sockaddr_in
            將套接字與服務器的網絡信息結構體綁定 bind( )
            將套接字設置爲監聽模式 listen( )
            阻塞等待客戶端的鏈接請求 accept( )
            進行通訊 recv( )/send( )
        客戶端:client.c
            建立套接字 socket( )
            填充服務器的網絡信息結構體 sockaddr_in
            發送客戶端的鏈接請求 connect( )
            進行通訊 send( )/recv( )
【2】ctags的建立
    第一步:在/usr/include 裏面執行
            sudo ctags -R,生成一個tags 的索引文件
            測試:vim -t sockaddr_in
    第二步:實現全局
            在家目錄下的 .vimrc 添加
            set tags+=/usr/include/tags
    使用:ctrl ] 追代碼
          ctrl t 返回上一層
    

【3】socket( )
    #include <sys/types.h>          /* See NOTES */
    #include <sys/socket.h>

    int socket(int domain, int type, int protocol);
    功能:建立一個套接字,返回一個文件描述符
    參數:
        domain:通訊域或者協議族
            AF_UNIX,AF_LOCAL 本地通訊
            AF_INET ipv4網絡通訊
            AF_PACKET 底層通訊
        type:類型
            SOCK_STREAM 流式套接字 TCP
            SOCK_DGRAM 數據報套接字 UDP
            SOCK_RAW 底層通訊
        protocol:協議,通常爲0
    返回值:
        成功:文件描述符
        失敗:-1
    例子:
        int sockfd;
        if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
        {
            perror("fail to socket");
            //return -1;
            exit(1);
        }
【4】bind( )
    #include <sys/types.h>          /* See NOTES */
    #include <sys/socket.h>

    int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
    功能:將套接字與網絡信息結構體綁定
    參數:
        sockfd:文件描述符,socket的返回值
        addr:網絡信息結構體
            通用的(通常不用)
                struct sockaddr {
                    sa_family_t sa_family;
                    char        sa_data[14];
                }
            通常使用sockaddr_in
                #include <netinet/in.h>
                struct sockaddr_in
                {
                    __SOCKADDR_COMMON (sin_);
                        ==>
                            #define __SOCKADDR_COMMON(sa_prefix) \
                                    sa_family_t sa_prefix##family
                        ==>
                            在函數宏裏面,##表明字符串的拼接
                            sa_family_t sin_family;  //協議族 AF_INET
                                    
                    in_port_t sin_port;  //端口號
                    
                    struct in_addr sin_addr;  
                        ==>
                            struct in_addr
                            {                                                                                                              
                                in_addr_t s_addr;  //ip地址
                            };
                                                                                                                            
                    //這個沒有用。爲了使得sockaddr_in與sockaddr大小一致
                    unsigned char sin_zero[sizeof (struct sockaddr) -
                            __SOCKADDR_COMMON_SIZE -
                            sizeof (in_port_t) -
                            sizeof (struct in_addr)];
                };

        addrlen:addr的大小
    返回值:
        成功:0
        失敗:-1
    例子:
        struct sockaddr_in serveraddr;
        serveraddr.sin_family = AF_INET;
        serveraddr.sin_addr.s_addr = inet_addr("192.168.2.222");
        serveraddr.sin_port = htons(9999);
        
        if(bind(sockfd, (struct sockaddr *)&serveraddr, sizeof(struct sockaddr_in)) < 0)
        {
            perror("fail to bind");
            exit(1);
        }
【5】listen( )
    #include <sys/types.h>          /* See NOTES */
    #include <sys/socket.h>

    int listen(int sockfd, int backlog);
    功能:將套接字設置爲監聽模式
    參數:
        sockfd:文件描述符,socket的返回值
        backlog:同時響應客戶端的鏈接的個數,通常設置爲5,10
    返回值:
        成功:0
        失敗:-1
    例子:
        if(listen(sockfd, 10) < 0)
        {
            perror("fail to listen");
            exit(1);
        }
【6】accept
    #include <sys/types.h>          /* See NOTES */
    #include <sys/socket.h>

    int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
    功能:阻塞等待文件描述符準備就緒
    參數:
        sockfd:文件描述符,socket的返回值
        addr:網絡信息結構體(自動填充的客戶端的網絡信息結構體)
        addrlen:addr的大小
    返回值:
        成功:新的文件描述符(用於通訊)
        失敗:-1
    例子:
        int acceptfd;
        struct sockaddr_in clientaddr;
        socklen_t addrlen = sizeof(clientaddr);
        if((acceptfd = accept(sockfd, (struct sockaddr *)&clientaddr, &addrlen)) <0 )
        {
            perror("fail to accept");
            exit(1);
        }
【7】connect
    #include <sys/types.h>          /* See NOTES */
    #include <sys/socket.h>

    int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
    功能:發送客戶端的鏈接請求
    參數:
        sockfd:文件描述符,socket的返回值
        addr:網絡信息結構體(本身填充的服務器的網絡信息結構體)
        addrlen:addr的大小
    返回值:
        成功:0
        失敗:-1
    例子:
        if(connect(sockfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr)) < 0)
        {
            perror("fail to connect");
            exit(1);
        }
【8】send( )
    #include <sys/types.h>
    #include <sys/socket.h>

    ssize_t send(int sockfd, const void *buf, size_t len, int flags);
    功能:發送數據
    參數:
        sockfd:文件描述符
            服務器:accept的返回值
            客戶端:socket的返回值
        buf:發送的數據
        len:buf的長度
        flags:標誌位
            0 阻塞
            MSG_DONTWAIT 非阻塞
    返回值:
        成功:發送的數據的長度
        失敗:-1
        
【9】recv( )        
    #include <sys/types.h>
    #include <sys/socket.h>

    ssize_t recv(int sockfd, void *buf, size_t len, int flags);
    功能:接收數據
    參數:
        sockfd:文件描述符
            服務器:accept的返回值
            客戶端:socket的返回值
        buf:接收的數據
        len:buf的長度
        flags:標誌位
            0 阻塞
            MSG_DONTWAIT 非阻塞
    返回值:
        成功:接收的數據的長度
            0 發送端異常退出或者關閉文件描述符
        失敗:-1
        
****************UDP網絡編程*******************
【1】流程
    服務器:
        建立套接字 socket( )
        填充服務器的網絡信息結構體 sockaddr_in
        將套接字與服務器的網絡信息結構體綁定 bind( )
        進行通訊 recvfrom( )/sendto( )
    客戶端:
        建立套接字 socket( )
        填充服務器的網絡信息結構體 sockaddr_in
        進行通訊 sendto( )/recvfrom( )
        
【2】sendto( )
    #include <sys/socket.h>

    ssize_t sendto(int socket, const void *message, size_t length,
            int flags, const struct sockaddr *dest_addr, socklen_t dest_len);
    功能:發送數據
    參數:
        socket:文件描述符,socket的返回值
        message:發送的數據
        length:數據的長度
        flags:標誌位,通常爲0
        dest_addr:目的地址(給誰發送數據)
        dest_len:addr的大小
    返回值:
        成功:發送的數據的長度
        失敗:-1
        
【3】recvfrom( )
    #include <sys/types.h>
    #include <sys/socket.h>

    ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,
                        struct sockaddr *src_addr, socklen_t *addrlen);
    功能:接收數據
    參數:
        sockfd:文件描述符
        buf:接收的數據
        len:數據的長度
        flags:標誌位,通常爲0
        src_addr:源的地址(接收誰的信息)
        addrlen:addr的長度
    返回值:
        成功:接收的數據的長度
        失敗:-1    
    做業:基於tcp 的文件服務器
        功能:
            客戶端能夠查看服務器所在目錄的文件 opendir readdir
            客戶端能夠下載服務器所在目錄的文件
            客戶端能夠上傳文件到服務器
    
******************day_2*******************
***********IO模型***************
【1】定義
    在UNIX/Linux下主要有4種I/O 模型:
    阻塞I/O:
        最經常使用、最簡單、效率最低
    非阻塞I/O:
        可防止進程阻塞在I/O操做上,須要輪詢
    I/O 多路複用:
        容許同時對多個I/O進行控制
    信號驅動I/O:
        一種異步通訊模型
【2】阻塞I/O 模式
    阻塞I/O 模式是最廣泛使用的I/O 模式,大部分程序使用的都是阻塞模式的I/O 。
    缺省狀況下,套接字創建後所處於的模式就是阻塞I/O 模式
    前面學習的不少讀寫函數在調用過程當中會發生阻塞。
        讀操做中的read、recv、recvfrom
        寫操做中的write、send
        其餘操做:accept、connect
【3】非阻塞模式I/O
    當咱們將一個套接字設置爲非阻塞模式,咱們至關於告訴了系統內核:「當我請求的I/O 操做不可以立刻完成,你想讓個人進程進行休眠等待的時候,
    不要這麼作,請立刻返回一個錯誤給我。」
    當一個應用程序使用了非阻塞模式的套接字,它須要使用一個循環來不停地測試是否一個文件描述符有數據可讀(稱作polling)。
    應用程序不停的polling 內核來檢查是否I/O操做已經就緒。這將是一個極浪費CPU 資源的操做。
    這種模式使用中不廣泛。
    
    使用fcntl函數實現非阻塞IO
    #include <unistd.h>
    #include <fcntl.h>

    int fcntl(int fd, int cmd, ... /* arg */ );
    功能:操做一個文件描述符
    參數:
        fd:文件描述符
        cmd:具體的命令或者選項
            F_GETFL 獲取文件狀態標誌位
            F_SETFL 設置文件狀態標誌位
                O_NONBLOCK 非阻塞
        arg:可變參,根據cmd的後面括號決定,若是是void則不使用,若是爲long,則須要
    返回值:    
        成功:
            F_GETFL 獲取到的文件狀態標誌位
            F_SETFL 0
        失敗:-1
    注意:對寄存器或者位的操做,通常執行讀、改、寫三步

    第一步:獲取標誌位
    int flags;
    if((flags = fcntl(0, F_GETFL)) < 0)
    {
        perror("fail to fcntl");
        exit(1);
    }
    
    第二步:修改標誌位
    //flags = flags | O_NONBLOCK;
    flags |= O_NONBLOCK;
    
    第三步:將新的標誌位寫回去
    if(fcntl(0, F_SETFL, flags) < 0)
    {
        perror("fail to fcntl");
        exit(1);
    }
【4】多路複用I/O
    應用程序中同時處理多路輸入輸出流,若採用阻塞模式,將得不到預期的目的;
    若採用非阻塞模式,對多個輸入進行輪詢,但又太浪費CPU時間;
    若設置多個進程,分別處理一條數據通路,將新產生進程間的同步與通訊問題,使程序變得更加複雜;
    比較好的方法是使用I/O多路複用。其基本思想是:
        先構造一張有關描述符的表,而後調用一個函數。當這些文件描述符中的一個或多個已準備好進行I/O時函數才返回。
        函數返回時告訴進程那個描述符已就緒,能夠進行I/O操做。

    使用select實現IO多路複用
    #include <sys/select.h>
    #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);
    功能:容許與一個程序能夠操做多個文件描述符,阻塞等待文件描述符準備就緒,
          若是有一個或者多個文件描述符準備就緒,函數當即返回,並執行相應的IO操做
    參數:
        nfds:最大的文件描述符加1
        readfds:讀文件描述符集合
        writefds:寫文件描述符集合
        exceptfds:其餘或者異常的文件描述符集合
        timeout:超時
            NULL 阻塞
    返回值:
        成功:準備就緒的文件描述符的個數
        失敗:-1
        
    void FD_ZERO(fd_set *set);
    清空一個集合
    
    void FD_SET(int fd, fd_set *set);
    將文件描述符fd添加到集合set裏面
    
    void FD_CLR(int fd, fd_set *set);
    將文件描述符fd從集合set裏面移除
    
    int  FD_ISSET(int fd, fd_set *set);
    判斷文件描述符fd是否在集合set裏面
    
***************服務器模型*********************
【1】定義
    在網絡程序裏面,一般都是一個服務器處理多個客戶機。

    爲了處理多個客戶機的請求, 服務器端的程序有不一樣的處理方式。

    目前最經常使用的服務器模型.
        循環服務器:
            循環服務器在同一個時刻只能響應一個客戶端的請求
            TCP循環服務器
            UDP循環服務器
        併發服務器:
            併發服務器在同一個時刻能夠響應多個客戶端的請求
            TCP併發服務器
            UDP併發服務器
【2】如何實現tcp併發服務器
    方法1:使用父子進程實現tcp併發服務器
    socket()
    sockaddr_in
    bind()
    listen()
    //如何處理殭屍進程
    while(1)
    {
        accept()
        pid = fork;
        if(pid > 0)  //父進程負責鏈接
        {
            
        }
        else if(pid == 0) //子進程負責通訊
        {
            while(1)
            {
                recv()/send()
            }
        }
    }
    
    方法2:使用select函數實現tcp併發服務器
    #include <sys/select.h>
    #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);
    功能:容許與一個程序能夠操做多個文件描述符,阻塞等待文件描述符準備就緒,
          若是有一個或者多個文件描述符準備就緒,函數當即返回,並執行相應的IO操做
    參數:
        nfds:最大的文件描述符加1
        readfds:讀文件描述符集合
        writefds:寫文件描述符集合
        exceptfds:其餘或者異常的文件描述符集合
        timeout:超時
            NULL 阻塞
    返回值:
        成功:準備就緒的文件描述符的個數
        失敗:-1
        
    void FD_ZERO(fd_set *set);
    清空一個集合
    
    void FD_SET(int fd, fd_set *set);
    將文件描述符fd添加到集合set裏面
    
    void FD_CLR(int fd, fd_set *set);
    將文件描述符fd從集合set裏面移除
    
    int  FD_ISSET(int fd, fd_set *set);
    判斷文件描述符fd是否在集合set裏面
    
    推薦書籍:
        tcp/ip 詳解 卷一 卷二 卷三
        UNIX環境高級編程
        UNIX網絡編程 卷一 卷二
        unix/linux系統編程手冊
    
    做業:
        使用poll實現io多路複用,fgets、accept
        基於udp的網絡聊天室
*********************day_3********************
【1】網絡信息檢索函數
    getsockopt( ) 獲取一個套接口選項
    #include <sys/socket.h>

    int getsockopt(int socket, int level, int option_name,
            void *restrict option_value, socklen_t *restrict option_len);
    功能:獲取一個套接字的選項
    參數:
        socket:文件描述符
        level:層次
            SOL_SOCKET 套接字層次
            IPPROTO_IP IP層次
            IPPROTO_TCP TCP層次
        option_name:選項的名稱(SOL_SOCKET)
            SO_BROADCAST 是否容許發送廣播信息
            SO_REUSEADDR 是否容許重複使用本地地址
            SO_SNDBUF 獲取發送緩衝區的大小
            SO_RCVBUF 獲取接收緩衝區的大小
            SO_RCVTIMEO 設置接收超時時間
            SO_SNDTIMEO 設置發送超時時間
        option_value:獲取到的值
        option_len:option_value的大小
    返回值:
        成功:0
        失敗:-1
**************網絡超時檢測***************
【1】必要性
    在網絡通訊中,不少操做會使得進程阻塞
    TCP套接字中的recv/accept/connect
    UDP套接字中的recvfrom
    超時檢測的必要性
        避免進程在沒有數據時無限制地阻塞
        當設定的時間到時,進程從原操做返回繼續運行
【2】本質
    阻塞函數若是沒有數據到來時會一直等待
    非阻塞即便沒有數據到來,函數也會當即返回
    網絡超時檢測的本質是設定必定的時間,在時間到達以前若是沒有數據到來,
    則一直阻塞等待,若是時間到達是尚未數據到來,則函數當即返回
【3】方法1:使用setsockopt設置網絡超時檢測
    #include <sys/socket.h>

    int setsockopt(int socket, int level, int option_name,
            const void *option_value, socklen_t option_len);
    功能:設置一個套接字的選項
    參數:
        socket:文件描述符
        level:層次
            SOL_SOCKET 套接字層次
        option_name:選項的名稱
            SO_RCVTIMEO 設置接收超時時間
        option_value:設置的值
            struct timeval
            {
                __time_t tv_sec;        秒
                __suseconds_t tv_usec;  微秒                                                                
            };
        option_len:option_value的大小
    返回值:
        成功:0
        失敗:-1
【4】方法2:使用select設置網絡超時檢測
    #include <sys/select.h>
    #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);
    功能:容許與一個程序能夠操做多個文件描述符,阻塞等待文件描述符準備就緒,
          若是有一個或者多個文件描述符準備就緒,函數當即返回,並執行相應的IO操做
    參數:
        nfds:最大的文件描述符加1
        readfds:讀文件描述符集合
        writefds:寫文件描述符集合
        exceptfds:其餘或者異常的文件描述符集合
        timeout:超時
            struct timeval {
               long    tv_sec;         /* seconds */
               long    tv_usec;        /* microseconds */
            };
            
            >0 超時時間
            0  當即返回
            NULL 一直阻塞
    返回值:
        成功:準備就緒的文件描述符的個數
        失敗:-1
        
    void FD_ZERO(fd_set *set);
    清空一個集合
    
    void FD_SET(int fd, fd_set *set);
    將文件描述符fd添加到集合set裏面
    
    void FD_CLR(int fd, fd_set *set);
    將文件描述符fd從集合set裏面移除
    
    int  FD_ISSET(int fd, fd_set *set);
    判斷文件描述符fd是否在集合set裏面
【5】使用alarm鬧鐘實現網絡超時檢測
    若是直接使用alarm,當時間到達時,會退出整個進程
    若是結合信號,當時間到達時,觸發SIGALRM信號,執行信號處理函數
    當信號處理函數執行完畢以後,會接着剛纔的位置繼續執行,這一屬性
    稱之爲自重啓屬性,若是想實現超時,須要關閉這一屬性,
    
    使用sigaction函數設置信號的行爲
    #include <signal.h>

    int sigaction(int signum, const struct sigaction *act,
                     struct sigaction *oldact);
    功能:設置一個信號的行爲
    參數:    
        signum:信號
        act:新的行爲
        oldact:舊的行爲
            struct sigaction {
                void     (*sa_handler)(int);  信號處理函數
                void     (*sa_sigaction)(int, siginfo_t *, void *); 信號處理函數
                sigset_t   sa_mask; 掩碼(有關阻塞)
                int        sa_flags; 標誌位
                    SA_RESTART 自重啓屬性
                void     (*sa_restorer)(void);  沒有用
            };
    返回值:
        成功:0
        失敗:-1

    注意:對寄存器或者位的操做,通常執行讀、改、寫三步
    
********************廣播*************
【1】定義
    前面介紹的數據包發送方式只有一個接受方,稱爲單播
    若是同時發給局域網中的全部主機,稱爲廣播
    只有用戶數據報(使用UDP協議)套接字才能廣播
    
    廣播地址
    以192.168.3.0 (255.255.255.0) 網段爲例,最大的主機地址192.168.3.255表明該網段的廣播地址
    發到該地址的數據包被全部的主機接收
    255.255.255.255在全部網段中都表明廣播地址
    
【2】流程(基於udp)
    發送者:    
        建立套接字 socket( )
        填充廣播信息結構體 sockaddr_in
        設置爲容許發送廣播權限 setsockopt( )
        接收數據 sendto( )
        
    接收者:
        建立套接字 socket( )
        填充廣播信息結構體 sockaddr_in
        將套接字與廣播信息結構體綁定 bind( )
        接收數據 recvfrom( )
【3】設置爲容許發送廣播權限
    #include <sys/socket.h>

    int setsockopt(int socket, int level, int option_name,
            const void *option_value, socklen_t option_len);
    功能:設置一個套接字的選項
    參數:
        socket:文件描述符
        level:層次
            SOL_SOCKET 套接字層次
        option_name:選項的名稱
            SO_BROADCAST 設置接收超時時間
        option_value:設置的值
            0 不容許
            1 容許
        option_len:option_value的大小
    返回值:
        成功:0
        失敗:-1

***************組播***************
【1】定義
    單播方式只能發給一個接收方。
    廣播方式發給全部的主機。過多的廣播會大量佔用網絡帶寬,形成廣播風暴,影響正常的通訊。
    組播(又稱爲多播)是一種折中的方式。只有加入某個多播組的主機才能收到數據。
    多播方式既能夠發給多個主機,又能避免象廣播那樣帶來過多的負載(每臺主機要到傳輸層才能判斷廣播包是否要處理)

    D類地址(組播地址)
        不分網絡地址和主機地址,第1字節的前4位固定爲1110
        224.0.0.1 – 239.255.255.255

    
【2】流程
    發送者:
        建立套接字 socket( )
        填充組播信息結構體 sockaddr_in
        發送數據 sendto( )
    接收者:
        建立套接字 socket( )
        填充組播信息結構體 sockaddr_in
        將套接字與組播信息結構體綁定 bind( )
        設置爲加入多播組 setsockopt( )
        接收數據 recvfrom( )
        
【3】加入多播組
    #include <sys/socket.h>

    int setsockopt(int socket, int level, int option_name,
            const void *option_value, socklen_t option_len);
    功能:設置一個套接字的選項
    參數:
        socket:文件描述符
        level:層次
            IPPROTO_IP     IP層次
        option_name:選項的名稱
            IP_ADD_MEMBERSHIP 加入多播組
        option_value:設置的值
            struct ip_mreq  {
                struct in_addr imr_multiaddr;   組播地址
                struct in_addr imr_interface;   本地地址
                        INADDR_ANY 任意主機地址
            };       
        option_len:option_value的大小
    返回值:
        成功:0
        失敗:-1
    
    例子:
        struct ip_mreq mreq;
        mreq.imr_multiaddr.s_addr = inet_addr(argv[1]);
        mreq.imr_interface.s_addr = htonl(INADDR_ANY);
        
        if(setsockopt(sockfd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) < 0)
        {
            perror("fail to setsockopt");
            exit(1);
        }
    
*************使用poll實現IO多路複用****************
    實現fgets、accept兩個阻塞函數
    
    #include <poll.h>

    int poll(struct pollfd *fds, nfds_t nfds, int timeout);
    功能:同select
    參數:
        fds:結構體數組
             struct pollfd {
                int   fd;         文件描述符
                
                short events;  請求的事件
                    POLLIN        普通或優先級帶數據可讀
                    POLLRDNORM    普通數據可讀
                    POLLRDBAND    優先級帶數據可讀
                    POLLPRI        高優先級數據可讀
                    POLLOUT        普通數據可寫
                    POLLWRNORM    普通數據可寫
                    POLLWRBAND    優先級帶數據可寫
                    POLLERR        發生錯誤
                    POLLHUP        發生掛起
                    POLLNVAL    描述字不是一個打開的文件
                    
                short revents;    返回的事件
            };
        nfds:文件描述符的個數
        timeout:超時
            <0    永遠等待
            0    當即返回,不阻塞進程
            >0    等待指定數目的毫秒數
    返回值:
        成功:準備就緒的文件描述符的個數
        失敗:-1    
    
******************本地通訊********************
【1】定義
    socket一樣能夠用於本地通訊
    建立套接字時使用本地協議PF_UNIX(或PF_LOCAL)。
    分爲流式套接字和用戶數據報套接字
    和其餘進程間通訊方式相比使用方便、效率更高
    經常使用於先後臺進程通訊

【2】本地信息結構體  sockaddr_un
    #include <sys/un.h>
    
    struct sockaddr_un                                                                                               
    {
        __SOCKADDR_COMMON (sun_);
            ==>
                #define __SOCKADDR_COMMON(sa_prefix) \                                                                           
                        sa_family_t sa_prefix##family
            ==>
                sa_family_t sun_family;  //地址族 AF_UNIX

        char sun_path[108];  路徑名
    };
    
【3】TCP本地通訊
    服務器:
        建立套接字 socket( )
        填充本地信息結構體 sockaddr_un
        將套接字與本地信息結構體綁定 bind( )
        將套接字設置爲監聽模式 listen( )
        阻塞等待客戶端的鏈接請求 accept( )
        進行通訊 recv( )/send( )
    客戶端:
        建立套接字 socket( )
        填充本地信息結構體 sockaddr_un
        發送客戶端的鏈接請求 connect( )
        進行通訊 send( )/recv( )
    linux

相關文章
相關標籤/搜索