日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > linux >内容正文

linux

Linux网络编程 | socket选项设定 及 网络信息API

發布時間:2023/12/13 linux 25 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Linux网络编程 | socket选项设定 及 网络信息API 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

文章目錄

  • 讀取和設置 socket 選項
    • SO_REUSEADDR
    • SO_RCVBUF 和 SO_SNDBUF
    • SO_RCVLOWAT 和 SO_SNDLOWAT
    • SO_LINGER 選項
  • 網絡信息API
    • gethostbyname 和 gethostbyaddr
    • getservbyname 和 getservbyport
    • getaddrinfo
    • getnameinfo


讀取和設置 socket 選項

正如 fcntl 系統調用是控制文件描述符屬性的通用 POSIX 方法;socket文件描述符的屬性也有兩個系統調用專門 讀取設置

#include<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, void* option_value, socklen_t* restrict option_len); // sockfd 指定被操作的socket。 // level 指定操作哪個協議的選項(屬性),如:IPv4、IPv6、TCP等。 // option_name 指定選項的名字。 // option_value 和 option_len 參數分別指定操作選項值和長度。 // 成功返回0,失敗返回-1并置errno。 socket 選項 leveloption數據類型說明必須在 listen/connect 調用之前設置
SOL_SOCKETSO_DEBUGint打開調試信息YES
(通用 socket 選項,與協議無關)SO_REUSEADDRint重用本地地址。如:可以令服務器處于 TIME_WAIT 的端口立刻被使用。
SO_TYPEint獲取 socket 類型
SO_ERRORint獲取并清除 socket 錯誤狀態
SO_DONTROUTEint不查看路由表,直接將數據發送給本地局域網內的主機。類同 send 系統調用的 MSG_DONTROUTE。YES
SO_RCVBUFintTCP接收緩沖區大小(最小值是256字節)YES
SO_SNDBUFintTCP發送緩沖區大小(最小值是2048字節)YES
SO_RCVLOWATintTCP接收緩沖區低水位標記YES
SO_SNDLOWATintTCP發送緩沖區低水位標記YES
SO_RCVLOWATint接收數據超時
SO_SNDLOWATint發送數據超時
SO_KEEPALIVEint發送周期性保活報文以維持連接YES
SO_OBBINLINEint接收到的帶外數據將 在線存留 在普通數據的輸入隊列中,此時我們不能使用帶 MSG_OOB 的讀操作來讀取帶外數據,而應該像讀取普通數據那樣讀取帶外數據。YES
SO_LINGERintger若有數據待發送,則延遲關閉。YES
IPPROTO_IPIP_TOSint服務類型
(IPv4選項)IP_TTLint存活時間
IPPROTO_IPV6IPV6_NEXTHOPsockaddr_in6下一跳IP地址
(IPv6選項)IPV6_RECVPKTINFOint接受分組信息
IPV6_DONTFRAGint禁止分片
IPV6_RECVTCLASSint接受通信類型
IPPROTO_TCPTCP_MAXSEGintTCP最大報文段大小YES
(TCP選項)TCP_NODELAYint禁止Nagle算法YES

服務器 而言,部分socket選項 只能在調用 listen 系統調用前 針對 socket 設置才有效。這是因為連接 socket 只能由 accept 調用返回,而 accept 從 listen 監聽隊列 中接受的連接至少是個 半連接 ,這說明 服務器 已經往 客戶端 上發送出了 TCP同步報文段(執行完了三次握手中的前兩次)。但 部分 socket 選項只能在 TCP同步報文中設置 ,如:TCP最大報文段選項。對此有兩種解決方案:

  • 對于服務器而言,執行 listen系統調用 時設置這些 socket選項,那么 accept 返回的 連接socket 將自動繼承這些選項(注1)。
  • 對于客戶端而言,這些 socket選項 應該在調用 connect函數 之前設置,因為 connect 調用成功之后三次握手已完成。

注1

這些選項包括:SO_DEBUG、SO_DONTROUTE、SO_KEEPALIVE、SO_LINGER、SO_OBBINLINE、SO_RCVBUF、SO_RCVLOWAT、TCP_MAXSEG、TCP_NODELAY 。


SO_REUSEADDR

服務器程序可以通過設置本選項來強制使用被處于 TIME_WAIT 狀態的連接占用的 socket 地址。此外,我們也可以通過修改內核參數 /proc/sys/net/ipv4/tcp_tw_recycle 來快速回收被關閉的 socket,甚至使 TCP 連接根本就不進入 TIME_WAIT 狀態。

