国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學(xué)院 > 開發(fā)設(shè)計 > 正文

談?wù)劸W(wǎng)絡(luò)編程里的那些事

2019-11-14 16:42:45
字體:
供稿:網(wǎng)友

網(wǎng)絡(luò)編程一直是經(jīng)久不衰的話題,今天就網(wǎng)絡(luò)編程里面一些問題做個總結(jié),由于UDP相對于TCP的處理問題比較簡單,所以這次總結(jié)的都是有關(guān)TCP的。

一、關(guān)于服務(wù)器最大TCP連接數(shù)問題。

       很多人認為單臺服務(wù)器的最大TCP連接數(shù)是65536也就是受限于服務(wù)器的端口的數(shù)量 如果服務(wù)器想開6W以上的TCP連接就要綁定多個ip。其實這是一個重大的錯誤認識,其實按照TCP/IP協(xié)議的規(guī)定,確定一個連接的唯一標識是一個4元組 <本地IP,本地端口,遠程IP,遠程端口>,因為服務(wù)器綁定的端口和IP是固定的(一般進程都只綁定一個端口和IP),所以決定連接的因素就是客戶機器的遠程IP和遠程端口 ,與服務(wù)器端口數(shù)量根本沒有任何關(guān)系,也就是說客戶機的IP范圍和端口號范圍才是決定連接的重要因素,我們知道IP地址的范圍是2^32個(這里IP和端口不考慮保留問題),端口的數(shù)量是2^16個 那么服務(wù)器保持的最大連接數(shù)的理論值是2^32*2^16=2^48  。不過,難道服務(wù)器端真的能開那么多連接么,答案是要看服務(wù)器有多少物理內(nèi)存。也就是說物理內(nèi)存的大小才是決定服務(wù)器能開多少連接的決定因數(shù)(當然也和操作系統(tǒng)的設(shè)置有關(guān) 例如:最大文件句柄的數(shù)量,但總體來說是受限于物理內(nèi)存),如果只有1G的物理內(nèi)存想開100W的連接這是不可能的。我們知道建立一個socket連接和打開一個文件一樣,是由操作系統(tǒng)建立一個文件句柄,文件句柄指向一個稱為文件對象的windows內(nèi)核對象 ,創(chuàng)建文件對象的數(shù)量決定了TCP的最大連接數(shù),也可以這么認為:對于有限的資源 服務(wù)器最大的TCP連接數(shù)是操作系統(tǒng)能打開文件的最大數(shù)量,如果系統(tǒng)資源足夠大時那么最大就是2^48。

       為了證明服務(wù)端的I連接數(shù)P和端口沒有關(guān)系   我寫了一個測試程序,該程序為了屏蔽線程棧消耗的內(nèi)存采用了.net 封裝好的IOCP的方式。

       客戶端測試機使用了3臺普通的PC機 ,三臺臺器分別與服務(wù)器保持5W,4W,1W個連接,統(tǒng)計服務(wù)端的連接數(shù)是否是10W。

      程序代碼如下:

   1. 服務(wù)端代碼:

    /// <summary>    /// IOCP soket    /// </summary>    class Server    {        PRivate Socket connSocket;        //統(tǒng)計連接總數(shù)        private static int count;        public  Server()        {                    connSocket = new Socket(SocketType.Stream, ProtocolType.Tcp);            IPAddress ip = IPAddress.Parse("192.168.1.123");            IPEndPoint endPoint = new IPEndPoint(ip, 6530);                        connSocket.Bind(endPoint);            connSocket.Listen(50000);            }        /// <summary>        /// 接收客戶端連接        /// </summary>        public void Start()        {            IAsyncResult acceptResult=null;            acceptResult = connSocket.BeginAccept(AcceptCallback, connSocket);        }             private void AcceptCallback(IAsyncResult ar)        {            Socket accetpSocket = ar.AsyncState as Socket;            if(accetpSocket==null)            {                return;            }            Socket  receiveSocket= accetpSocket.EndAccept(ar);            //打印連接數(shù)            count++;            Console.WriteLine("連接數(shù):{0}",count);            Start();            Receieve(receiveSocket);           }        /// <summary>        /// 接收發(fā)送的數(shù)據(jù)        /// </summary>        /// <param name="receiveSocket">接收數(shù)據(jù)的socket</param>        private void Receieve(Socket receiveSocket)        {            byte[] buffer = new byte[28];            receiveSocket.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, (state) =>            {                Socket receive = state.AsyncState as Socket;                try                {                    int length = receive.EndReceive(state);                    Console.WriteLine(Encoding.Unicode.GetString(buffer));                    Receieve(receive);                }                catch (SocketException socketEx)                {                    receive.Dispose();                }            }, receiveSocket);        }     }

     2. 客戶端代碼:

   /// <summary>    /// 測試客戶端    /// </summary>    class Program    {        static List<Socket> socketList = new List<Socket>();            static void Main(string[] args)        {                       for (int i = 0; i < 40000;i++)            {                Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);                                int port = 6530;                IPAddress ip = IPAddress.Parse("192.168.1.123");//本地                socket.Connect(ip, port);                socketList.Add(socket);            }            Console.ReadKey();        }    }

   3.測試結(jié)果:  

      (1)Server運行結(jié)果:

          2

                                                                                        圖3.1

        (2)Tcpview監(jiān)視的連接:

          1

                                                                                 圖3.2

