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 調用返回,而 acceptlisten 監聽隊列 中接受的連接至少是個 半連接 ,這說明 服務器 已經往 客戶端 上發送出了 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 的行為取決于兩個條件:
    1. 被關閉的 socket 對應的 發送緩沖區 中是否還有殘留的數據;
    2. socket 是阻塞的還是非阻塞的,阻塞: close 將等待一段長為 l_linger 的時間,直到 TCP模板 發送完所有殘留數據并得到對方的確認。如果沒發送完并得到確認,則 close 返回 -1 并設置 errnoEWOULDBLOCK非阻塞: 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
};

不可重入

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


getaddrinfo

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

#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

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

#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的行為

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

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

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/443731.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/443731.shtml
英文地址,請注明出處:http://en.pswp.cn/news/443731.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Linux | 高級I/O函數

文章目錄創建文件描述符的函數pipe函數dup函數、dup2函數讀取或寫入數據readv函數、writev函數零拷貝sendfile函數splice函數tee函數進程間通信——共享內存mmap函數 和 munmap函數控制文件描述符fcntl函數創建文件描述符的函數 pipe函數 不再贅述&#xff0c;詳情見我的另一…

分布式理論:CAP、BASE | 分布式存儲與一致性哈希

文章目錄分布式理論CAP定理BASE理論分布式存儲與一致性哈希簡單哈希一致性哈希虛擬節點分布式理論 CAP定理 一致性&#xff08;Consistency&#xff09;&#xff1a; 在分布式系統中的所有數據副本&#xff0c;在同一時刻是否一致&#xff08;所有節點訪問同一份最新的數據副…

Tomcat服務器性能優化

一、概述 本文檔主要介紹了Tomcat的性能調優的原理和方法。可作為公司技術人員為客戶Tomcat系統調優的技術指南&#xff0c;也可以提供給客戶的技術人員作為他們性能調優的指導手冊。 二、調優分類 由于Tomcat的運行依賴于JVM&#xff0c;從虛擬機的角度我們把Tomcat的調整分為…

分布式系統概念 | 分布式事務:2PC、3PC、本地消息表

文章目錄分布式事務2PC&#xff08;二階段提交協議&#xff09;執行流程優缺點3PC&#xff08;三階段提交協議&#xff09;執行流程優缺點本地消息表&#xff08;異步確保&#xff09;分布式事務 分布式事務就是指事務的參與者、支持事務的服務器、資源服務器以及事務管理器分…

數據結構算法 | 單調棧

文章目錄算法概述題目下一個更大的元素 I思路代碼下一個更大元素 II思路代碼132 模式思路代碼接雨水思路算法概述 當題目出現 「找到最近一個比其大的元素」 的字眼時&#xff0c;自然會想到 「單調棧」 。——三葉姐 單調棧以嚴格遞增or遞減的規則將無序的數列進行選擇性排序…

最長下降子序列

文章目錄題目解法DP暴搜思路代碼實現貪心二分思路代碼實現題目 給出一組數據 nums&#xff0c;求出其最長下降子序列&#xff08;子序列允許不連續&#xff09;的長度。&#xff08;類似于lc的最長遞增子序列&#xff09; 示例&#xff1a; 輸入&#xff1a; 6 // 數組元素個…

Linux 服務器程序規范、服務器日志、用戶、進程間的關系

文章目錄服務器程序規范日志rsyslogd 守護進程syslog函數openlog函數setlogmask函數closelog函數用戶進程間的關系進程組會話系統資源限制改變工作目錄和根目錄服務器程序后臺化服務器程序規范 Linux 服務器程序一般以后臺進程&#xff08;守護進程[daemon]&#xff09;形式運…

IO模型 :阻塞IO、非阻塞IO、信號驅動IO、異步IO、多路復用IO

文章目錄IO模型阻塞IO非阻塞IO信號驅動IO多路復用IO異步IOIO模型 根據各自的特性不同&#xff0c;IO模型被分為阻塞IO、非阻塞IO、信號驅動IO、異步IO、多路復用IO五類。 最主要的兩個區別就是阻塞與非阻塞&#xff0c;同步與異步。 阻塞與非阻塞 阻塞與非阻塞最主要的區別就…

Tomcat服務器集群與負載均衡實現

一、前言 在單一的服務器上執行WEB應用程序有一些重大的問題&#xff0c;當網站成功建成并開始接受大量請求時&#xff0c;單一服務器終究無法滿足需要處理的負荷量&#xff0c;所以就有點顯得有點力不從心了。另外一個常見的問題是會產生單點故障&#xff0c;如果該服務器壞掉…

Linux服務器 | 事件處理模式:Reactor模式、Proactor模式

