常識一:文件句柄限制
在Linux下編寫網絡服務器程序的朋友確定都知道每個tcp鏈接都要佔一個文件描述符,一旦這個文件描述符使用完了,新的鏈接到來返回給咱們的錯誤是「Socket/File:Can'topen so many files」。linux
這時你須要明白操做系統對能夠打開的最大文件數的限制。程序員
-
進程限制面試
-
執行ulimit -n 輸出1024,說明對於一個進程而言最多隻能打開1024個文件,因此你要採用此默認配置最多也就能夠併發上千個TCP鏈接。編程
-
臨時修改:ulimit -n1000000,可是這種臨時修改只對當前登陸用戶目前的使用環境有效,系統重啓或用戶退出後就會失效。centos
-
永久修改:編輯/etc/security/limits.conf 文件, 修改後內容爲服務器
* soft nofile 1000000
網絡* hard nofile 1000000
多線程
-
-
全侷限制併發
-
執行 cat /proc/sys/fs/file-nr 輸出11968 0 1618287,分別爲:1.已經分配的文件句柄數,2.已經分配但沒有使用的文件句柄數,3.最大文件句柄數。但在kernel2.6版本中第二項的值總爲0,這並非一個錯誤,它實際上意味着已經分配的文件描述符無一浪費的都已經被使用了 。socket
-
咱們能夠把這個數值改大些,用 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,remoteip,remoteport}。好吧,咱們拿出《UNIX網絡編程:卷一》第四章中對accept的講解來看看概念性的東西,第二個參數cliaddr表明了客戶端的ip地址和端口號。而咱們做爲服務端實際只使用了bind時這一個端口,說明端口號65535並非併發量的限制。
-
server最大tcp鏈接數:server一般固定在某個本地端口上監聽,等待client的鏈接請求。不考慮地址重用(unix的SO_REUSEADDR選項)的狀況下,即便server端有多個ip,本地監聽端口也是獨佔的,所以server端tcp鏈接4元組中只有remoteip(也就是client ip)和remoteport(客戶端port)是可變的,所以最大tcp鏈接爲客戶端ip數×客戶端port數,對IPV4,不考慮ip地址分類等因素,最大tcp鏈接數約爲2的32次方(ip數)×2的16次方(port數),也就是server端單機最大tcp鏈接數約爲2的48次方。
要寫網絡程序就必須用Socket,這是程序員都知道的。並且,面試的時候,咱們也會問對方會不會Socket編程?通常來講,不少人都會說,Socket編程基本就是listen,accept以及send,write等幾個基本的操做。是的,就跟常見的文件操做同樣,只要寫過就必定知道。
對於網絡編程,咱們也言必稱TCP/IP,彷佛其它網絡協議已經不存在了。對於TCP/IP,咱們還知道TCP和UDP,前者能夠保證數據的正確和可靠性,後者則容許數據丟失。最後,咱們還知道,在創建鏈接前,必須知道對方的IP地址和端口號。除此,普通的程序員就不會知道太多了,不少時候這些知識已經夠用了。最多,寫服務程序的時候,會使用多線程來處理併發訪問。
咱們還知道以下幾個事實:
1。一個指定的端口號不能被多個程序共用。好比,若是IIS佔用了80端口,那麼Apache就不能也用80端口了。
2。不少防火牆只容許特定目標端口的數據包經過。
3。服務程序在listen某個端口並accept某個鏈接請求後,會生成一個新的socket來對該請求進行處理。
因而,一個困惑了我好久的問題就產生了。若是一個socket建立後並與80端口綁定後,是否就意味着該socket佔用了80端口呢?若是是這樣的,那麼當其accept一個請求後,生成的新的socket到底使用的是什麼端口呢(我一直覺得系統會默認給其分配一個空閒的端口號)?若是是一個空閒的端口,那必定不是80端口了,因而之後的TCP數據包的目標端口就不是80了--防火牆必定會組織其經過的!實際上,咱們能夠看到,防火牆並無阻止這樣的鏈接,並且這是最多見的鏈接請求和處理方式。個人不解就是,爲何防火牆沒有阻止這樣的鏈接?它是如何斷定那條鏈接是由於connet80端口而生成的?是否是TCP數據包裏有什麼特別的標誌?或者防火牆記住了什麼東西?
後來,我又仔細研讀了TCP/IP的協議棧的原理,對不少概念有了更深入的認識。好比,在TCP和UDP同屬於傳輸層,共同架設在IP層(網絡層)之上。而IP層主要負責的是在節點之間(End
to End)的數據包傳送,這裏的節點是一臺網絡設備,好比計算機。由於IP層只負責把數據送到節點,而不能區分上面的不一樣應用,因此TCP和UDP協議在其基礎上加入了端口的信息,端口因而標識的是一個節點上的一個應用。除了增長端口信息,UPD協議基本就沒有對IP層的數據進行任何的處理了。而TCP協議還加入了更加複雜的傳輸控制,好比滑動的數據發送窗口(Slice Window),以及接收確認和重發機制,以達到數據的可靠傳送。無論應用層看到的是怎樣一個穩定的TCP數據流,下面傳送的都是一個個的IP數據包,須要由TCP協議來進行數據重組。
因此,我有理由懷疑,防火牆並無足夠的信息判斷TCP數據包的更多信息,除了IP地址和端口號。並且,咱們也看到,所謂的端口,是爲了區分不一樣的應用的,以在不一樣的IP包來到的時候可以正確轉發。
TCP/IP只是一個協議棧,就像操做系統的運行機制同樣,必需要具體實現,同時還要提供對外的操做接口。就像操做系統會提供標準的編程接口,好比Win32編程接口同樣,TCP/IP也必須對外提供編程接口,這就是Socket編程接口--原來是這麼回事啊!
在Socket編程接口裏,設計者提出了一個很重要的概念,那就是socket。這個socket跟文件句柄很類似,實際上在BSD系統裏就是跟文件句柄同樣存放在同樣的進程句柄表裏。這個socket實際上是一個序號,表示其在句柄表中的位置。這一點,咱們已經見過不少了,好比文件句柄,窗口句柄等等。這些句柄,實際上是表明了系統中的某些特定的對象,用於在各類函數中做爲參數傳入,以對特定的對象進行操做--這實際上是C語言的問題,在C++語言裏,這個句柄其實就是this指針,實際就是對象指針啦。
如今咱們知道,socket跟TCP/IP並無必然的聯繫。Socket編程接口在設計的時候,就但願也能適應其餘的網絡協議。因此,socket的出現只是能夠更方便的使用TCP/IP協議棧而已,其對TCP/IP進行了抽象,造成了幾個最基本的函數接口。好比create,listen,accept,connect,read和write等等。
如今咱們明白,若是一個程序建立了一個socket,並讓其監聽80端口,實際上是向TCP/IP協議棧聲明瞭其對80端口的佔有。之後,全部目標是80端口的TCP數據包都會轉發給該程序(這裏的程序,由於使用的是Socket編程接口,因此首先由Socket層來處理)。所謂accept函數,其實抽象的是TCP的鏈接創建過程。accept函數返回的新socket其實指代的是本次建立的鏈接,而一個鏈接是包括兩部分信息的,一個是源IP和源端口,另外一個是宿IP和宿端口。因此,accept能夠產生多個不一樣的socket,而這些socket裏包含的宿IP和宿端口是不變的,變化的只是源IP和源端口。這樣的話,這些socket宿端口就能夠都是80,而Socket層仍是能根據源/宿對來準確地分辨出IP包和socket的歸屬關係,從而完成對TCP/IP協議的操做封裝!而同時,放火牆的對IP包的處理規則也是清晰明瞭,不存在前面設想的種種複雜的情形。
明白socket只是對TCP/IP協議棧操做的抽象,而不是簡單的映射關係,這很重要.
關於TCP服務器最大併發鏈接數有一種誤解就是「由於端口號上限爲65535,因此TCP服務器理論上的可承載的最大併發鏈接數也是65535」。
先說結論:對於TCP服務端進程來講,他能夠同時鏈接的客戶端數量並不受限於可用端口號。併發鏈接數受限於linux可打開文件數,這個數是能夠配置的,能夠很是大,因此實際上受限於系統性能。
從理論上說,端口號的做用是在網絡鏈接中標識應用層的進程,服務端通常使用衆所周知的端口號進行監聽,客戶端鏈接時系統自動分配端口號。一個服務端進程服務於n個客戶遠程進程,只須要能經過ip地址+端口號的組合把他們區分開便可,沒有必要佔用本機的其餘端口號,客戶端鏈接數增長並不會佔用服務器端口號,所以端口號並不能限制併發鏈接數。固然一臺機器上端口號數量的上限確實是65536個,由於tcp首部中使用16bit去存儲端口號。因此若是說65536影響了鏈接數,只有一種可能,就是同一臺客戶端機子上開n個進程去連同一個服務端進程,由於客戶端ip是同一個,爲了區分出這些鏈接,只能使用客戶端鏈接的端口號,那麼服務端和一個客戶端主機之間的tcp鏈接數理論上線確實是65536。可是,服務端能夠鏈接n多客戶端機子呢。
實際上,確實有個限制端口號的配置,就是MaxUserPort,這其實是一臺主機向外鏈接使用端口數的限制,這個數也能夠配置的,可能默認值才5000,實際上對於正常的服務器主機是夠用的,由於你是等別人鏈接進來的,不是要去鏈接不少不一樣的其餘主機的。固然你的服務器上可能跑了一些轉發的服務,這樣你就須要對外鏈接了,若是被限制在這個配置這兒了確實須要改。可是這個MaxUserPort確實和服務器能夠承載的來自客戶端的併發鏈接數沒有關係。
伴隨這個誤解的還有另一個誤解,就是accept以後產生的已鏈接套接字佔用了新的端口。這個絕對是錯誤的,linux內核不會這麼寫的,由於徹底不必嘛。客戶端鏈接上來以後產生的這個socket fd就是用來區分客戶端的,裏面會填上客戶端的ip和端口做爲發包用,來自客戶端的包也會使用這個fd去讀取。能夠試試netstat -ano,而後起一個服務器看下,客戶端連上來這後產生的套接字的服務端端口仍是監聽的端口。
高併發網絡鏈接數因端口數量受限問題
遇到的問題:端口數量受限
通常來講,單獨對外提供請求的服務不用考慮端口數量問題,監聽某一個端口便可。可是向提供代理服務器,就不得不考慮端口數量受限問題了。當前的1M併發鏈接測試,也須要在客戶端突破6萬可用端口的限制。
單機端口上限爲65536
端口爲16進制,那麼2的16次方值爲65536,在linux系統裏面,1024如下端口都是超級管理員用戶(如root)纔可使用,普通用戶只能使用大於1024的端口值。
系統提供了默認的端口範圍:cat /proc/sys/net/ipv4/ip_local_port_range 1024 65535
大概也就是共65535-1024=64511個端口可使用,單個IP對外只能發送64511個TCP請求。
以管理員身份,把端口的範圍區間增到最大:echo "1024 65535"> /proc/sys/net/ipv4/ip_local_port_range
如今有64511個端口可用.
以上作法只是臨時,系統下次重啓,會還原。 更爲穩妥的作法是修改/etc/sysctl.conf文件,增長一行內容
net.ipv4.ip_local_port_range= 1024 65535 保存,而後使之生效:sysctl -p
如今可使用的端口達到64510個(假設系統全部運行的服務器是沒有佔用大於1024的端口的,較爲純淨的centos系統能夠作到),要想達到50萬請求,還得再想辦法。
增長IP地址
通常假設本機網卡名稱爲 eth0,那麼手動再添加幾個虛擬的IP:
ifconfig eth0:1 192.168.190.151
ifconfig eth0:2 192.168.190.152 ......
或者偷懶一些:for i in `seq 1 9`; do ifconfig eth0:$i 192.168.190.15$i up ; done
這些虛擬的IP地址,一旦重啓,或者 service network restart 就會丟失。
爲了模擬較爲真實環境,在測試端,手動再次添加9個vmware虛擬機網卡,每個網卡固定一個IP地址,這樣省去每次重啓都要從新設置的麻煩
192.168.190.134
192.168.190.143
192.168.190.144
192.168.190.145
192.168.190.146
192.168.190.147
192.168.190.148
192.168.190.149
192.168.190.150
192.168.190.151
在server服務器端,手動添加橋接網卡和NAT方式網卡
192.168.190.230
192.168.190.240
10.95.20.250
要求測試端和服務器端彼此雙方都是能夠ping通。
網絡四元組/網絡五元組
四元組是指的是 {源IP地址,源端口,目的IP地址,目的端口}
五元組指的是(多了協議) {源IP地址,目的IP地址,協議號,源端口,目的端口}
在《UNIX網絡編程卷1:套接字聯網API(第3版)》一書中,是這樣解釋:
一個TCP鏈接的套接字對(socket pari)是一個定義該鏈接的兩個端點的四元組,即本地IP地址、本地TCP端口號、外地IP地址、外地TCP端口號。套接字對惟一標識一個網絡上的每一個TCP鏈接。
......
標識每一個端點的兩個值(IP地址和端口號)一般稱爲一個套接字。
如下以四元組爲準。在測試端四元組能夠這樣認爲:
{本機IP地址,本機端口,目的IP地址,目的端口}
請求的IP地址和目的端口基本上是固定的,不會變化,那麼只能從本機IP地址和本機端口上考慮,端口的範圍一旦指定了,那麼增長IP地址,能夠增長對外發出的請求數量。假設系統可使用的端口範圍已經如上所設,那麼可使用的大體端口爲64000個,系統添加了10個IP地址,那麼能夠對外發出的數量爲 64000 * 10 = 640000,數量很可觀。
只有{源IP地址,源端口}肯定對外TCP請求數量
經測試,四元組裏面,只有{源IP地址,源端口}纔可以肯定對外發出請求的數量,跟{目的IP地址,目的端口}無關。