(3)server占用資源情況:

         3

                                                                                                     圖3.3

     

          根據(jù)圖3.1的結(jié)果可以看出, 服務(wù)端接受的連接數(shù)為10W ,所以說 最大TCP連接數(shù)與服務(wù)端的端口的數(shù)量沒關(guān)系,而且根據(jù)圖3.2得知服務(wù)端的端口是保持不變的,變的只是客戶端的IP和端口,所以一個連接的唯一標識就是一個4元組 <本地IP,本地端口,遠程IP,遠程端口>。

          進一步分析,我們假定該程序所有的內(nèi)存都是連接占用的,根據(jù)圖3.3可以看出10W個連接占用了 200 M內(nèi)存,也就是每個連接占用了 2kb   在物理內(nèi)存4G的32操作系統(tǒng)下開起連接數(shù)最大約為100W(用戶模式使用了2G的尋址空間),但是即使有4G的物理內(nèi)存也未必能開啟50W個連接,因為在內(nèi)核模式的地址空間分為分頁內(nèi)存池和非分頁內(nèi)存池(用戶模式下總是分頁的),文件對象這個windows內(nèi)核對象是存儲在內(nèi)核模式下的非分頁內(nèi)存池,所謂非分頁內(nèi)存池就是該內(nèi)存區(qū)始終在物理內(nèi)存而不會在磁盤文件的頁交換文件中,而內(nèi)核模式的分頁內(nèi)存池也占用了一部分物理內(nèi)存,導(dǎo)致整個內(nèi)核模式的非分頁內(nèi)存池不能達到2G,所以100W個連接在32位操作系統(tǒng)是也無法開啟。那么在64位操作系統(tǒng)上,理論上內(nèi)存大的話是沒有限制的,亞馬遜曾經(jīng)測試過node.js的連接數(shù) 100W個連接占用了16G的內(nèi)存(.net程序沒有測過100W的內(nèi)存情況,所以4G的100W個連接只是推測,和實際情況可能差別較大) http://blog.caustik.com/2012/08/19/node-js-w1m-concurrent-connections/。

         最后還要補充一句:服務(wù)器的性能和處理多少連接數(shù)沒關(guān)系,即使100W個連接只是連接而不發(fā)送數(shù)據(jù),服務(wù)器只是浪費點內(nèi)存,如果100W個同時發(fā)送數(shù)據(jù),那么服務(wù)器可能會處理不過來,處理不過來只是相對于而言,如果發(fā)送的每個連接消息非常小,服務(wù)器的配置好,而且服務(wù)端處理消息的邏輯相對簡單,那可能會處理過來,相反 一個連接只發(fā)送一條消息,但服務(wù)器處理這條消息的邏輯非常復(fù)查,比如要做個幾分鐘的大型的計算,那么服務(wù)器也是處理不過來的。所以對于服務(wù)端來說 一味的追求多少連接數(shù)是不可取的,主要還是要衡量處理消息的邏輯。