int sock = socket( PF_INET, SOCK_STREAM, 0); // TCP協議,IPv4版本,基于流服務,0:使用默認協議 assert( sock >= 0); // 檢測創建sock是否成功 int reuse = 1; setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse) ); // 操作描述符為sock的socket套接字,操作的屬性是通用socket選項 // 選項類型為SO_REUSEADDR,SO_REUSEADDR的數據類型為int // 1即為啟用SO_REUSEADDR選項

SO_RCVBUF 和 SO_SNDBUF

這兩個選項分別用來表示 TCP 接收緩沖區大小和發送緩沖區大小。不過,當我們 通過setsockopt 來設置 TCP 的接收、發送緩沖區大小時,系統都會將其值加倍,并且不小于某個值。

一般來講,TCP 接收緩沖區的最小值是 256 字節,發送緩沖區的最小值是 2048 字節(不同操作系統可能有差異)。我們可以直接修改內核參數 /proc/sys/net/ipv4/tcp_rmem 和 /proc/sys/net/ipv4/tcp_wmem 來強制緩沖區沒有最小值限制。

/* 先設置 TCP 接收緩沖區的大小,然后立即讀取 */ setsockopt( sock, SOL_SOCKET, SO_RCVBUF, &recvbuf, sizeof(recvbuf) ); getsockopt( sock, SOL_SOCKET, SO_RCVBUF, &recvbuf, ( socklen_t* )&len ); // sock 為目標套接字的文件描述符 // recvbuf 為設定的緩沖區大小 // len=sizeof( recvbuf );

SO_RCVLOWAT 和 SO_SNDLOWAT

該兩個選項分別表示接收、發送緩沖區的低水位標記。一般被 I/O復用系統調用 用來判斷 socket 是否可讀或可寫:

  • 當 接收緩沖區中 可讀數據的總數大于其低水位標記時,I/O復用系統調用 將通知應用程序可以從對應的 socket 上讀取數據
  • 當 發送緩沖區 中的空閑空間大于其低水位標記時,I/O復用系統調用 將通知應用程序可以往對應的 socket 上寫入數據。

默認情況下,兩者均為 1字節 。


SO_LINGER 選項

該選項用于控制 close系統調用 在關閉 TCP 連接時的行為。默認情況下,當我們使用 close系統調用 來關閉一個 socket 時,close 將立即返回,TCP 模塊負責把該 socket 對應的 TCP 發送緩沖區中殘留的數據發送給對方。

設置(獲取)SO_LINGER 選項的值時,我們需要給 setsockopt(getsockopt)系統調用傳遞一個 linger 類型的結構體:

#include <sys/socket.h>struct linger{int l_onoff; // 開啟(非0)/關閉(0)該選項int l_linger; // 滯留時間 };

根據 linger 結構體中兩個成員變量的不同值,close 系統調用可能產生如下行為:

  • l_onoff 等于 0。 此時 SO_LINGER 選項不起作用,close 用默認行為來關閉 socket。
  • l_onoff 不為 0,l_linger 等于 0。 此時 close系統調用 立即返回,TCP模塊 將丟棄被關閉的 socket 對應的 TCP發送緩沖區 中殘留的數據,同時給對方發送一個 復位報文段。這為服務器提供了異常終止一個連接的方法。
  • l_onoff 不為 0,l_linger 大于 0。 此時 close 的行為取決于兩個條件:
  • 被關閉的 socket 對應的 發送緩沖區 中是否還有殘留的數據;
  • 該 socket 是阻塞的還是非阻塞的,阻塞: close 將等待一段長為 l_linger 的時間,直到 TCP模板 發送完所有殘留數據并得到對方的確認。如果沒發送完并得到確認,則 close 返回 -1 并設置 errno 為 EWOULDBLOCK。非阻塞: close 將立即返回,此時需要根據其 返回值 和 errno 來判斷殘留數據是否發送完畢。

網絡信息API

gethostbyname 和 gethostbyaddr

  • gethostbyname: 根據主機名稱獲取主機的完整信息。通常先在本地的 /etc/hosts 配置文件中查找主機,沒有找到再去訪問 DNS 服務器。
  • gethostbyaddr: 根據IP地址獲取主機的完整信息。
#include<netdb.h> struct hostent* gethostbyname( const char* name ); struct hostent* gethostbyaddr( const void* addr, size_t len, int type ); // len IIP地址長度 // type 指定addr所指IP地址的類型,可以是AF_INET或AFINET6

兩者的返回類型都是 hostent 結構體類型的指針:

#include<netdb.h> struct hostent{char* h_name; // 主機名char** h_aliases; // 主機別名列表,可有多個int h_addrtype; // 地址類型(地址族)int h_length; // 地址長度char** h_addr_list; // 按網絡字節序列出的主機IP地址列表 };

getservbyname 和 getservbyport

根據名稱/端口號獲得某個服務的完整信息。實際上都是通過讀取 /etc/services 文件來獲取服務的信息的。

#include<netdb.h> struct servent* getservbyname( const char* name, const char* proto ); struct servent* getservbyport( int port, const char* proto ); // name 目標服務的名字 // port 目標服務對應的端口號 // proto 服務類型,tcp表流服務、udp表數據報服務、NULL表獲取所有類型的服務

兩者的返回類型都是 servent 結構體類型的指針:

#include<netdb.h> struct servent{char* s_name; // 服務名稱char** s_aliases; // 服務別名列表,可有多個int s_port; // 端口號char* s_proto; // 服務類型,通常是 tcp 或 udp };

不可重入

gethostbyname、gethostbyaddr、getservbyname 和 getservbyport 都是不可重入的,即非線程安全的。但是 netdb.h 頭文件給出了它們的可重入版本:在原函數名尾部加上 _r(re-entrant)。


getaddrinfo

既能通過主機名獲取 IP 地址(內部使用的是 gethostbyname),也能通過服務名獲取端口號(內部使用的是 getservbyname),是否可重入取決于內部調用的函數( gethostbyname、 getservbyname )是否是它們的可重入版本。

#include<netdb.h> int getaddrinfo( const char* hostname, const char* service, const struct addrinfo* hints, struct addrinfo** result ); // hostname:可以接收主機名(服務名)/字符串表示的IP地址【IPv4使用點分十進制字符串、IPv6使用十六進制字符串】 // service:可以接收服務名/字符串表示的十進制端口號 // hints:可以為NULL,表示允許反饋任何可用的結果。 // result:指向一個用于存儲反饋結果的鏈表

getaddrinfo 將隱式分配堆內存,因此調用結束后,必須釋放這塊內存:

#include <netdb.h> void freeaddrinfo( struct addrinfo* res );

getaddrinfo 反饋的每一條結果都是 addinfo 結構體類型的對象:

struct addrinfo {int ai_flags; // 標志int ai_family; // 地址族int ai_socktype; // 服務類型,SOCK_STREAM或SOCK_DGRAMint ai_protocol; // 具體的網絡協議,等同于socket系統調用的第三個參數,常被設為0以表自動匹配對應協議。socklen_t ai_addrlen; // socket地址ai_addr的長度char* ai_canonname; // 主機的別名struct sockaddr* ai_addr; // 指向socket地址struct addrinfo* ai_next; // 指向下一個 sockinfo 結構的對象 };

ai_flags成員可以取下表中的標志的按位或:

選項含義
AI_PASSIVE套接字地址將用于調用 bind 函數,服務器通常需要設置以表接受任何本地 socket 地址上的服務請求。客戶端不能設置。
AI_CANONNAME返回主機的別名
AI_NUMERICHOSThostname 參數必須是IP地址字符串,避免了DNS查詢。
AI_NUMERICSERV強制 service 參數必須是十進制端口號字符串,不能是服務名。
AI_V4MAPPED如果對 IPv6 地址的 getaddrinfo 請求失敗,則將 IPv4 映射為 IPv6 地址格式。
AI_ALL必須和 AI_V4MAPPED 同時使用,否則將被忽略。同時返回 符合條件 和 由IPv4轉換而來 的 IPv6地址。
AI_ADDRCONFIG只有至少配置了一個IPv4/IPv6地址(除了回路地址)后,getaddrinfo 才會解析。和 AI_V4MAPPED 互斥。

當我們使用 hints 參數時,可以設置 addrinfo 中前四個成員,其他成員必須設置為 NULL。


getnameinfo

內部使用 gethostbyaddr 和 getservbyport,是否可重入取決于內部調用的函數版本是否可重入:

#include<netdb.h> int getnameinfo( const struct sockaddr* sockaddr, socklen_t addrlen, char* host, socklen_t hostlen, char* serv, socklen_t servlen, int flags ); // 將返回的主機名(服務名)存儲在 host(serv) 參數指向的緩存中 // flags控制getnameinfo的行為

getaddrinfo 和 getnameinfo 成功時返回0,失敗返回錯誤碼。Linux下 strerror 函數能將數值錯誤碼 error 轉換為易讀的字符串形式:

#include<netdb.h> const char* gai_strerror( int error );

總結

以上是生活随笔為你收集整理的Linux网络编程 | socket选项设定 及 网络信息API的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。