網路編程中TCP基礎鞏固以及Linux打開的文件過多文件句柄的總結

来源:https://www.cnblogs.com/lys_013/archive/2019/01/23/10308580.html
-Advertisement-
Play Games

1.TCP連接(短鏈接和長連接) 什麼是TCP連接?TCP(Transmission Control Protocol 傳輸控制協議)是一種面向連接的、可靠的、基於位元組流的傳輸層通信協議。 當網路通信時採用TCP協議時,在真正的讀寫操作之前,server與client之間必須建立一個連接,當讀寫操作 ...


1.TCP連接(短鏈接和長連接)

什麼是TCP連接?TCP(Transmission Control Protocol 傳輸控制協議)是一種面向連接的、可靠的、基於位元組流的傳輸層通信協議。

當網路通信時採用TCP協議時,在真正的讀寫操作之前,server與client之間必須建立一個連接,當讀寫操作完成後,雙方不再需要這個連接時它們可以釋放這個連接,連接的建立是需要三次握手的,而釋放則需要4次揮手,所以說每個連接的建立都是需要資源消耗和時間消耗的。

TCP短鏈接:短連接是指通信雙方有數據交互時,就建立一個TCP連接,數據發送完成後,則斷開此TCP連接(管理起來比較簡單,存在的連接都是有用的連接,不需要額外的控制手段)。

連接→數據傳輸→關閉連接;

TCP長連接:所謂長連接,指在一個TCP連接上可以連續發送多個數據包,在TCP連接保持期間,如果沒有數據包發送,需要雙方發檢測包以維持此連接,一般需要自己做線上維持(不發生RST(reset)包用於強制關閉TCP連接的包和四次揮手)。

 連接→數據傳輸→保持連接(心跳)→數據傳輸→保持連接(心跳)→……→關閉連接(一個TCP連接通道多個讀寫通信);

這就要求長連接在沒有數據通信時,定時發送數據包(心跳),以維持連接狀態。這就是TCP保活功能,保活功能主要為伺服器應用提供,伺服器應用希望知道客戶主機是否崩潰,從而可以釋放客戶端占用的資源。

心跳包:它像心跳一樣每隔固定時間發一次,以此來告訴伺服器,這個客戶端還活著。用於保持長連接,至於這個包的內容,是沒有什麼特別規定的,不過一般都是很小的包,或者只包含包頭的一個空包。在TCP的機制裡面,本身是存在有心跳包的機制的,也就是TCP的選項:SO_KEEPALIVE。系統預設是設置的2小時的心跳頻率。但是它檢查不到機器斷電、網線拔出、防火牆這些斷線。心跳包主要也就是用於長連接的保活和斷線處理。一般的應用下,判定時間在30-40秒比較不錯。如果實在要求高,那就在6-9秒。

應用場景:長連接多用於操作頻繁(讀寫),點對點的通訊,而且連接數不能太多情況。每個TCP連接都需要三步握手,這需要時間,如果每個操作都是先連接,再操作的話那麼處理速度會降低很多,所以每個操作完後都不斷開,下次處理時直接發送數據包就OK了,不用建立TCP連接。例如:資料庫的連接用長連接, 如果用短連接頻繁的通信會造成socket錯誤,而且頻繁的socket 創建也是對資源的浪費。

而像WEB網站的http服務一般都用短鏈接(http1.0只支持短連接,http1.1keep alive 帶時間,操作次數限制的長連接),因為長連接對於服務端來說會耗費一定的資源,而像WEB網站這麼頻繁的成千上萬甚至上億客戶端的連接用短連接會更省一些資源,如果用長連接,而且同時有成千上萬的用戶,如果每個用戶都占用一個連接的話,那可想而知吧。所以併發量大,但每個用戶無需頻繁操作情況下需用短連好;

在長連接中一般是沒有條件能夠判斷讀寫什麼時候結束,所以必須要加長度報文頭。讀函數先是讀取報文頭的長度,以及報文開始結束符號,再根據這個長度去讀相應長度的報文。

 

 2.單台伺服器支持最大的TCP連接數

在tcp應用中,server事先在某個固定埠監聽,client主動發起連接,經過三路握手後建立tcp連接。那麼對單機,其最大併發tcp連接數是多少?

如何標識一個TCP連接:系統用一個4四元組來唯一標識一個TCP連接:{local ip, local port,remote ip,remote port}。

Client最大tcp連接數:client每次發起tcp連接請求時,除非綁定埠,通常會讓系統選取一個空閑的本地埠(local port),該埠是獨占的,不能和其他tcp連接共用。tcp埠的數據類型是unsigned short,因此本地埠個數最大隻有65536,埠0有特殊含義,不能使用,這樣可用埠最多只有65535,所以在全部作為client端的情況下,客戶端發起的最大tcp連接數為65535,這些連接可以連到不同的server ip。

