《UNIX網絡編程》筆記 - 套接字選項/UDP套接字

套接字選項

繼承自監聽套接字的選項

  • SO_DEBUG
  • SO_NOROUTE
  • SO_KEEPALIVE
  • SO_LINGER
  • SO_OOBINLINE
  • SO_RCVBUF
  • SO_RCVLOWWAT
  • SO_SNDBUF
  • SO_SNDLOWAT
  • TCP_MAXSEG
  • TCP_NODELAY

這幾個屬性是從監聽套接字繼承的,要想設置已鏈接套接字的這些屬性,須要在監聽套接字上設置。linux

SO_KEEPALIVE

若是設置了這個選項,當2小時內套接字的任一方向都沒有數據交換時,TCP會自動發送一個探活數據包(keep-alive probe),接下來會有幾種可能:算法

  1. 對端正常響應,這時不會通知應用程序。接下來2小時內若是仍沒有數據,發送另外一個探活數據包。
  2. 對端響應RST,表示對端已經崩潰並從新啓動,這時將SO_ERROR設置爲ECONNRESET,關閉套接字。
  3. 對端沒有任何響應,這時會繼續發送探活數據包試圖得到響應(不一樣系統的重試次數和間隔不一樣),若是仍沒有任何響應則放棄。這時會將SO_ERROR設置爲ETIMEOUT,關閉套接字。
  4. 套接字收到一個ICMP錯誤(好比host unreachable),這時會將SO_ERROR設置爲對應的錯誤,而後關閉套接字。

SO_LINGER

這個選項表示如何關閉面向鏈接的協議,默認是調用close時當即返回,但若是發送緩衝區有殘留的數據,會嘗試將其發送給對端。服務器

設置時經過如下結構體來控制參數:網絡

struct linger {
	int     l_onoff;                /* option on/off */
	int     l_linger;               /* linger time, 單位爲秒*/
};
複製代碼
  1. l_onoff爲0時表示選項關閉,l_linger的值被忽略,調用close時會馬上返回
  2. l_onoff爲非0而l_linger爲0時,調用close關閉某個鏈接時TCP會停止該鏈接,即丟棄發送緩衝區的全部數據並向對端發送一個RST,而不是進行正常的四次揮手。這樣可以避免TCP的TIME_WAIT狀態,可是也可能出現2MSL內建立出該鏈接的化身的狀況,致使來自剛纔被終止的鏈接上的舊的數據被髮送到新的化身上。
  3. l_onoff爲非0並且l_linger也爲非0時,關閉套接字時內核將會拖延一段時間。若是此時發送緩衝區中有數據,進程將會進入睡眠,直到:(a) 全部數據都已發送並被對端確認;(b) 拖延時間到。若是套接字被設置爲非阻塞型,close會當即返回,即便拖延時間爲非0的狀況也是。在使用SO_LINGER選項時,應該檢查close的返回值,若是在數據發送完並被確認前拖延時間到的話,close會返回EWOULDBLOCK,且發送緩衝區的數據都會被丟棄。

SO_REUSEADDR

  1. SO_REUSEADDR容許一個監聽套接字綁定到其衆所周知端口,即便之前創建的將該端口做爲本地端口的鏈接仍存在。好比監聽的進程中途關閉了但其創建的子進程和鏈接仍存在,這時監聽進程重啓時嘗試從新綁定端口時須要指定了SO_REUSEADDR才行,不然會綁定失敗。
  2. 容許在同一個端口上啓動同一服務器的多個實例,只要每一個實例綁定不一樣的本地地址便可

TCP_MAXSEG

獲取或設置TCP最大分節大小(MSS),表示TCP可以發送的最大數據量,一般由對端的SYN分節指定,除非咱們選擇一個更小的值。若是在鏈接創建以前查詢,返回的是默認值。異步

TCP_NODELAY

開啓該選項將禁用Nagle算法,默認狀況其是啓用的。nagle算法能夠減小大量小的數據包在網絡中傳輸的狀況。函數

TCP_CORK

linux2.4以後的版本才支持選項,開啓該選項將啓用cork算法,默認是禁用的。cork選項能夠禁止小的數據包在網絡中傳輸。性能

