(七)Linux網絡編程--7. TCP/IP協議 8. 套接字選項

你也許聽說過TCP/IP協議,那麼你知道到底什麼是TCP,什麼是IP嗎?在這一章裏面,我們一起來學習這個目前網絡上用最廣泛的協議.
7.1 網絡傳輸分層
如果你考過計算機等級考試,那麼你就應該已經知道了網絡傳輸分層這個概念.在網絡上,人們爲了傳輸數據時的方便,
把網絡的傳輸分爲7個層次.分別是:應用層,表示層,會話層,傳輸層,網絡層,數據鏈路層和物理層.分好了層以後,傳輸數據時,
上一層如果要數據的話,就可以直接向下一層要了,而不必要管數據傳輸的細節.下一層也只向它的上一層提供數據,
而不要去管其它東西了.如果你不想考試,你沒有必要去記這些東西的.只要知道是分層的,而且各層的作用不同.
7.2 IP協議
IP協議是在網絡層的協議.它主要完成數據包的發送作用. 下面這個表是IP4的數據包格式

0 4 8 16 32

|版本 |首部長度|服務類型| 數據包總長 |

| 標識 |DF |MF| 碎片偏移 |

| 生存時間 | 協議 | 首部較驗和 |

| 源IP地址 |

| 目的IP地址 |

| 選項 |

| 數據 |

下面我們看一看IP的結構定義
struct ip
{

if __BYTE_ORDER == __LITTLE_ENDIAN

    unsigned int ip_hl:4;           /* header length */
    unsigned int ip_v:4;            /* version */

endif

if __BYTE_ORDER == __BIG_ENDIAN

    unsigned int ip_v:4;            /* version */
    unsigned int ip_hl:4;           /* header length */

endif

    u_int8_t ip_tos;                /* type of service */
    u_short ip_len;                 /* total length */
    u_short ip_id;                  /* identification */
    u_short ip_off;                 /* fragment offset field */

define IP_RF 0x8000 /* reserved fragment flag */

define IP_DF 0x4000 /* dont fragment flag */

define IP_MF 0x2000 /* more fragments flag */

define IP_OFFMASK 0x1fff /* mask for fragmenting bits */

    u_int8_t ip_ttl;                /* time to live */
    u_int8_t ip_p;                  /* protocol */
    u_short ip_sum;                 /* checksum */
    struct in_addr ip_src, ip_dst;  /* source and dest address */

};
ip_vIP協議的版本號,這裏是4,現在IPV6已經出來了
ip_hlIP包首部長度,這個值以4字節爲單位.IP協議首部的固定長度爲20個字節,如果IP包沒有選項,那麼這個值爲5.
ip_tos服務類型,說明提供的優先權.
ip_len說明IP數據的長度.以字節爲單位.
ip_id標識這個IP數據包.
ip_off碎片偏移,這和上面ID一起用來重組碎片的.
ip_ttl生存時間.沒經過一個路由的時候減一,直到爲0時被拋棄.
ip_p協議,表示創建這個IP數據包的高層協議.如TCP,UDP協議.
ip_sum首部校驗和,提供對首部數據的校驗.
ip_src,ip_dst發送者和接收者的IP地址
關於IP協議的詳細情況,請參考 RFC791
7.3 ICMP協議
ICMP是消息控制協議,也處於網絡層.在網絡上傳遞IP數據包時,如果發生了錯誤,那麼就會用ICMP協議來報告錯誤.
ICMP包的結構如下:

0 8 16 32

| 類型 | 代碼 | 校驗和 |

| 數據 | 數據 |

ICMP在中的定義是
struct icmphdr
{
u_int8_t type; /* message type */
u_int8_t code; /* type sub-code */
u_int16_t checksum;
union
{
struct
{
u_int16_t id;
u_int16_t sequence;
} echo; /* echo datagram */
u_int32_t gateway; /* gateway address */
struct
{
u_int16_t __unused;
u_int16_t mtu;
} frag; /* path mtu discovery */
} un;
};
關於ICMP協議的詳細情況可以查看 RFC792
7.4 UDP協議
UDP協議是建立在IP協議基礎之上的,用在傳輸層的協議.UDP和IP協議一樣是不可靠的數據報服務.UDP的頭格式爲:

0 16 32

| UDP源端口 | UDP目的端口 |

| UDP數據報長度 | UDP數據報校驗 |

UDP結構在中的定義爲:
struct udphdr {
u_int16_t source;
u_int16_t dest;
u_int16_t len;
u_int16_t check;
};
關於UDP協議的詳細情況,請參考 RFC768
7.5 TCP
TCP協議也是建立在IP協議之上的,不過TCP協議是可靠的.按照順序發送的.TCP的數據結構比前面的結構都要複雜.

0 4 8 10 16 24 32

| 源端口 | 目的端口 |

| 序列號 |

| 確認號 |

| | |U|A|P|S|F| |
|首部長度| 保留 |R|C|S|Y|I| 窗口 |

| | |G|K|H|N|N| |

| 校驗和 | 緊急指針 |

| 選項 | 填充字節 |

TCP的結構在中定義爲:
struct tcphdr
{
u_int16_t source;
u_int16_t dest;
u_int32_t seq;
u_int32_t ack_seq;

if __BYTE_ORDER == __LITTLE_ENDIAN

u_int16_t res1:4;
u_int16_t doff:4;
u_int16_t fin:1;
u_int16_t syn:1;
u_int16_t rst:1;
u_int16_t psh:1;
u_int16_t ack:1;
u_int16_t urg:1;
u_int16_t res2:2;

elif __BYTE_ORDER == __BIG_ENDIAN

u_int16_t doff:4;
u_int16_t res1:4;
u_int16_t res2:2;
u_int16_t urg:1;
u_int16_t ack:1;
u_int16_t psh:1;
u_int16_t rst:1;
u_int16_t syn:1;
u_int16_t fin:1;

endif

u_int16_t window;
u_int16_t check;
u_int16_t urg_prt;

};
source發送TCP數據的源端口
dest接受TCP數據的目的端口
seq標識該TCP所包含的數據字節的開始序列號
ack_seq確認序列號,表示接受方下一次接受的數據序列號.
doff數據首部長度.和IP協議一樣,以4字節爲單位.一般的時候爲5
urg如果設置緊急數據指針,則該位爲1
ack如果確認號正確,那麼爲1
psh如果設置爲1,那麼接收方收到數據後,立即交給上一層程序
rst爲1的時候,表示請求重新連接
syn爲1的時候,表示請求建立連接
fin爲1的時候,表示親戚關閉連接
window窗口,告訴接收者可以接收的大小
check對TCP數據進行較核
urg_ptr如果urg=1,那麼指出緊急數據對於歷史數據開始的序列號的偏移值
關於TCP協議的詳細情況,請查看 RFC793

7.6 TCP連接的建立
TCP協議是一種可靠的連接,爲了保證連接的可靠性,TCP的連接要分爲幾個步驟.我們把這個連接過程稱爲”三次握手”.
下面我們從一個實例來分析建立連接的過程.
第一步客戶機向服務器發送一個TCP數據包,表示請求建立連接. 爲此,客戶端將數據包的SYN位設置爲1,
並且設置序列號seq=1000(我們假設爲1000).
第二步服務器收到了數據包,並從SYN位爲1知道這是一個建立請求的連接.於是服務器也向客戶端發送一個TCP數據包.
因爲是響應客戶機的請求, 於是服務器設置ACK爲1,sak_seq=1001(1000+1)同時設置自己的序列號.seq=2000(我們假設爲2000).
第三步客戶機收到了服務器的TCP,並從ACK爲1和ack_seq=1001知道是從服務器來的確認信息.於是客戶機也向服務器發送確認信息.
客戶機設置ACK=1,和ack_seq=2001,seq=1001,發送給服務器.至此客戶端完成連接.
最後一步服務器受到確認信息,也完成連接.
通過上面幾個步驟,一個TCP連接就建立了.當然在建立過程中可能出現錯誤,不過TCP協議可以保證自己去處理錯誤的.