Server最大tcp連接數:server通常固定在某個本地埠上監聽,等待client的連接請求。不考慮地址重用(unix的SO_REUSEADDR選項)的情況下,即使server端有多個ip,本地監聽埠也是獨占的,因此server端tcp連接4元組中只有remote ip(也就是client ip)和remote port(客戶端port)是可變的,因此最大tcp連接為客戶端ip數×客戶端port數,對IPV4,不考慮ip地址分類等因素,最大tcp連接數約為2的32次方(ip數)×2的16次方(port數),也就是server端單機最大tcp連接數約為2的48次方。

實際的tcp連接數:上面給出的是理論上的單機最大連接數,在實際環境中,受到機器資源、操作系統等的限制,特別是sever端,其最大併發tcp連接數遠不能達到理論上限。在unix/linux下限制連接數的主要因素是記憶體和允許的文件描述符個數(每個tcp連接都要占用一定記憶體,每個socket就是一個文件描述符),另外1024以下的埠通常為保留埠。在預設2.6內核配置下,經過試驗,每個socket占用記憶體在15~20k之間。

對server端,通過增加記憶體、修改最大文件描述符個數等參數,單機最大併發TCP連接數超過10萬 是沒問題的,國外 Urban Airship 公司在產品環境中已做到 50 萬併發 。在實際應用中,對大規模網路應用,還需要考慮C10K 問題。我們先假設單台伺服器最多只能支持萬級併發連接,其實對絕大多數應用來說已經遠遠足夠了,但是對於一些擁有很大用戶基數的互聯網公司,往往面臨的併發連接數是百萬,千萬,甚至騰訊的上億(註:QQ預設用的UDP協議)。雖然現在的集群,分散式技術可以為我們將併發負載分擔在多台伺服器上,那我們只需要擴展出數十臺電腦就可以解決問題,但是我們更希望能更大的挖掘單台伺服器的資源,先努力垂直擴展,再進行水平擴展,這樣可以有效的節省伺服器相關的開支(硬體資源,機房,運維,電力其實也是一筆不小的開支)。

 補充知識:

一、文件句柄限制

在linux下編寫網路伺服器程式時每一個tcp連接都要占一個文件描述符,一旦這個文件描述符使用完了,新的連接到來返回給我們的錯誤是“Socket/File:Can't open so many files”即文件打開過多!

這時你需要明白操作系統對可以打開的最大文件數的限制。

  • 進程限制

    • 執行 ulimit -n 輸出 1024,說明對於一個進程而言最多只能打開1024個文件,所以你要採用此預設配置最多也就可以併發上千個TCP連接。

    • 臨時修改:ulimit -n 1000000,但是這種臨時修改只對當前登錄用戶目前的使用環境有效,系統重啟或用戶退出後就會失效。

    • 重啟後失效的修改(不過我在CentOS 6.5下測試,重啟後未發現失效):編輯 /etc/security/limits.conf 文件, 修改後內容為

      * soft nofile 1000000

      * hard nofile 1000000

    • 永久修改:編輯/etc/rc.local,在其後添加如下內容

      ulimit -SHn 1000000

  • 全局限制

    • 執行 cat /proc/sys/fs/file-nr 輸出 9344 0 592026,分別為:1.已經分配的文件句柄數,2.已經分配但沒有使用的文件句柄數,3.最大文件句柄數。但在kernel 2.6版本中第二項的值總為0,這並不是一個錯誤,它實際上意味著已經分配的文件描述符無一浪費的都已經被使用了 。

    • 我們可以把這個數值改大些,用 root 許可權修改 /etc/sysctl.conf 文件:

      fs.file-max = 1000000

      net.ipv4.ip_conntrack_max = 1000000

      net.ipv4.netfilter.ip_conntrack_max = 1000000

二、埠號範圍限制?

操作系統上埠號1024以下是系統保留的,從1024-65535是用戶使用的。由於每個TCP連接都要占一個埠號,所以我們最多可以有60000多個併發連接。我想有這種錯誤思路朋友不在少數吧?(其中我過去就一直這麼認為)

  • 如何標識一個TCP連接:系統用一個4四元組來唯一標識一個TCP連接:{local ip, local port,remote ip,remote port}。好吧,我們拿出《UNIX網路編程:捲一》第四章中對accept的講解來看看概念性的東西,第二個參數cliaddr代表了客戶端的ip地址和埠號。而我們作為服務端實際只使用了bind時這一個埠,說明埠號65535並不是併發量的限制。

  • server最大tcp連接數:server通常固定在某個本地埠上監聽,等待client的連接請求。不考慮地址重用(unix的SO_REUSEADDR選項)的情況下,即使server端有多個ip,本地監聽埠也是獨占的,因此server端tcp連接4元組中只有remote ip(也就是client ip)和remote port(客戶端port)是可變的,因此最大tcp連接為客戶端ip數×客戶端port數,對IPV4,不考慮ip地址分類等因素,最大tcp連接數約為2的32次方(ip數)×2的16次方(port數),也就是server端單機最大tcp連接數約為2的48次方。