fcntl(file control)

fcntl顧名思義,能夠對描述符進行各類控制操做,主要經過cmd和arg兩個參數來控制。大數據

int fcntl(int fd, int cmd, .../* args */) 複製代碼

可選的cmd有:spa

  • F_SETFL 設置flag
  • F_GETFL 獲取flag
  • F_SETOWN 設置套接字屬主
  • F_GETOWN 獲取套接字屬主

使用方式:指針

int flags;
//先獲取當前flags
if ((flags = fcntl(fd, F_GETFL, 0)) < 0) {
    //error
}
//增長O_NONBLOCK選項
flags |= O_NONBLOCK;
//設置flags
if ((flags = fcntl(fd, F_SETFL, flag)) < 0) {
    //error
}
//設置flags
if (fcntl(fd, F_SETFL, flags) < 0) {
    //error
}
//關閉O_NONBLOCK選項
flags &= ~O_NONBLOCK
//設置flags
if (fcntl(fd, F_SETFL, flags) < 0) {
    //error
}
複製代碼

在使用fcntl時,必須先獲取當前的標誌,而後與新的標誌或以後再設置標誌,不然會清除描述符的其餘標誌。

UDP套接字

sendto和recvfrom

ssize_t sendto(int fd, const void *buff, size_t nbytes,
    int flags, const struct sockaddr *to, socklen_t *addrlen);
ssize_t recvfrom(int fd, void *buff, size_t nbytes,
    int flags, struct sockaddr *from, socklen_t *addrlen);
複製代碼

函數返回發送或者接收的字節數,最後兩個參數指定了對端地址。若是recvfrom的後兩個參數是空指針,則表示不關心數據發送者的協議地址。

異步錯誤

在一個UDP套接字上調用sendto時,若是對端不可用,對端會返回一個ICMP消息(好比"port unreachable"),但這個錯誤不會返回給應用程序,sendto仍可以正常返回。

咱們稱這裏的這個ICMP錯誤爲異步錯誤,這個錯誤由sendto觸發,可是sendto自己卻成功返回;緣由是sendto的成功僅表示在網絡接口輸出隊列中具有足夠的空間存放sendto造成的IP數據報,而真正的錯誤在隨後實際發出數據包的時候才發生,因此咱們稱這個錯誤是異步的。

對於異步錯誤,處理的基本規則是:對於一個UDP套接字,由它引起的異步錯誤不會返回給它,除非它已鏈接。這裏的已鏈接指的是成功調用了connect函數。爲何這麼規定呢?由於一個UDP套接字可能會往多個對端發送和讀取數據,而sendtorecvfrom只能返回單純的errno,不能返回對端的ip和端口號信息。因此咱們決定:只有在UDP已經只綁定到一個對端時,這些異步錯誤才返回給進程。

connect

除非udp套接字事先成功調用了connect,不然sendtorecvfrom發生的異常不會返回給應用程序。

對一個UDP套接字調用connect函數並不會像TCP套接字那樣進行三次握手,而是先檢查傳入的地址是否合法(是否可達等)而後保存ip和port到傳入的套接字地址結構體中,接着直接返回。

一旦一個UDP套接字已鏈接,會發生三個變化:

  1. sendto不能再指定最後兩個參數(目標地址和地址長度),必須設置爲空指針;或者改用write或者send
  2. recvfrom不能再指定最後兩個參數(目標地址和地址長度),必須設置爲空指針;或者改用read/recv/recvmsg
  3. 由已鏈接UDP套接字引起的異步錯誤會返回給進程。

屢次調用connect

和TCP套接字不一樣,UDP套接字能夠屢次調用connect函數,經過這樣作能夠達到兩個目的:

  1. 爲套接字指定新的對端ip和port
  2. 斷開套接字(將套接字地址結構體的地址族設置爲AF_UNSPEC便可)

connect與性能

對於未鏈接的UDP套接字,每次調用sendto函數時都會隱式地進行套接字鏈接和斷開鏈接;因此若是肯定UDP套接字要發送的對端只有一個時,能夠經過顯式鏈接來提升效率。

相關文章
相關標籤/搜索