一、協議至關於相互通訊的程序間達成的一種約定,它規定了分組報文的結構、交換方式、包含的意義以及怎樣對報文所包含的信息進行解析。java
二、TCP/IP協議族有IP協議、TCP協議和UDP協議。程序員
三、TCP協議和UDP協議使用的地址叫作端口號,用來區分同一主機上的不一樣應用程序。TCP協議和UDP協議也叫端到端傳輸協議,由於他們將數據從一個應用程序傳輸到另外一個應用程序,而IP協議只是將數據從一個主機傳輸到另外一個主機。web
四、在TCP/IP協議中,有兩部分信息用來肯定一個指定的程序:互聯網地址和端口號:其中互聯網地址由IP協議使用,而附加的端口地址信息則由傳輸協議(TCP或UDP協議)對其進行解析。編程
五、如今TCP/IP協議族中的主要socket類型爲流套接字(使用TCP協議)和數據報套接字(使用UDP協議),其中經過數據報套接字,應用程序一次只能發送最長65507個字節長度的信息。數組
六、一個TCP/IP套接字由一個互聯網地址,一個端對端協議(TCP協議或UDP協議)以及一個端口號惟一肯定。緩存
七、每一個端口都標識了一臺主機上的一個應用程序,實際上,一個端口肯定了一個主機上的一個套接字。主機中的多個程序能夠同時訪問同一個套接字,在實際應用中,訪問相同套接字的不一樣程序一般都屬於一個應用(如web服務程序的多個副本),但從理論上講,它們能夠屬於不一樣的應用。安全
一、編寫TCP客戶端程序,在實例化Socket類時,要注意,底層的TCP協議只能處理IP協議,若是傳遞的第一個參數是主機名字而不是你IP地址,Socket類具體實現的時候會將其解析成相應的地址,若由於某些緣由鏈接失敗,構造函數會拋出一個IOException異常。服務器
二、TCP協議讀寫數據時,read()方法在沒有可讀數據時會阻塞等待,直到有新的數據可讀。另外,TCP協議並不能肯定在read()和write()方法中所發送信息的界限,接收或發送的數據可能被TCP協議分割成了多個部分。網絡
三、編寫TCP服務器端的程序將在accept()方法處阻塞,以等待客戶端的鏈接請求,一旦取得鏈接,便要爲每一個客戶端的鏈接創建一個Socket實例來進行數據通訊。異步
四、在UDP程序中,建立DatagramPacket實例時,若是沒有指定遠程主機地址和端口,則該實例用來接收數據(儘管能夠調用setXXX()等方法指定),若是指定了遠程主機地址和端口,則該實例用來發送數據。
五、UDP程序在receive()方法處阻塞,直到收到一個數據報文或等待超時。因爲UDP協議是不可靠協議,若是數據報在傳輸過程當中發生丟失,那麼程序將會一直阻塞在receive()方法處,這對客戶端來講是確定不行的,爲了不這個問題,咱們在客戶端使用DatagramSocket類的setSoTimeout()方法來制定receive()方法的最長阻塞時間,並指定重發數據報的次數,若是每次阻塞都超時,而且重發次數達到了設置的上限,則關閉客戶端。
六、UDP服務器爲全部通訊使用同一套接字,這點與TCP服務器不一樣,TCP服務器則爲每一個成功返回的accept()方法建立一個新的套接字。
七、在UDP程序中,DatagramSocket的每一次receive()調用最多隻能接收調用一次send()方法所發送的數據,並且,不一樣的receive()方法調用絕對不會返回同一個send()方法所發送的額數據。
八、在UDP套接字編程中,若是receive()方法在一個緩衝區大小爲n的DatagramPscket實例中調用,而接受隊列中的第一個消息長度大於n,則receive()方法只返回這條消息的前n個字節,超出的其餘字節部分將自動被丟棄,並且也沒有任何消息丟失的提示。所以,接受者應該提供一個足夠大的緩存空間的DatagramPacket實例,以完整地存放調用receive()方法時應用程序協議所容許的最大長度的消息。一個DatagramPacket實例中所運行傳輸的最大數據量爲65507個字節,即UDP數據報文所能負載的最多數據,所以,使用一個有65600字節左右緩存數組的數據老是安全的。
九、在UDP套接字編程中,每個DatagramPacket實例都包含一個內部消息長度值,而該實例一接收到新消息,這個長度值即可能改變(以反映實際接收的消息的字節數)。若是一個應用程序使用同一個DatagramPacket實例屢次調用receive()方法,每次調用前就必須顯式地將消息的內部長度重置爲緩衝區的實際長度。
十、另外一個潛在問題的根源是DatagramPacket類的getData()方法,該方法老是返回緩衝區的原始大小,忽略了實際數據的內部偏移量和長度信息。
一、程序間達成的一種包含了信息交換的形式和意義的共識稱爲協議,用來實現特定應用程序的協議叫作應用程序協議。
二、TCP/IP協議的惟一約束是:信息必須在塊中發送和接收,而塊的長度必須是8的倍數,所以,咱們能夠認爲TCP/IP協議中傳輸的信息是字節序列。
三、關於字符,對於每一個整數值都比255小的一組字符,由於其每一個字符都可以做爲一個單獨的字節進行編碼,所以不須要其餘信息,而對於可能使用超過一個字節的大整數的編碼方式,就有多種方式對其進行編碼,這就是編碼方案。編碼字符集和字符的編碼方案結合起來稱爲字符集。在網絡編程中,發送者和接收者必須在文本字符串的表示方式上達成共識,最簡單的方法就是使用同一個標準字符集。
四、成幀技術解決了消息接收端如何定位消息的首尾位置的問題。與UDP協議不一樣,TCP協議中沒有消息邊界的概念,所以在使用TCP套接字時,成幀就是一個很是重要的考慮因素。
五、主要有兩種技術可以使消息接收者準確地找到消息的結束位置:基於定界符和顯式長度,前者對消息的結束由一個惟一的標記指出,即發送者在傳輸完數據後顯式添加一個特殊字符序列,這個特殊標記不能在傳輸的數據中出現,固然,填充技術可以對消息中出現的界定符進行修改,從而使接受者不將其識別爲界定符;後者在變長字段或消息前附加一個固定大小的字段值,用來指示該字段或消息中包含多少個字節。
一、主線程結束後,其餘線程也能夠繼續執行,Java虛擬機只有在全部非守護線程都執行完畢的狀況下才終止。
二、服務器通常每分鐘都要執行上千次客戶端的請求,所以,爲了更好的分析異常,大部分的服務器都會將它們的活動記錄寫入日誌,Java中能夠用java.util.logging.Logger類來實現相關功能,在Java中,每一個日誌記錄器由一個全局惟一的名字識別,能夠經過氣靜態方法getLogger(string name)來獲取者由名字name標示的惟一記錄器。默認狀況下,每一個logger有一個ConsoleHandler用來將消息打印到System.err中。Logger的一個重要特徵是它是線程安全的,便可以在並行運行的不一樣線程中調用它的方法,而不須要在調用者中添加額外的同步措施,若是沒有這個特徵,由不一樣線程記錄的不一樣消息將錯亂無章地寫入到日誌中。
三、用線程池實現TCP服務器端時,首先建立一個ServerSocket實例,而後建立N個線程,每一個線程反覆循環,從(共享的)ServerSocket實例接收客戶端鏈接。當多個線程同時調用一個ServerSocket實例的accept()方法時,它們都將阻塞等待,直到一個新的鏈接成功創建,而後系統選擇一個線程,用於剛剛創建起的新的鏈接,其餘線程則繼續阻塞等待。若是在一個客戶端鏈接被建立時,沒有線程在accept()方法上阻塞(即全部的線程都在爲其餘鏈接服務),系統則將新的鏈接排列在一個隊列中,直到下一次調用accept()方法。
四、利用線程池實現服務器端程序時,線程池的大小須要根據負載狀況進行調整,以使客戶端鏈接時間最短,理想的狀況是有一個調度工具,能夠在系統負載增長時擴展線程池的大小(低於上限值),負載減輕時縮減線程池的大小。Java中提供了Executor接口來管理調度線程,它就表明了一個根據某種策略來執行Runnable實例的對象其中可能包含了排隊和調度等細節,或如何選擇要執行的任務。在使用Executor時,任務是在Executor內部排隊,而不是在網絡系統中排隊。
五、ExecutorService接口繼承於Executor接口,當ExecutorService接口的實例調用execute()方法時,須要傳入一個實現了Runnable接口的實例,若是必要,它將建立一個新的線程來處理任務,但它首先會嘗試使用已有的線程,若是一個線程空閒了60秒以上,則將被移除線程池。值得注意的是,當達到穩定狀態時,緩存線程池服務最終將保持合適的線程數,以使每一個線程都保持忙碌,同時又不多建立或銷燬線程。
六、阻塞式Socket編程中,Socket的I/O會由於多種緣由而阻塞。數據輸入方法read()和receive()在沒有數據可讀時會阻塞,TCP套接字的write()方法在沒有足夠的空間緩存傳輸的數據時可能阻塞,ServerSocket的accept()方法和Socket的構造函數都會阻塞等待。當調用一個已經阻塞的方法將使用應用程序中止,並使運行它的線程無效。
七、Write()方法調用會阻塞等待,直到最有一個字節成功寫入到TCP實現的本地緩存中,若是可用的緩存空間比要寫入的數據小,在write()方法調用返回前,必須把一些數據成功傳輸到鏈接的另外一端。Java如今尚未提供任何使write()超時或有其餘線程將其打斷的方法,因此,若是一個能夠在Socket實例上發送大量數據的協議可能會無限期地阻塞下去。
八、有兩種類型的一對多服務:廣播和多播。對於廣播,(本地)網絡中的全部主機都會接收到一份數據副本,對於多播,消息只是發送給一個多播地址,網絡只是將數據分發給那些表示想要接收發送到該多播地址數據的主機。總的來講,只有UDP套接字容許廣播和多播。IPv4的多播地址範圍是224.0.0.0到239.255.255.255,IPv6中的多播地址是任何由FF開頭的地址。除了少出系統暴露的多播地址外,發送者能夠向異常範圍內的任何地址發送數據。Java中多播應用程序主要經過MulticastSocket實例進行通訊。
九、在TCP Socket通訊中,其中一端的read()方法返回-1代表通訊的另外一端關閉了套接字,更確切地說,是關閉了套接字所關聯的輸出流。
一、NIO主要包括兩個部分:java.nio.channles包介紹Selector和Channel抽象,java.nio包介紹Buffer抽象。Selector和Channel抽象的關鍵點是:一次輪詢一組客戶端,查找哪一個客戶端須要服務;Buffer則提供了比Stream抽象更高效和可預測的I/O。Channel使用的不是流,正是Buffer緩衝區來發送或讀寫數據。
二、Buffer抽象表明了一個有限容量的數據容器,其本質是一個數組,由指針指示了在哪存放數據和從哪讀取數據。使用Buffer有兩個主要的好處:第一,與讀寫緩衝區數據相關聯的系統開銷暴露給了程序員,能夠由程序員直接控制操做;第二,一些對Java對象的特殊Buffer映射操做可以直接操做底層平臺的資源。這些操做節省了在不一樣地址空間中複製數據的開銷——這在現代計算機體系結構中是開銷很大的操做。
三、NIO的強大功能部分來自於channel的非阻塞特性。NIO的Channel抽象的一個重要特徵就是能夠經過配置它的阻塞行爲,以實現非阻塞式的信道。在非阻塞式信道上調用一個方法老是會返回。例如,在一個非阻塞式ServerSocketChannel上調用accept()方法,若是有鏈接請求在等待,則返回客戶端SocketChannel,不然,返回null;read()方法在沒有數據可讀時,不會阻塞等待,而是返回0。 在等待鏈接、讀取數據等的時候,線程也能夠作其餘事情,這便實現了線程的異步操做
四、Selector類的select()方法會阻塞等待,直到有信道準備好了IO操做,或等待超時,或另外一個線程喚醒了它(調用了該選擇器的wakeup()方法)。select()方法返回的是自上次調用它以後,有多少通道變爲就緒狀態。若是調用select()方法,由於有一個通道變成就緒狀態,返回了1,若再次調用select()方法,若是另外一個通道就緒了,它會再次返回1。若是對第一個就緒的channel沒有作任何操做,如今就有兩個就緒的通道,但在每次select()方法調用之間,只有一個通道就緒了。
五、咱們在用Iterator迭代SelectionKey集合時,每次迭代末尾註意調用remove()方法。Selector不會本身從已選擇鍵集中移除SelectionKey實例。必須在處理完通道時本身移除,以備下次該通道變成就緒時,Selector能夠再次將其放入已選擇鍵集中。若是不移除每一個處理過的鍵,它就會在下次調用select()方法時仍然保留在集合中,並且可能會有無用的操做來調用它。Selector選擇器實現了在單個線程中監聽多個信道的功能。
六、緩衝區是定長的,不能夠擴展容量,ByteBuffer是最經常使用的緩衝區。緩衝區中各索引值的大小關係:0=<mark=<position=<limit=<capacity。
七、allocateDirect()方法嘗試分配直接緩存區,使用直接緩衝區,Java將從平臺可以直接進行I/O操做的存儲空間中爲緩衝區分配後援存儲空間,但不保證必定能成功,所以在嘗試分配直接緩衝區後必須調用isDirect()方法進行檢查,分配和銷燬直接緩衝區一般要比分配和銷燬非直接緩衝區消耗更多的系統資源。
八、Buffer的clear()方法並不改變緩衝區中的數據,它只是將position設置爲0,並將limit設置爲等於capacity,從而使緩衝區準備好從緩衝區的put操做或信道的讀操做接收新的數據。flip()方法用來將緩衝區準備爲數據傳出狀態,這經過將limit設置爲position的當前值,再將position的值設爲0來實現。Rewind()方法將position設置爲0,並使mark值無效,limit值不變,這樣即可以重複傳送緩衝區中的數據。compact()方法將position與limit之間的元素複製到緩衝區的開始位置,從而爲後續的read()/put()操做讓出空間,但數據複製是一個很是耗費系統資源的操做,所以要保守地使用compact()方法。若是調用slice()方法建立了一個共享了原始緩衝區子序列的新緩衝區,則在先緩衝區上調用array()方法仍是返回整個緩衝數組。
九、對於非阻塞SocketChannel來講,一旦已經調用connect()方法發起鏈接,底層套接字可能既不是已經鏈接,也不是沒有鏈接,而是正在鏈接。因爲底層協議的工做機制,套接字可能會在這個狀態一直保持下去,這時候就須要循環地調用finishConnect()方法來檢查是否完成鏈接,在等待鏈接的同時,線程也能夠作其餘事情,這便實現了線程的異步操做。
十、每一個選擇器都有一組與之關聯的信道,一個信道也能夠註冊多個Selector實例,所以能夠有多個關聯的SelectionKey實例。任何對key所關聯的興趣操做集的改變,都只在下次調用select()方法後纔會生效。對於serverSocketChannel來講,accept是惟一的有效操做,而對於socketChannel來講,有效操做包括讀、寫和鏈接,對於DatagramChannle,只有讀寫操做是有效的。一個信道可能只與一個選擇器註冊一次,所以後續對register()方法的調用只是簡單地更新該key所關聯的興趣操做集。
十一、 selectedKeys()方法返回的鍵集是可修改的,實際上在兩次調用select()方法之間,都必須手動將其清空,換句話說,select()方法只會在已有的所選鍵集上添加鍵,它們不會建立新的建集。