三、總結

TCP/IP 協議規定的,只用了2個位元組表示埠號。容易讓人誤解為1個server只允許連接65535個Client。

typedef struct _NETWORK_ADDRESS_IP
{ USHORT      sin_port;//0~65535
 ULONG       in_addr;
 UCHAR       sin_zero[8];
} NETWORK_ADDRESS_IP, *PNETWORK_ADDRESS_IP;

(1)其實65535這個數字,只是決定了伺服器端最多可以擁有65535個Bind的Socket。也就是說,最多可以開65535個伺服器進程,但是你要知道這個能夠連接客戶端的數量沒有任何關係,Accept過來的Socket是不需要Bind任何IP地址的,也沒有埠占用這一說。作為Server端的Socket本身只負責監聽和接受連接操作。

(2)TCP協議裡面是用[源IP+源Port+目的IP+目的 Port]來區別兩個不同連接,所以連入和連出是兩個不同的概念。連出Connect就不錯了,需要生成隨機埠,這個是有限的連入的話, 因SOCKET的分配受記憶體分頁限制,而連接受限制(WINDOWS)。

(3)所以,千萬不要誤以為1個server只允許連接65535個Client。記住,TCP連出受埠限制,連入僅受記憶體限制

例如server,IP:192.168.16.254,Port:8009

Client1:IP:192.168.16.1,Port:2378

Client2:IP:192.168.16.2,Port:2378

Client1和Client2雖然Port相同,但是IP不同,所以是不同的連接。

(4)想讓1個server併發高效得連接幾萬個Client,需要使用IOCP“完成埠(Completion Port)”的技術。

上面給出的結論都是理論上的單機TCP併發連接數,實際上單機併發連接數肯定要受硬體資源(記憶體)、網路資源(帶寬)的限制。

這種單台機器10w併發,不考慮記憶體cpu的實現,主要是程式網路模型的選擇。項目在Github上有提供https://github.com/yaocoder/HPNetServer

 linux系統優化設置調整參見:https://www.cnblogs.com/duanxz/p/4464178.html

 3.too many open files(打開的文件過多)解決方法

 

Netty作為通訊伺服器,發生了打開的文件過多異常,之後在有新的連接就無法連接上來,導致程式崩潰。首先要檢查程式斷開後是否釋放資源,確認無誤後在調整文件句柄限制。

too many open files(打開的文件過多)是Linux系統中常見的錯誤,從字面意思上看就是說程式打開的文件數過多,不過這裡的files不單是文件的意思,也包括打開的通訊鏈接(比如socket),正在監聽的埠等等,所以有時候也可以叫做句柄(handle),這個錯誤通常也可以叫做句柄數超出系統限制。

引起的原因就是進程在某個時刻打開了超過系統限制的文件數量以及通訊鏈接數,通過命令ulimit -a可以查看當前系統設置的最大句柄數是多少:

[dsuser@test02-ds-gps01 ~]$ ulimit -a
core file size (blocks, -c) 0
data seg size (kbytes, -d) unlimited
scheduling priority (-e) 0
file size (blocks, -f) unlimited
pending signals (-i) 15220
max locked memory (kbytes, -l) 64
max memory size (kbytes, -m) unlimited
open files (-n) 1024
pipe size (512 bytes, -p) 8
POSIX message queues (bytes, -q) 819200
real-time priority (-r) 0
stack size (kbytes, -s) 10240
cpu time (seconds, -t) unlimited
max user processes (-u) 1024
virtual memory (kbytes, -v) unlimited
file locks (-x) unlimited

open files那一行就代表系統目前允許單個進程打開的最大句柄數,這裡是1024。 

[dsuser@test02-ds-gps01 ~]$ jps -l
9314 com.hns.gps.gw.jt808.app.Main
17135 sun.tools.jps.Jps
使用命令lsof -p 進程id可以查看單個進程所有打開的文件詳情,使用命令lsof -p 進程id | wc -l可以統計進程打開了多少文件:

[dsuser@test02-ds-gps01 ~]$ lsof -p 9314 | wc -l
226

以目前的通訊程式為例,可以看到它目前打開了226個文件數,如果文件數過多使用lsof -p 進程id命令無法完全查看的話,可以使用lsof -p 進程id > openfiles.log將執行結果內容輸出到日誌文件中查看。
解決方法:

1、增大允許打開的文件數——命令方式
ulimit -n 2048
這樣就可以把當前用戶的最大允許打開文件數量設置為2048了,但這種設置方法在重啟後會還原為預設值。
ulimit -n命令非root用戶只能設置到4096。
想要設置到8192需要sudo許可權或者root用戶。

2、增大允許打開的文件數——修改系統配置文件
vim /etc/security/limits.conf
#在最後加入
* soft nofile 4096
* hard nofile 4096
或者只加入

* - hard nofile 8192
最前的 * 表示所有用戶,可根據需要設置某一用戶,例如

dsuser soft nofile 8192
dsuser hard nofile 8192
註意”nofile”項有兩個可能的限制措施。就是項下的hard和soft。 要使修改過得最大打開文件數生效,必須對這兩種限制進行設定。 如果使用”-“字元設定, 則hard和soft設定會同時被設定。

3、檢查程式問題
如果你對你的程式有一定的解的話,應該對程式打開文件數(鏈接數)上限有一定的估算,如果感覺數字異常,請使用第一步的lsof -p 進程id > openfiles.log命令,獲得當前占用句柄的全部詳情進行分析,

1)打開的這些文件是不是都是必要的?
2)定位到打開這些文件的代碼
3)是否程式操作了文件寫入,但是沒有進行正常關閉
4)是否程式進行了通訊,但是沒有正常關閉(也就是沒有超時結束的機制)
如果程式中存在這些問題的話,無論系統句柄數設置的多麼大,隨著時間的推移,也一定會占用完。

總結完畢!延伸閱讀:Netty系列之Netty百萬級推送服務設計要點 (https://www.cnblogs.com/ruixueyan/p/6382770.html)

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 說說高斯模糊 高斯模糊的理論我這裡就不太多費話了,百度下太多,都是抄來抄去。 主要用到二個函數“高斯函數” 一維形式為: 二維形式為: X,Y對應的一維二維坐標,σ表示模糊半徑(半徑* 2 + 1) / 2) 根據這二個公式獲取對應的權重。 先看二維 假設我們現在圖片中的像素點位置為(0,0) 假設 ...
  • 關於轉換問題,剛開始我需要從資料庫讀出一個二進位數據流,並將其轉換成一個Image格式。 在不涉及資料庫的情況下,我先將一個圖片轉換成一個二進位數組顯示出來,再寫一個方法將其轉換成圖片image格式。 一、 先不涉及資料庫,將圖片轉換成二進位,在將二進位轉換成圖片。 第一步,我將圖片轉換成二進位數組 ...
  • ASP.NET Core是微軟ASP.NET Web框架的最新版本。於2016年6月發佈,相比之前ASP.NET有很多增量更新。 ASP.NET Core通過進行重大的體繫結構調整來提高開發人員的工作效率和向後相容性。重新設計Web框架和構建方式。 ASP.NET Core很多功能來至之前ASP.N... ...
  • 最近閑餘時間在做一個仿百度網盤的項目,其中就有一個上傳文件夾的功能。查了下網上好像對這個問題的描述比較少,所以在此記錄一下。 1、網上找來找去發現webkitdirectory這個東西,H5的一個新的屬性吧,就是在文件控制項上標記這個屬性可以獲取到選擇文件夾里的所有文件的。 特地看了下百度網盤網頁版也 ...
  • 1.問題 今天在使用docker掛載redis的時候老是報錯 然後一直報錯: 2.排查過程 查看日誌也是這樣 然後我把使用配置文件的地方去掉 然後進入容器 然後進入掛載的文件夾下 發現報錯: 也就是沒有許可權 3.原因以及解決方案 3.1 原因 centos7中安全模塊selinux把許可權禁掉了 3. ...
  • 本軟體本是練習、討論爬蟲技術所用。如果侵犯了您的利益請聯繫我,我會立即刪除! 小工具安裝包: 百度網盤鏈接:https://pan.baidu.com/s/1m_OuEBOEE47kYaXq5fwpIg 提取碼:w4p1 下麵附上源碼,如有不同意見還請賜教! 百度網盤鏈接:https://pan.b ...
  • VS一直停留在“正在還原nuget程式包” 在開發中,運行不同版本的vs會顯示還原nuget程式包,還原需要不短的時間,並且不一定還原成功。 或者其他什麼原因導致需要還原nuget程式包,這樣很煩的有木有。 我們只需要去掉【允許NuGet下載缺少的程式包】就行了。 如下截圖: 這樣再次運行就不會出現 ...
  • 一.概述 ASP.NET Core 支持適用於各種內置和第三方日誌記錄, 供程式的日誌記錄 API,本文介紹瞭如何將日誌記錄 API 與內置提供程式一起使用。對於第三方日誌記錄提供程式使用,文章最後有鏈接。 1.1 添加內置日誌提供程式 日誌記錄提供程式能夠用於顯示日誌信息或存儲日誌,比如控制台提供 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...