文章目錄Reactor模式Proactor模式同步I/O模型模擬Proactor模式兩者的優缺點ReactorProactor同步I/O模型通常用于實現 Reactor 模式&#xff0c;異步I/O模型通常用于實現 Proactor 模式。&#xff08;不是絕對的&#xff0c;同步I/O也可模擬出 Proactor 模式&#xff09; React…

Linux服務器 | 服務器模型與三個模塊、兩種并發模式:半同步/半異步、領導者/追隨者

文章目錄兩種服務器模型及三個模塊C/S模型P2P模型I/O處理單元、邏輯單元、存儲單元并發同步與異步半同步/半異步模式變體&#xff1a;半同步/半反應堆模式改進&#xff1a;高效的半同步/半異步模式領導者/追隨者模式組件 &#xff1a;句柄集、線程集、事件處理器工作流程兩種服…

香農信息熵之可憐的小豬

文章目錄題目解析香農熵公式樣例具體分析代碼題目 有 n 桶液體&#xff0c;其其中 正好 有一桶含有毒藥&#xff0c;其裝的都是水。它們從外觀看起來都一樣。為了弄清楚哪只水桶含有毒藥&#xff0c;你可以喂一些豬喝&#xff0c;通過觀察豬是否會死進行判斷&#xff0c;實驗對…

字符串匹配之KMP(KnuthMorrisPratt)算法(圖解)

文章目錄最長相等前后綴next數組概念代碼實現圖解GetNext中的回溯改進代碼實現代碼復雜度分析最長相等前后綴 給出一個字符串 ababa 前綴集合&#xff1a;{a, ab, aba, abab} 后綴集合&#xff1a;{a, ba, aba, baba} 相等前后綴 即上面用同樣顏色標識出來的集合元素&#…

linux下tomcat6.0與jdk安裝詳細步驟

安裝Tomcat6.0和JDK1.6 在linux系統上安裝tomcat和jdk應該說是我學習linux知識的第一課了&#xff0c;之前只 是聽說過&#xff0c;從沒接觸過&#xff0c;但我們公司項目都是部署在linux系統上的&#xff0c;那天上司突 然給我發了幾個文檔&#xff0c;讓我看一下&#xff…

Android入門(一) | Android Studio的配置與使用

文章目錄安裝配置Android Studio使用Android Studio模擬器更改Android SDK的路徑Hello World&#xff01;安裝配置Android Studio 從這一步開始&#xff1a; 一直點 next 即可&#xff0c;直到存儲路徑的選擇上&#xff0c;可以放到非 C 盤&#xff0c;這里我放到 D 盤了&am…

Android 入門(四) | Intent 實現 Activity 切換

文章目錄Intent顯式 Intent定義兩個 xml 文件android:orientationmatch_parent 和 wrap_contentIntent函數定義兩個 Activity隱式 Intent更多隱式 Intent 的用法用隱式 Intent 打開系統瀏覽器自建 Activity 以響應打開網頁的 Intent向下一個活動傳遞數據返回數據給上一個活動In…

Android入門(二) | 項目目錄及主要文件作用分析

文章目錄項目目錄分析app目錄分析AndroidManifest.xml 分析MainActivity.kt 分析build.gradle 分析最外層目錄下的 build.gradleapp 目錄下的 build.gradle項目目錄分析 我們來看一下 src/main/res 下的一些文件&#xff1a; .gradle 和 .idea &#xff1a;這兩個目錄下放置…

Android入門(三) | Android 的日志工具 Logcat

文章目錄日志工具類 android.util.LogLogcat 中的過濾器日志工具類 android.util.Log Log 從屬日志工具類 android.util.Log &#xff0c;該類提供了五個方法供我們打印日志&#xff1a; Log.v() &#xff1a;用于打印那些最為瑣碎的、意義最小的日志信息。對應級別 verbose&…

Android 客戶端與服務器交互方式

突然想到一個問題就是Android客戶端與服務器交互有幾種方式&#xff0c;因為在腦袋里想當然的就是webservices和json。要在Android手機客戶端與pc服務器交互&#xff0c;需要滿足下面幾種條件&#xff1a;跨平臺、傳輸數據格式標準、交互方便...。 為了與服務器通訊其實無非就…

Android入門(五) | Activity 的生命周期

文章目錄Activity 的狀態及生命周期實現管理生命周期FirstActivitySecondActivityDialogActivity運行結果舊活動被回收了還能返回嗎&#xff1f;Activity 的狀態及生命周期 Android 的應用程序運用 棧&#xff08;Back Stack&#xff09; 的思想來管理 Activity&#xff1a; …