二、關(guān)于TCP多個連接比一個連接快的問題

   TCP/IP協(xié)議限制了每個連接的最大傳輸速度。而且使各大連接的速度保持一直,所以說不考慮服務(wù)端的處理速度等其他因數(shù),多個連接的發(fā)送數(shù)據(jù)速度要比單個連接的快,所以迅雷采用了多線程下載的方式。當然,我們實際開發(fā)中要采用多少個連接需要根據(jù)服務(wù)端對每個連接的處理能力進行測算,找出一個平衡點。

三、關(guān)于處理字節(jié)序的問題

              在.net平臺之間進行通訊時可以忽略主機字節(jié)序的問題,.net平臺統(tǒng)一采用了小端法,如果需要跨平臺傳輸,就需要統(tǒng)一字節(jié)序,一般有兩種方案:

               1.采用字符串的形式,注意UNICODE編碼的字符串占2個字節(jié),所以同樣會有字節(jié)序的問題,所以應(yīng)該使用ANSCI的字符串。

     2.都采用大端或小端,在報文里用一個字節(jié)做個表識來表明主機的字節(jié)序。

四、關(guān)于處理TCP斷包和粘包的問題

             因為TCP屬于流式傳輸,所以沒有數(shù)據(jù)邊界,所以我們不知道每次發(fā)送數(shù)據(jù)的長度,所以每次接受數(shù)據(jù)時可能是兩個包或者可能是一個不完整的包,這就我們需要處理TCP斷包和粘包這兩個場景,一般解決方案是在頭部前4個字節(jié)來標識整個數(shù)據(jù)包的長度,接受數(shù)據(jù)時先接受4個字節(jié)的長度,然后根據(jù)解析出的包長進行循環(huán)接收直到數(shù)據(jù)包接完整為止,注意接收前4個字節(jié)的包長是也要進行循環(huán)接受直到接滿4個字節(jié)。

           有時候數(shù)據(jù)包傳輸過程中會出現(xiàn)錯誤,一般都是校驗CRC是否完整來校驗包的正確性。一旦數(shù)據(jù)包出現(xiàn)錯誤一般有兩種測試處理:第一是直接丟棄整個包, 該方法簡單但可能會失去某些比較重要的數(shù)據(jù)。 第二個是對根據(jù)報文對整個包進行遍歷把可能沒有錯誤的數(shù)據(jù)保持下來,但是該方案比較麻煩。

 

           最后一句感言:無論什么平臺和語言,基礎(chǔ)知識都是通用的,制約發(fā)展的因素不是我們會哪幾種語言,而是我們運用基礎(chǔ)知識去解決實際問題的能力。


發(fā)表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發(fā)表
主站蜘蛛池模板: 上饶市| 沙河市| 云安县| 独山县| 信宜市| 阿瓦提县| 虹口区| 油尖旺区| 怀远县| 南部县| 陕西省| 美姑县| 皋兰县| 清河县| 修文县| 泰宁县| 余姚市| 师宗县| 太谷县| 西和县| 仁布县| 安泽县| 沧源| 湟中县| 顺昌县| 蒙阴县| 大兴区| 崇阳县| 治县。| 平果县| 宝坻区| 阿合奇县| 大厂| 邻水| 五指山市| 鄱阳县| 中方县| 镇江市| 田林县| 剑河县| 乌拉特后旗|