說一說其中的一種錯誤.
聽說過DOS嗎?(可不是操作系統啊).今年春節的時候,美國的五大網站一起受到攻擊.攻擊者用的就是DOS(拒絕式服務)方式.
概括的說一下原理.客戶機先進行第一個步驟.服務器收到後,進行第二個步驟.按照正常的TCP連接,客戶機應該進行第三個步驟.
不過攻擊者實際上並不進行第三個步驟.因爲客戶端在進行第一個步驟的時候,修改了自己的IP地址,就是說將一個實際上不存在的
IP填充在自己IP 數據包的發送者的IP一欄.這樣因爲服務器發的IP地址沒有人接收,所以服務端會收不到第三個步驟的確認信號,
這樣服務務端會在那邊一直等待,直到超時.這樣當有大量的客戶發出請求後,服務端會有大量等待,直到所有的資源被用光,
而不能再接收客戶機的請求.這樣當正常的用戶向服務器發出請求時,由於沒有了資源而不能成功.
於是就出現了春節時所出現的情況.

(八)Linux網絡編程–8. 套接字選項
有時候我們要控制套接字的行爲(如修改緩衝區的大小),這個時候我們就要控制套接字的選項了.

8.1 getsockopt和setsockopt
int getsockopt(int sockfd,int level,int optname,void *optval,socklen_t *optlen)
int setsockopt(int sockfd,int level,int optname,const void *optval,socklen_t *optlen)
level指定控制套接字的層次.可以取三種值:
1)SOL_SOCKET:通用套接字選項.
2)IPPROTO_IP:IP選項.
3)IPPROTO_TCP:TCP選項.
optname指定控制的方式(選項的名稱),我們下面詳細解釋
optval獲得或者是設置套接字選項.根據選項名稱的數據類型進行轉換

選項名稱                說明                                    數據類型
========================================================================
                        SOL_SOCKET
------------------------------------------------------------------------
SO_BROADCAST            允許發送廣播數據                        int
SO_DEBUG                允許調試                                int
SO_DONTROUTE            不查找路由                              int
SO_ERROR                獲得套接字錯誤                          int
SO_KEEPALIVE            保持連接                                int
SO_LINGER               延遲關閉連接                            struct linger
SO_OOBINLINE            帶外數據放入正常數據流                  int
SO_RCVBUF               接收緩衝區大小                          int
SO_SNDBUF               發送緩衝區大小                          int
SO_RCVLOWAT             接收緩衝區下限                          int
SO_SNDLOWAT             發送緩衝區下限                          int
SO_RCVTIMEO             接收超時                                struct timeval
SO_SNDTIMEO             發送超時                                struct timeval
SO_REUSERADDR           允許重用本地地址和端口                  int
SO_TYPE                 獲得套接字類型                          int
SO_BSDCOMPAT            與BSD系統兼容                           int
==========================================================================
                        IPPROTO_IP
--------------------------------------------------------------------------
IP_HDRINCL              在數據包中包含IP首部                    int
IP_OPTINOS              IP首部選項                              int
IP_TOS                  服務類型
IP_TTL                  生存時間                                int
==========================================================================
                        IPPRO_TCP
--------------------------------------------------------------------------
TCP_MAXSEG              TCP最大數據段的大小                     int
TCP_NODELAY             不使用Nagle算法                         int
=========================================================================
關於這些選項的詳細情況請查看 Linux Programmer's Manual 
8.2 ioctl 
ioctl可以控制所有的文件描述符的情況,這裏介紹一下控制套接字的選項. 
int ioctl(int fd,int req,...)
==========================================================================
                        ioctl的控制選項
--------------------------------------------------------------------------
SIOCATMARK              是否到達帶外標記                        int
FIOASYNC                異步輸入/輸出標誌                       int
FIONREAD                緩衝區可讀的字節數                      int
==========================================================================
詳細的選項請用 man ioctl_list 查看.
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章