04.Java網(wǎng)絡(luò)編程(轉(zhuǎn)載)

1.網(wǎng)絡(luò)編程
  1.1計算機網(wǎng)絡(luò)概述
  網(wǎng)絡(luò)編程的實質(zhì)就是兩個(或多個)設(shè)備(例如計算機)之間的數(shù)據(jù)傳輸。
  按照計算機網(wǎng)絡(luò)的定義,通過一定的物理設(shè)備將處于不同位置的計算機連接起來組成的網(wǎng)絡(luò),這個網(wǎng)絡(luò)中包含的設(shè)備有:計算機、路由器、交換機等等。
  其實從軟件編程的角度來說,對于物理設(shè)備的理解不需要很深刻,就像你打電話時不需要很熟悉通信網(wǎng)絡(luò)的底層實現(xiàn)是一樣的,但是當深入到網(wǎng)絡(luò)編程的底層時,這些基礎(chǔ)知識是必須要補的。
  路由器和交換機組成了核心的計算機網(wǎng)絡(luò),計算機只是這個網(wǎng)絡(luò)上的節(jié)點以及控制等,通過光纖、網(wǎng)線等連接將設(shè)備連接起來,從而形成了一張巨大的計算機網(wǎng)絡(luò)。
  網(wǎng)絡(luò)最主要的優(yōu)勢在于共享:共享設(shè)備和數(shù)據(jù),現(xiàn)在共享設(shè)備最常見的是打印機,一個公司一般一個打印機即可,共享數(shù)據(jù)就是將大量的數(shù)據(jù)存儲在一組機器中,其它的計算機通過網(wǎng)絡(luò)訪問這些數(shù)據(jù),例如網(wǎng)站、銀行服務(wù)器等等。
  如果需要了解更多的網(wǎng)絡(luò)硬件基礎(chǔ)知識,可以閱讀《計算機網(wǎng)絡(luò)》教材,對于基礎(chǔ)進行強化,這個在基礎(chǔ)學習階段不是必須的,但是如果想在網(wǎng)絡(luò)編程領(lǐng)域有所造詣,則是一個必須的基本功。
  對于網(wǎng)絡(luò)編程來說,最主要的是計算機和計算機之間的通信,這樣首要的問題就是如何找到網(wǎng)絡(luò)上的計算機呢?這就需要了解IP地址的概念。
  為了能夠方便的識別網(wǎng)絡(luò)上的每個設(shè)備,網(wǎng)絡(luò)中的每個設(shè)備都會有一個唯一的數(shù)字標識,這個就是IP地址。在計算機網(wǎng)絡(luò)中,現(xiàn)在命名IP地址的規(guī)定是IPv4協(xié)議,該協(xié)議規(guī)定每個IP地址由4個0-255之間的數(shù)字組成,例如10.0.120.34。每個接入網(wǎng)絡(luò)的計算機都擁有唯一的IP地址,這個IP地址可能是固定的,例如網(wǎng)絡(luò)上各種各樣的服務(wù)器,也可以是動態(tài)的,例如使用ADSL撥號上網(wǎng)的寬帶用戶,無論以何種方式獲得或是否是固定的,每個計算機在聯(lián)網(wǎng)以后都擁有一個唯一的合法IP地址,就像每個手機號碼一樣。
  但是由于IP地址不容易記憶,所以為了方便記憶,有創(chuàng)造了另外一個概念——域名(Domain Name),例如sohu.com等。一個IP地址可以對應(yīng)多個域名,一個域名只能對應(yīng)一個IP地址。域名的概念可以類比手機中的通訊簿,由于手機號碼不方便記憶,所以添加一個姓名標識號碼,在實際撥打電話時可以選擇該姓名,然后撥打即可。
  在網(wǎng)絡(luò)中傳輸?shù)臄?shù)據(jù),全部是以IP地址作為地址標識,所以在實際傳輸數(shù)據(jù)以前需要將域名轉(zhuǎn)換為IP地址,實現(xiàn)這種功能的服務(wù)器稱之為DNS服務(wù)器,也就是通俗的說法叫做域名解析。例如當用戶在瀏覽器輸入域名時,瀏覽器首先請求DNS服務(wù)器,將域名轉(zhuǎn)換為IP地址,然后將轉(zhuǎn)換后的IP地址反饋給瀏覽器,然后再進行實際的數(shù)據(jù)傳輸。
  當DNS服務(wù)器正常工作時,使用IP地址或域名都可以很方便的找到計算機網(wǎng)絡(luò)中的某個設(shè)備,例如服務(wù)器計算機。當DNS不正常工作時,只能通過IP地址訪問該設(shè)備。所以IP地址的使用要比域名通用一些。
  IP地址和域名很好的解決了在網(wǎng)絡(luò)中找到一個計算機的問題,但是為了讓一個計算機可以同時運行多個網(wǎng)絡(luò)程序,就引入了另外一個概念——端口(port)。
  在介紹端口的概念以前,首先來看一個例子,一般一個公司前臺會有一個電話,每個員工會有一個分機,這樣如果需要找到這個員工的話,需要首先撥打前臺總機,然后轉(zhuǎn)該分機號即可。這樣減少了公司的開銷,也方便了每個員工。在該示例中前臺總機的電話號碼就相當于IP地址,而每個員工的分機號就相當于端口。
  有了端口的概念以后,在同一個計算機中每個程序?qū)?yīng)唯一的端口,這樣一個計算機上就可以通過端口區(qū)分發(fā)送給每個端口的數(shù)據(jù)了,換句話說,也就是一個計算機上可以并發(fā)運行多個網(wǎng)絡(luò)程序,而不會在互相之間產(chǎn)生干擾。
  在硬件上規(guī)定,端口的號碼必須位于0-65535之間,每個端口唯一的對應(yīng)一個網(wǎng)絡(luò)程序,一個網(wǎng)絡(luò)程序可以使用多個端口。這樣一個網(wǎng)絡(luò)程序運行在一臺計算上時,不管是客戶端還是服務(wù)器,都是至少占用一個端口進行網(wǎng)絡(luò)通訊。在接收數(shù)據(jù)時,首先發(fā)送給對應(yīng)的計算機,然后計算機根據(jù)端口把數(shù)據(jù)轉(zhuǎn)發(fā)給對應(yīng)的程序。
  有了IP地址和端口的概念以后,在進行網(wǎng)絡(luò)通訊交換時,就可以通過IP地址查找到該臺計算機,然后通過端口標識這臺計算機上的一個唯一的程序。這樣就可以進行網(wǎng)絡(luò)數(shù)據(jù)的交換了。
  但是,進行網(wǎng)絡(luò)編程時,只有IP地址和端口的概念還是不夠的,下面就介紹一下基礎(chǔ)的網(wǎng)絡(luò)編程相關(guān)的軟件基礎(chǔ)知識。

1.2網(wǎng)絡(luò)編程概述
  網(wǎng)絡(luò)編程中有兩個主要的問題,一個是如何準確的定位網(wǎng)絡(luò)上一臺或多臺主機,另一個就是找到主機后如何可靠高效的進行數(shù)據(jù)傳輸。在TCP/IP協(xié)議中IP層主要負責網(wǎng)絡(luò)主機的定位,數(shù)據(jù)傳輸?shù)穆酚桑蒊P地址可以唯一地確定Internet上的一臺主機。而TCP層則提供面向應(yīng)用的可靠的或非可靠的數(shù)據(jù)傳輸機制,這是網(wǎng)絡(luò)編程的主要對象,一般不需要關(guān)心IP層是如何處理數(shù)據(jù)的。
  按照前面的介紹,網(wǎng)絡(luò)編程就是兩個或多個設(shè)備之間的數(shù)據(jù)交換,其實更具體的說,網(wǎng)絡(luò)編程就是兩個或多個程序之間的數(shù)據(jù)交換,和普通的單機程序相比,網(wǎng)絡(luò)程序最大的不同就是需要交換數(shù)據(jù)的程序運行在不同的計算機上,這樣就造成了數(shù)據(jù)好換的復雜。雖然通過IP地址和端口號可以找到網(wǎng)絡(luò)上運行的一個程序,但是如果需要進行網(wǎng)絡(luò)編程,則需要了解網(wǎng)絡(luò)通訊的過程。
  網(wǎng)絡(luò)通訊基于“請求—響應(yīng)”模型。在網(wǎng)絡(luò)通訊中,第一次主動發(fā)起通訊的程序被稱為客戶端(client)程序,簡稱客戶端,而第一次通訊中等待鏈接的程序被稱為服務(wù)器端(Server)程序,簡稱服務(wù)器。一旦通訊建立,則客戶端和服務(wù)器端完全一樣,沒有本質(zhì)區(qū)別。
  由此,網(wǎng)絡(luò)編程中的兩種程序就分別是客戶端和服務(wù)器端,例如QQ程序,每個QQ用戶安裝的都是QQ客戶端程序,而QQ服務(wù)器端程序則在騰訊公司的機房中,為大量的QQ用戶提供服務(wù)。這種網(wǎng)絡(luò)編程的結(jié)構(gòu)被稱為客戶端/服務(wù)器結(jié)構(gòu),也叫Client/Serverj結(jié)構(gòu),簡稱C/S結(jié)構(gòu)。
  使用C/S結(jié)構(gòu)的程序,在開發(fā)時需要分別開發(fā)客戶端和服務(wù)器端,這種結(jié)構(gòu)的優(yōu)勢在于客戶端是專門開發(fā)的,所以根據(jù)需要實現(xiàn)各種效果,專業(yè)點的說就是表現(xiàn)力豐富,而服務(wù)器端也需要專門進行開發(fā)。但是這種結(jié)構(gòu)也存在著很多不足,例如通用性差,幾乎不能通用,也就是說一種程序的客戶端只能和對應(yīng)的服務(wù)器端通訊,而不能和其他服務(wù)器端通訊,在實際維護中,也需要維護專門的客戶端和服務(wù)器端,維護的壓力比較大。
  其實在運行很多程序時,沒有必要使用專門的客戶端,而需要使用通用的客戶端,例如瀏覽器,使用瀏覽器作為客戶端的結(jié)構(gòu)稱為瀏覽器/服務(wù)器結(jié)構(gòu),也叫做Browser/Server結(jié)構(gòu),簡稱B/S結(jié)構(gòu)。
  使用B/S結(jié)構(gòu)的程序,在開發(fā)時只需要開發(fā)服務(wù)器端即可,這種優(yōu)勢在于開發(fā)壓力比較小,不需要維護客戶端,但是這種結(jié)構(gòu)也存在這很多不足,例如瀏覽器的限制比較大,表現(xiàn)了不強,不能進行系統(tǒng)級別的操作等。
  總之C/S結(jié)構(gòu)和B/S結(jié)構(gòu)是現(xiàn)在網(wǎng)絡(luò)編程中常見的兩種結(jié)構(gòu),B/S結(jié)構(gòu)其實也就是一種特殊的C/S結(jié)構(gòu)。
  另外簡單的介紹一下P2P(Point to Point)程序,常見的如BT、電驢等。P2P程序是一種特殊的程序,應(yīng)該一個P2P程序中既包含客戶端程序,也包含服務(wù)器端程序,例如BT,使用客戶端程序部分連接其它的種子(服務(wù)器端),而使用服務(wù)器端向其它的BT客戶端傳輸數(shù)據(jù)。如果這個還不是很清楚,其實P2P程序和手機是一樣的,當手機撥打電話時就是使用客戶端的作用,而手機處于待機狀態(tài)時,可以接收到其它用戶撥打的電話則起的就是服務(wù)器端的功能,只是一般的手機不能同時使用撥打電話和接聽電話的功能,而P2P程序?qū)崿F(xiàn)了該功能。
  最后介紹一下網(wǎng)絡(luò)編程中最重要的,也是最復雜的概念——協(xié)議(protocol)。按照前面的介紹,網(wǎng)絡(luò)編程就是運行在不同計算機中兩個程序之間的數(shù)據(jù)交換。在實際進行數(shù)據(jù)交換時,為了讓接收端理解該數(shù)據(jù),計算機比較笨,什么都不懂的,那么久需要規(guī)定該數(shù)據(jù)的格式,這個數(shù)據(jù)的格式就是協(xié)議。
  如果沒有理解協(xié)議的概念,那么再舉一個例子,記得有個電影叫《永不消逝的電波》,講述的是地下黨通過電臺發(fā)送情報的故事,這里我們不探討電影的劇情,而只關(guān) 心電臺發(fā)送的數(shù)據(jù)。在實際發(fā)報時,需要首先將需要發(fā)送的內(nèi)容轉(zhuǎn)換為電報編碼,然后將電報編碼發(fā)送出去,而接收端接收的是電報編碼,如果需要理解電報的內(nèi)容 則需要根據(jù)密碼本翻譯出該電報的內(nèi)容。這里的密碼本就規(guī)定了一種數(shù)據(jù)格式,這種對于網(wǎng)絡(luò)中傳輸?shù)臄?shù)據(jù)格式在網(wǎng)絡(luò)編程中就被稱作協(xié)議。
  那么如何編寫協(xié)議格式呢?答案是隨意。只要按照這種協(xié)議格式能夠生成唯一的編碼,按照該編碼可以唯一的解析出發(fā)送數(shù)據(jù)的內(nèi)容即可。也正因為各個網(wǎng)絡(luò)程序之間協(xié)議格式的不同,所以才導致了客戶端程序都是專用的結(jié)構(gòu)。
  在實際的網(wǎng)絡(luò)編程中,最麻煩的內(nèi)容不是數(shù)據(jù)的發(fā)送和接受,因為這個功能在幾乎所有編程語言中都提供了封裝好的API進行調(diào)用,最麻煩的內(nèi)容就是協(xié)議的設(shè)計及協(xié)議的生產(chǎn)和解析,這個才是網(wǎng)絡(luò)編程最核心的內(nèi)容。
  1.3網(wǎng)絡(luò)通訊方式
  在現(xiàn)有的網(wǎng)絡(luò)中,網(wǎng)絡(luò)通訊的方式主要有兩種:
  1.TCP(傳輸控制協(xié)議)方式。
  2.UDP(用戶數(shù)據(jù)協(xié)議)方式。
  為了方便理解這兩種方式,還是先來看個例子。大家使用手機時,向別人傳遞信息時有兩種方式:撥打電話和發(fā)送短信。使用撥打電話的方式可以保證該信息傳遞給別人,因為別人接電話時本身就確認收到了該信息。而發(fā)送短信的方式價格低廉,使用方便,但是接受人可能收不到。
  在網(wǎng)絡(luò)通訊中,TCP方式就類似于撥打電話,使用該種方式進行網(wǎng)絡(luò)通訊時,需要建立專門的虛擬連接,然后進行可靠的數(shù)據(jù)傳輸,如果數(shù)據(jù)發(fā)送失敗,則客戶端會自動重發(fā)該數(shù)據(jù),而UDP方式就類似于發(fā)送短信,使用這種方式進行網(wǎng)絡(luò)通訊時,不需要建立專門的虛擬連接,傳輸也不是很可靠,如果發(fā)送失敗則客戶端無法獲得。
  這兩種傳輸方式都是實際的網(wǎng)絡(luò)編程中進行使用,重要的數(shù)據(jù)一般使用TCP方式進行數(shù)據(jù)傳輸,而大量的非核心數(shù)據(jù)則都通過UDP方式進行傳遞,在一些程序中甚至結(jié)合使用這兩種方式進行數(shù)據(jù)的傳遞。
  由于TCP需要建立專用的虛擬連接以及確認傳輸是否正確,所以使用TCP方式的速度稍微慢一些,而且傳輸時產(chǎn)生的數(shù)據(jù)量要比UDP稍微大一些。
  關(guān)于網(wǎng)絡(luò)編程的基礎(chǔ)知識就介紹這么多,如果需要深入了解相關(guān)知識請閱讀專門的計算機網(wǎng)絡(luò)書籍,下面開始介紹Java語言中網(wǎng)絡(luò)編程的相關(guān)技術(shù)。

1.3網(wǎng)絡(luò)編程步驟
  按照前面的基礎(chǔ)知識介紹,無論使用TCP方式還是UDP方式進行網(wǎng)絡(luò)通訊,網(wǎng)絡(luò)編程都是由客戶端和服務(wù)器端組成,所以,下面介紹網(wǎng)絡(luò)編程的步驟時,均以C/S結(jié)構(gòu)為基礎(chǔ)進行介紹。
  1.3.1客戶端網(wǎng)絡(luò)編程步驟
  客戶端是指網(wǎng)絡(luò)編程中首先發(fā)起連接的程序,客戶端一般實現(xiàn)程序界面和基本邏輯實現(xiàn),在進行實際的客戶端編程時,無論客戶端復雜還是簡單,以及客戶端實現(xiàn)的方式,客戶端的編程主要由三個步驟實現(xiàn):
  1.建立網(wǎng)絡(luò)連接
  客戶端網(wǎng)絡(luò)編程的第一步都是建立網(wǎng)絡(luò)連接。在建立網(wǎng)絡(luò)連接時需要指定連接的服務(wù)器的IP地址和端口號,建立完成以后,會形成一條虛擬的連接,后續(xù)的操作就可以通過該連接實現(xiàn)數(shù)據(jù)交換了。
  2.交換數(shù)據(jù)
  連接建立以后,就可以通過這個連接交換數(shù)據(jù)了,交換數(shù)據(jù)嚴格要求按照請求響應(yīng)模型進行,由客戶端發(fā)送一個請求數(shù)據(jù)到服務(wù)器,服務(wù)器反饋一個響應(yīng)數(shù)據(jù)后給客戶端,如果客戶端不發(fā)送請求則服務(wù)器就不響應(yīng)。
  根據(jù)邏輯需要,可以多次交換數(shù)據(jù),但是還是必須遵循請求響應(yīng)模型。
  3.關(guān)閉網(wǎng)絡(luò)連接
  在數(shù)據(jù)交換完成后,關(guān)閉網(wǎng)絡(luò)連接,釋放程序占用的端口、內(nèi)存等系統(tǒng)資源,結(jié)束網(wǎng)絡(luò)編程。
  最基本的步驟一般都是這三個步驟,在實際實現(xiàn)時,步驟2會出現(xiàn)重復,在進行代碼組織時,由于網(wǎng)絡(luò)編程是比較耗時的操作,所以一般開啟專門的現(xiàn)場進行網(wǎng)絡(luò)通訊。

1.4服務(wù)器端網(wǎng)絡(luò)編程步驟
  服務(wù)器是指網(wǎng)絡(luò)編程中被等待連接的程序,服務(wù)器端一般實現(xiàn)程序的核心邏輯以及數(shù)據(jù)存儲等核心功能。服務(wù)器端的編程步驟和客戶端不同,是由四個步驟實現(xiàn),依次是:
  1.監(jiān)聽端口
  服務(wù)器端屬于被動等待連接,所以服務(wù)器端啟動以后,不需要發(fā)起連接,而只需要監(jiān)聽本地計算機的某個固定端口即可。這個端口就是服務(wù)器端開放給客戶端的端口,服務(wù)器端程序運行的本地計算機的IP地址就是服務(wù)器端程序的IP地址。
  2.獲得連接
  當客戶端連接到服務(wù)器端時,服務(wù)器端就可以獲得一個連接,這個連接包含客戶端信息,例如客戶端IP地址等,服務(wù)器端和客戶端通過該連接進行數(shù)據(jù)交換。
  一般在服務(wù)器端編程中,當獲得連接時,需要開啟專門的線程處理該連接,每個連接都由獨立的線程實現(xiàn)。
  3.交換數(shù)據(jù)
  服務(wù)器端通過獲得的連接進行數(shù)據(jù)交換。服務(wù)器端的數(shù)據(jù)交換步驟是首先接收客戶端發(fā)送過來的數(shù)據(jù),然后進行邏輯處理,再把處理以后的結(jié)果數(shù)據(jù)發(fā)送給客戶端。簡單來說,就是先接收再發(fā)送,這個和客戶端的數(shù)據(jù)交換順序不同。
  其實,服務(wù)器端獲得的連接和客戶端的連接是一樣的,只是數(shù)據(jù)交換的步驟不同。當然,服務(wù)器端的數(shù)據(jù)交換也是可以多次進行的。在數(shù)據(jù)交換完成以后,關(guān)閉和客戶端的連接。
  4.關(guān)閉連接
  當服務(wù)器程序關(guān)閉時,需要關(guān)閉服務(wù)器端,通過關(guān)閉服務(wù)器端使得服務(wù)器監(jiān)聽的端口以及占用的內(nèi)存可以釋放出來,實現(xiàn)了連接的關(guān)閉。
  其實服務(wù)器端編程的模型和呼叫中心的實現(xiàn)是類似的,例如移動的客服電話10086就是典型的呼叫中心,當一個用戶撥打10086時,轉(zhuǎn)接給一個專門的客服人員,由該客服實現(xiàn)和該用戶的問題解決,當另外一個用戶撥打10086時,則轉(zhuǎn)接給另一個客服,實現(xiàn)問題解決,依次類推。
  在服務(wù)器端編程時,10086這個電話號碼就類似于服務(wù)器端的端口號碼,每個用戶就相當于一個客戶端程序,每個客服人員就相當于服務(wù)器端啟動的專門和客戶端連接的線程,每個線程都是獨立進行交互的。
  這就是服務(wù)器端編程的模型,只是TCP方式是需要建立連接的,對于服務(wù)器端的壓力比較大,而UDP是不需要建立連接的,對于服務(wù)器端的壓力比較小罷了。
  總之,無論使用任何語言,任何方式進行基礎(chǔ)的網(wǎng)絡(luò)編程,都必須遵循固定的步驟進行操作,在熟悉了這些步驟以后,可以根據(jù)需要進行邏輯上的處理,但是還是必須遵循固定的步驟進行。
  其實,基礎(chǔ)的網(wǎng)絡(luò)編程本身不難,也不需要很多的基礎(chǔ)網(wǎng)絡(luò)知識,只是由于編程的基礎(chǔ)功能都已經(jīng)由API實現(xiàn),而且需要按照固定的步驟進行,所以在入門時有一定的門檻,希望下面的內(nèi)容能夠?qū)⒛憧焖俚膸刖W(wǎng)絡(luò)編程技術(shù)的大門。

2.Java網(wǎng)絡(luò)編程技術(shù)
  和網(wǎng)絡(luò)編程有關(guān)的基本API位于Java.NET包中,該包中包含了基本的網(wǎng)絡(luò)編程實現(xiàn),該包是網(wǎng)絡(luò)編程的基礎(chǔ)。該包既包含基本的網(wǎng)絡(luò)編程類,也包含封裝后的專門處理WEB相關(guān)的處理類。
  首先來介紹一下基礎(chǔ)的網(wǎng)絡(luò)類-InetAddress類。該類的功能是代表一個IP地址,并且將IP地址和域名相關(guān)的操作方法包含在該類的內(nèi)部。關(guān)于該類的使用,下面通過一個基礎(chǔ)的代碼演示該類的使用。

import java.net.InetAddress;
import java.net.UnknownHostException;

public class InetAddressDemo {
    public static void main(String[] args) {

        try {
            InetAddress inet1 = InetAddress.getByName("www.163.com");
            System.out.println(inet1);
            InetAddress inet2=InetAddress.getByName("127.0.0.1");
            System.out.println(inet2);
            InetAddress inet3=InetAddress.getLocalHost();
            System.out.println(inet3);
            String host =inet3.getHostName();
            System.out.println("域名:"+host);
            String ip=inet3.getHostAddress();
            System.out.println("IP:"+ip);
        } catch (UnknownHostException e) {
            e.printStackTrace();
        }
    }
}

在該示例代碼中,演示了InetAddress類的基本使用,并使用了該類的幾個常用方法,該代碼的執(zhí)行結(jié)果是:
www.163.com/202.201.14.182
/127.0.0.1
DESKTOP-HRHF03J/192.168.1.196
域名:DESKTOP-HRHF03J
IP:192.168.1.196
說明:由于該代碼中包含一個互聯(lián)網(wǎng)的網(wǎng)址,所以運行該程序時需要聯(lián)網(wǎng),否則將產(chǎn)生異常。
在后續(xù)的使用中,經(jīng)常包含需要使用InetAddress對象代表IP地址的構(gòu)造方法,當然,該類的使用補水必須的,也可以使用字符串來代表IP地址。

3.TCP編程
  在Java語言中,對于TCP方式的網(wǎng)絡(luò)編程提供了良好的支持,在實際實現(xiàn)時,以java.net.socket類代表客戶端連接,以java.net.ServerSocket類作為服務(wù)器端連接。在進行網(wǎng)絡(luò)編程時,底層網(wǎng)絡(luò)通訊的細節(jié)已經(jīng)實現(xiàn)了比較高的封裝,所以在程序員實際編程時,只需要指定IP地址和端口號就可以建立連接了。正是由于這種高度的封裝,一方面,簡化了Java語言網(wǎng)絡(luò)編程的難度,另外也使得使用Java語言進行網(wǎng)絡(luò)編程無法深入到網(wǎng)絡(luò)的底層,所以使用Java語言進行網(wǎng)絡(luò)底層系統(tǒng)編程很困難,具體點說,Java語言無法事先底層的網(wǎng)絡(luò)嗅探以及獲得IP包結(jié)構(gòu)等消息。但是由于Java語言的網(wǎng)絡(luò)編程比較簡答,所以還是獲得了廣泛的使用。
  在使用TCP方式進行網(wǎng)絡(luò)編程時,需要按照前面介紹的網(wǎng)絡(luò)編程的步驟進行,下面分別介紹一下在Java語言中客戶端和服務(wù)器端的實現(xiàn)步驟。在客戶端網(wǎng)絡(luò)編程中,首先需要建立連接,在Java API中以及java.net.socket類的對象代表網(wǎng)絡(luò)連接,所以建立客戶端網(wǎng)絡(luò)連接,也就是創(chuàng)建Socket類型的對象,該對象代表網(wǎng)絡(luò)連接,示例如下:
  Socket socket1=new Socket(“192.168.1.103”,10000);   Socket socket2=new Socket(“www.sohu.com”,80);
  上面的代碼中,socket1實現(xiàn)的是連接到IP地址是192.168.1.103的計算機的10000號端口,而socket2實現(xiàn)的是連接到域名是www.sohu.com的計算機的80號端口,至于底層網(wǎng)絡(luò)如何實現(xiàn)建立連接,對于程序員來說是完全透明的。如果建立連接時,本機網(wǎng)絡(luò)不通,或服務(wù)器端程序未開啟,則會拋出異常。
  連接一旦建立,則完成了客戶端編程的第一步,緊接著的步驟就是按照“請求-響應(yīng)”模型進行網(wǎng)絡(luò)數(shù)據(jù)交換,在Java語言中,數(shù)據(jù)傳輸功能由Java IO實現(xiàn),也就是說只需要從連接中獲得輸入流和輸出流即可,然后將需要發(fā)送的數(shù)據(jù)寫入連接對象的輸出流中,在發(fā)送完成后從輸入流中讀取數(shù)據(jù)即可。示例代碼如下:

OutputStream os=socket1.getOutputStream();
InputStream is=socket1,getInputStream();

上面的代碼中,分別從socket1這個連接對象獲得了輸出流和輸入流對象,在整個網(wǎng)絡(luò)編程中,后續(xù)的數(shù)據(jù)交換就變成了IO操作,也就是遵循“請求-響應(yīng)”模式的規(guī)定,先向輸出流中寫入數(shù)據(jù),這些數(shù)據(jù)會被系統(tǒng)發(fā)送出去,然后再從輸入流中讀取服務(wù)器端的反饋信息,這樣就完成了一次數(shù)據(jù)交換工作,當然這個數(shù)據(jù)交換可以多次進行。
  這里獲得的只是最基本的輸出流和輸入流對象,還可以根據(jù)前面學習到的IO知識,使用流的嵌套將這些獲得的基本流對象轉(zhuǎn)換成需要的裝飾流對象,從而方便數(shù)據(jù)的操作。
  最后當數(shù)據(jù)交換完成以后,關(guān)閉網(wǎng)絡(luò)連接,釋放網(wǎng)絡(luò)連接占用的系統(tǒng)端口和內(nèi)存等資源,完成網(wǎng)絡(luò)操作,示例代碼如下:
  socket1.close();
  這就是最基本的網(wǎng)絡(luò)編程功能介紹,下面是一個簡單的網(wǎng)絡(luò)客戶端程序示例,該程序的作用是向服務(wù)器發(fā)送一個字符串“Hello”,并將服務(wù)器端的反饋顯示到控制臺,數(shù)據(jù)交換只進行一次,當數(shù)據(jù)交換完成以后關(guān)閉網(wǎng)絡(luò)連接,程序結(jié)束,實現(xiàn)的代碼如下:

package tcp;
import java.io.*;
import java.net.*;
/**
 * 簡單的Socket客戶端
 * 功能為:發(fā)送字符串“Hello”到服務(wù)器端,并打印出服務(wù)器端的反饋
 */
public class SimpleSocketClient {
         public static void main(String[] args) {
                   Socket socket = null;
                   InputStream is = null;
                   OutputStream os = null;
                   //服務(wù)器端IP地址
                   String serverIP = "127.0.0.1";
                   //服務(wù)器端端口號
                   int port = 10000;
                   //發(fā)送內(nèi)容
                   String data = "Hello";
                   try {
                            //建立連接
                            socket = new Socket(serverIP,port);
                            //發(fā)送數(shù)據(jù)
                            os = socket.getOutputStream();
                            os.write(data.getBytes());
                            //接收數(shù)據(jù)
                            is = socket.getInputStream();
                            byte[] b = new byte[1024];
                            int n = is.read(b);
                            //輸出反饋數(shù)據(jù)
                            System.out.println("服務(wù)器反饋:" + new String(b,0,n));
                   } catch (Exception e) {
                            e.printStackTrace(); //打印異常信息
                   }finally{
                            try {
                                     //關(guān)閉流和連接
                                     is.close();
                                     os.close();
                                     socket.close();
                            } catch (Exception e2) {}
                   }
         }
}

在該示例代碼中建立了一個連接到IP地址為127.0.0.1,端口號為10000的TCP類型的網(wǎng)絡(luò)連接,然后獲得連接的輸出流對象,將需要發(fā)送的字符串“Hello”轉(zhuǎn)換為波耶特數(shù)組寫入到輸出流中,由系統(tǒng)自動完成將輸出流中的數(shù)據(jù)發(fā)送出去,如果需要強制發(fā)送,可以調(diào)用輸出流對象中的flush方法實現(xiàn)。在數(shù)據(jù)發(fā)送出去后,從連接對象的輸入流中讀取服務(wù)器端的反饋信息,讀取時可以使用IO中的各種讀取方法進行讀取,這里使用最簡單的方法進行讀取。從輸入流中讀取到的內(nèi)容就是服務(wù)器端的反饋,并將讀取到的內(nèi)容在客戶端的控制臺進行輸出,最后依次關(guān)閉打開的流對象和網(wǎng)絡(luò)連接對象。
  這是一個簡單的功能示例,在該示例中演示了TCP類型的網(wǎng)絡(luò)客戶端基本方法的使用,該代碼只起演示目的,還無法達到實用的級別。
  如果需要在控制臺下面編譯和運行該代碼,需要首先在控制臺下切換到源代碼所在的目錄,然后依次輸入編譯和運行命令:

 javac –d . SimpleSocketClient.java

 java tcp.SimpleSocketClient

和下面將要介紹的SimpleSocketServer服務(wù)器端組合運行時,程序的輸出結(jié)果為:

 服務(wù)器反饋:Hello

介紹完一個簡單的客戶端編程的示例,下面接著介紹一下TCP類型的服務(wù)器端的編寫。首先需要說明的是,客戶端的步驟和服務(wù)器端的編寫步驟不同,所以在學習服務(wù)器端編程時注意不要和客戶端混淆起來。
  在服務(wù)器端程序編程中,由于服務(wù)器端實現(xiàn)的是被動等待連接,所以服務(wù)器端編程的第一個步驟是監(jiān)聽端口,也就是監(jiān)聽是否有客戶端連接到達。實現(xiàn)服務(wù)器端監(jiān)聽的代碼為:
  ServerSocket ss = new ServerSocket(10000);
  該代碼實現(xiàn)的功能是監(jiān)聽當前計算機的10000號端口,如果在執(zhí)行該代碼時,10000號端口已經(jīng)被別的程序占用,那么將拋出異常。否則將實現(xiàn)監(jiān)聽。
  服務(wù)器端編程的第二個步驟是獲得連接。該步驟的作用是當有客戶端連接到達時,建立一個和客戶端連接對應(yīng)的Socket連 接對象,從而釋放客戶端連接對于服務(wù)器端端口的占用。實現(xiàn)功能就像公司的前臺一樣,當一個客戶到達公司時,會告訴前臺我找某某某,然后前臺就通知某某某, 然后就可以繼續(xù)接待其它客戶了。通過獲得連接,使得客戶端的連接在服務(wù)器端獲得了保持,另外使得服務(wù)器端的端口釋放出來,可以繼續(xù)等待其它的客戶端連接。 實現(xiàn)獲得連接的代碼是:

Socket socket = ss.accept();

該代碼實現(xiàn)的功能是獲得當前連接到服務(wù)器端的客戶端連接。需要說明的是accept和前面IO部分介紹的read方法一樣,都是一個阻塞方法,也就是當無連接時,該方法將阻塞程序的執(zhí)行,直到連接到達時才執(zhí)行該行代碼。另外獲得的連接會在服務(wù)器端的該端口注冊,這樣以后就可以通過在服務(wù)器端的注冊信息直接通信,而注冊以后服務(wù)器端的端口就被釋放出來,又可以繼續(xù)接受其它的連接了。
   連接獲得以后,后續(xù)的編程就和客戶端的網(wǎng)絡(luò)編程類似了,這里獲得的Socket類型的連接就和客戶端的網(wǎng)絡(luò)連接一樣了,只是服務(wù)器端需要首先讀取發(fā)送過來的數(shù)據(jù),然后進行邏輯處理以后再發(fā)送給客戶端,也就是交換數(shù)據(jù)的順序和客戶端交換數(shù)據(jù)的步驟剛好相反。這部分的內(nèi)容和客戶端很類似,所以就不重復了,如果還不熟悉,可以參看下面的示例代碼。
  最后,在服務(wù)器端通信完成以后,關(guān)閉服務(wù)器端連接。實現(xiàn)的代碼為:ss.close();
  這就是基本的TCP類型的服務(wù)器端編程步驟。下面以一個簡單的echo服務(wù)實現(xiàn)為例子,介紹綜合使用示例。echo的意思就是“回聲”,echo服務(wù)器端實現(xiàn)的功能就是將客戶端發(fā)送的內(nèi)容再原封不動的反饋給客戶端。實現(xiàn)的代碼如下:

package tcp;

import java.io.*;
import java.net.*;
/**
 * echo服務(wù)器
 * 功能:將客戶端發(fā)送的內(nèi)容反饋給客戶端
 */
public class SimpleSocketServer {
         public static void main(String[] args) {
                   ServerSocket serverSocket = null;
                   Socket socket = null;
                   OutputStream os = null;
                   InputStream is = null;
                   //監(jiān)聽端口號
                   int port = 10000;
                   try {
                            //建立連接
                            serverSocket = new ServerSocket(port);
                            //獲得連接
                            socket = serverSocket.accept();
                            //接收客戶端發(fā)送內(nèi)容
                            is = socket.getInputStream();
                            byte[] b = new byte[1024];
                            int n = is.read(b);
                            //輸出
                            System.out.println("客戶端發(fā)送內(nèi)容為:" + new String(b,0,n));
                            //向客戶端發(fā)送反饋內(nèi)容
                            os = socket.getOutputStream();
                            os.write(b, 0, n);
                   } catch (Exception e) {
                            e.printStackTrace();
                   }finally{
                            try{
                                     //關(guān)閉流和連接
                                     os.close();
                                     is.close();
                                     socket.close();
                                     serverSocket.close();
                            }catch(Exception e){}
                   }
         }
}

在該示例代碼中建立了一個監(jiān)聽當前計算機10000號端口的服務(wù)器端Socket連接,然后獲得客戶端發(fā)送過來的連接,如果有連接到達時,讀取連接中發(fā)送過來的內(nèi)容,并將發(fā)送的內(nèi)容在控制臺進行輸出,輸出完成以后將客戶端發(fā)送的內(nèi)容再反饋給客戶端。最后關(guān)閉流和連接對象,結(jié)束程序。
  在控制臺下面編譯和運行該程序的命令和客戶端部分的類似。
  這樣,就以一個很簡單的示例演示了TCP類型的網(wǎng)絡(luò)編程在Java語言中的基本實現(xiàn),這個示例只是演示了網(wǎng)絡(luò)編程的基本步驟以及各個功能方法的基本使用,只是為網(wǎng)絡(luò)編程打下了一個基礎(chǔ),下面將就幾個問題來深入介紹網(wǎng)絡(luò)編程深層次的一些知識。
  為了一步一步的掌握網(wǎng)絡(luò)編程,下面再研究網(wǎng)絡(luò)編程中的兩個基本問題,通過解決這兩個問題將對網(wǎng)絡(luò)編程的認識深入一層。
  1、如何復用Socket連接?
  在前面的示例中,客戶端中建立了一次連接,只發(fā)送一次數(shù)據(jù)就關(guān)閉了,這就相當于撥打電話時,電話打通了只對話一次就關(guān)閉了,其實更加常用的應(yīng)該是撥通一次電話以后多次對話,這就是復用客戶端連接。
  那么如何實現(xiàn)建立一次連接,進行多次數(shù)據(jù)交換呢?其實很簡單,建立連接以后,將數(shù)據(jù)交換的邏輯寫到一個循環(huán)中就可以了。這樣只要循環(huán)不結(jié)束則連接就不會被關(guān) 閉。按照這種思路,可以改造一下上面的代碼,讓該程序可以在建立連接一次以后,發(fā)送三次數(shù)據(jù),當然這里的次數(shù)也可以是多次,示例代碼如下:

package tcp;
import java.io.*;
import java.net.*;
/**
 * 復用連接的Socket客戶端
 * 功能為:發(fā)送字符串“Hello”到服務(wù)器端,并打印出服務(wù)器端的反饋
 */
public class MulSocketClient {
         public static void main(String[] args) {
                   Socket socket = null;
                   InputStream is = null;
                   OutputStream os = null;
                   //服務(wù)器端IP地址
                   String serverIP = "127.0.0.1";
                   //服務(wù)器端端口號
                   int port = 10000;
                   //發(fā)送內(nèi)容
                   String data[] ={"First","Second","Third"};
                   try {
                            //建立連接
                            socket = new Socket(serverIP,port);
                            //初始化流
                            os = socket.getOutputStream();
                            is = socket.getInputStream();
                            byte[] b = new byte[1024];
                            for(int i = 0;i < data.length;i++){
                                     //發(fā)送數(shù)據(jù)
                                     os.write(data[i].getBytes());
                                     //接收數(shù)據(jù)
                                     int n = is.read(b);
                                     //輸出反饋數(shù)據(jù)
                                     System.out.println("服務(wù)器反饋:" + new String(b,0,n));
                            }
                   } catch (Exception e) {
                            e.printStackTrace(); //打印異常信息
                   }finally{
                            try {
                                     //關(guān)閉流和連接
                                     is.close();
                                     os.close();
                                     socket.close();
                            } catch (Exception e2) {}
                   }
         }
}

該示例程序和前面的代碼相比,將數(shù)據(jù)交換部分的邏輯寫在一個for循環(huán)的內(nèi)容,這樣就可以建立一次連接,依次將data數(shù)組中的數(shù)據(jù)按照順序發(fā)送給服務(wù)器端了。
  如果還是使用前面示例代碼中的服務(wù)器端程序運行該程序,則該程序的結(jié)果是:

java.net.SocketException: Software caused connection abort: recv failed

                                 at java.net.SocketInputStream.socketRead0(Native Method)

                                 at java.net.SocketInputStream.read(SocketInputStream.java:129)

                                 at java.net.SocketInputStream.read(SocketInputStream.java:90)

                                 at tcp.MulSocketClient.main(MulSocketClient.java:30)

服務(wù)器反饋:First

顯然,客戶端在實際運行時出現(xiàn)了異常,出現(xiàn)異常的原因是什么呢?如果仔細閱讀前面的代碼,應(yīng)該還記得前面示例代碼中的服務(wù)器端是對話一次數(shù)據(jù)以后就關(guān)閉了連接,如果服務(wù)器端程序關(guān)閉了,客戶端繼續(xù)發(fā)送數(shù)據(jù)肯定會出現(xiàn)異常,這就是出現(xiàn)該問題的原因。
  按照客戶端實現(xiàn)的邏輯,也可以復用服務(wù)器端的連接,實現(xiàn)的原理也是將服務(wù)器端的數(shù)據(jù)交換邏輯寫在循環(huán)中即可,按照該種思路改造以后的服務(wù)器端代碼為:

package tcp;
import java.io.*;
import java.net.*;
/**
 * 復用連接的echo服務(wù)器
 * 功能:將客戶端發(fā)送的內(nèi)容反饋給客戶端
 */
public class MulSocketServer {
         public static void main(String[] args) {
                   ServerSocket serverSocket = null;
                   Socket socket = null;
                   OutputStream os = null;
                   InputStream is = null;
                   //監(jiān)聽端口號
                   int port = 10000;
                   try {
                            //建立連接
                            serverSocket = new ServerSocket(port);
                            System.out.println("服務(wù)器已啟動:");
                            //獲得連接
                            socket = serverSocket.accept();
                            //初始化流
                            is = socket.getInputStream();
                            os = socket.getOutputStream();
                            byte[] b = new byte[1024];
                            for(int i = 0;i < 3;i++){
                                     int n = is.read(b);
                                     //輸出
                                     System.out.println("客戶端發(fā)送內(nèi)容為:" + new String(b,0,n));
                                     //向客戶端發(fā)送反饋內(nèi)容
                                     os.write(b, 0, n);
                            }
                   } catch (Exception e) {
                            e.printStackTrace();
                   }finally{
                            try{
                                     //關(guān)閉流和連接
                                     os.close();
                                     is.close();
                                     socket.close();
                                     serverSocket.close();
                            }catch(Exception e){}
                   }
         }
}

在該示例代碼中,也將數(shù)據(jù)發(fā)送和接收的邏輯寫在了一個for循環(huán)內(nèi)部,只是在實現(xiàn)時硬性的將循環(huán)次數(shù)規(guī)定成了3次,這樣代碼雖然比較簡單,但是通用性比較差。
  以該服務(wù)器端代碼實現(xiàn)為基礎(chǔ)運行前面的客戶端程序時,客戶端的輸出為:
服務(wù)器反饋:First
服務(wù)器反饋:Second
服務(wù)器反饋:Third
服務(wù)器端程序的輸出結(jié)果為:
服務(wù)器已啟動:
客戶端發(fā)送內(nèi)容為:First
客戶端發(fā)送內(nèi)容為:Second
客戶端發(fā)送內(nèi)容為:Third
  在該程序中,比較明顯的體現(xiàn)出了“請求-響應(yīng)”模型,也就是在客戶端發(fā)起連接以后,首先發(fā)送字符串“First”給服務(wù)器端,服務(wù)器端輸出客戶端發(fā)送的內(nèi)容“First”,然后將客戶端發(fā)送的內(nèi)容再反饋給客戶端,這樣客戶端也輸出服務(wù)器反饋“First”,這樣就完成了客戶端和服務(wù)器端的一次對話,緊接著客戶端發(fā)送“Second”給服務(wù)器端,服務(wù)端輸出“Second”,然后將“Second”再反饋給客戶端,客戶端再輸出“Second”,從而完成第二次會話,第三次會話的過程和這個一樣。在這個過程中,每次都是客戶端程序首先發(fā)送數(shù)據(jù)給服務(wù)器端,服務(wù)器接收數(shù)據(jù)以后,將結(jié)果反饋給客戶端,客戶端接收到服務(wù)器端的反饋,從而完成一次通訊過程。
在該示例中,雖然解決了多次發(fā)送的問題,但是客戶端和服務(wù)器端的次數(shù)控制還不夠靈活,如果客戶端的次數(shù)不固定怎么辦呢?是否可以使用某個特殊的字符串,例如quit,表示客戶端退出呢,這就涉及到網(wǎng)絡(luò)協(xié)議的內(nèi)容了,會在后續(xù)的網(wǎng)絡(luò)應(yīng)用示例部分詳細介紹。下面開始介紹另外一個網(wǎng)絡(luò)編程的突出問題。
2、如何使服務(wù)器端支持多個客戶端同時工作?
前面介紹的服務(wù)器端程序,只是實現(xiàn)了概念上的服務(wù)器端,離實際的服務(wù)器端程序結(jié)構(gòu)距離還很遙遠,如果需要讓服務(wù)器端能夠?qū)嶋H使用,那么最需要解決的問題就是——如何支持多個客戶端同時工作。
一個服務(wù)器端一般都需要同時為多個客戶端提供通訊,如果需要同時支持多個客戶端,則必須使用前面介紹的線程的概念。簡單來說,也就是當服務(wù)器端接收到一個連接時,啟動一個專門的線程處理和該客戶端的通訊。
按照這個思路改寫的服務(wù)端示例程序?qū)⒂蓛蓚€部分組成,MulThreadSocketServer類實現(xiàn)服務(wù)器端控制,實現(xiàn)接收客戶端連接,然后開啟專門的邏輯線程處理該連接,LogicThread類實現(xiàn)對于一個客戶端連接的邏輯處理,將處理的邏輯放置在該類的run方法中。該示例的代碼實現(xiàn)為:

package tcp;

import java.net.ServerSocket;
import java.net.Socket;
/**
 * 支持多客戶端的服務(wù)器端實現(xiàn)
 */
public class MulThreadSocketServer {
         public static void main(String[] args) {
                   ServerSocket serverSocket = null;
                   Socket socket = null;
                   //監(jiān)聽端口號
                   int port = 10000;
                   try {
                            //建立連接
                            serverSocket = new ServerSocket(port);
                            System.out.println("服務(wù)器已啟動:");
                            while(true){
                                     //獲得連接
                                     socket = serverSocket.accept();
                                     //啟動線程
                                     new LogicThread(socket);
                            }
                   } catch (Exception e) {
                            e.printStackTrace();
                   }finally{
                            try{
                                     //關(guān)閉連接
                                     serverSocket.close();
                            }catch(Exception e){}
                   }
         }
}

在該示例代碼中,實現(xiàn)了一個while形式的死循環(huán),由于accept方法是阻塞方法,所以當客戶端連接未到達時,將阻塞該程序的執(zhí)行,當客戶端到達時接收該連接,并啟動一個新的LogicThread線程處理該連接,然后按照循環(huán)的執(zhí)行流程,繼續(xù)等待下一個客戶端連接。這樣當任何一個客戶端連接到達時,都開啟一個專門的線程處理,通過多個線程支持多個客戶端同時處理。
  下面再看一下LogicThread線程類的源代碼實現(xiàn):

package tcp;

import java.io.*;
import java.net.*;
/**
 * 服務(wù)器端邏輯線程
 */
public class LogicThread extends Thread {
         Socket socket;
         InputStream is;
         OutputStream os;
         public LogicThread(Socket socket){
                   this.socket = socket;
                   start(); //啟動線程
         }

         public void run(){
                   byte[] b = new byte[1024];
                   try{
                            //初始化流
                            os = socket.getOutputStream();
                            is = socket.getInputStream();
                            for(int i = 0;i < 3;i++){
                                     //讀取數(shù)據(jù)
                                     int n = is.read(b);
                                     //邏輯處理
                                     byte[] response = logic(b,0,n);
                                     //反饋數(shù)據(jù)
                                     os.write(response);
                            }
                   }catch(Exception e){
                            e.printStackTrace();
                   }finally{
                            close();
                   }
         }

         /**
          * 關(guān)閉流和連接
          */
         private void close(){
                   try{
                            //關(guān)閉流和連接
                            os.close();
                            is.close();
                            socket.close();
                   }catch(Exception e){}
         }

         /**
          * 邏輯處理方法,實現(xiàn)echo邏輯
          * @param b 客戶端發(fā)送數(shù)據(jù)緩沖區(qū)
          * @param off 起始下標
          * @param len 有效數(shù)據(jù)長度
          * @return
          */
         private byte[] logic(byte[] b,int off,int len){
                   byte[] response = new byte[len];
                   //將有效數(shù)據(jù)拷貝到數(shù)組response中
                   System.arraycopy(b, 0, response, 0, len);
                   return response;
         }
}

在該示例代碼中,每次使用一個連接對象構(gòu)造該線程,該連接對象就是該線程需要處理的連接,在線程構(gòu)造完成以后,該線程就被啟動起來了,然后在run方法內(nèi)部對客戶端連接進行處理,數(shù)據(jù)交換的邏輯和前面的示例代碼一致,只是這里將接收到客戶端發(fā)送過來的數(shù)據(jù)并進行處理的邏輯封裝成了logic方法,按照前面介紹的IO編程的內(nèi)容,客戶端發(fā)送過來的內(nèi)容存儲在數(shù)組b的起始下標為0,長度為n個中,這些數(shù)據(jù)是客戶端發(fā)送過來的有效數(shù)據(jù),將有效的數(shù)據(jù)傳遞給logic方法,logic方法實現(xiàn)的是echo服務(wù)的邏輯,也就是將客戶端發(fā)送的有效數(shù)據(jù)形成以后新的response數(shù)組,并作為返回值反饋。
在線程中將logic方法的返回值反饋給客戶端,這樣就完成了服務(wù)器端的邏輯處理模擬,其他的實現(xiàn)和前面的介紹類似,這里就不在重復了。
這里的示例還只是基礎(chǔ)的服務(wù)器端實現(xiàn),在實際的服務(wù)器端實現(xiàn)中,由于硬件和端口數(shù)的限制,所以不能無限制的創(chuàng)建線程對象,而且頻繁的創(chuàng)建線程對象效率也比較低,所以程序中都實現(xiàn)了線程池來提高程序的執(zhí)行效率。
這里簡單介紹一下線程池的概念,線程池(Thread pool)是池技術(shù)的一種,就是在程序啟動時首先把需要個數(shù)的線程對象創(chuàng)建好,例如創(chuàng)建5000個線程對象,然后當客戶端連接到達時從池中取出一個已經(jīng)創(chuàng)建完成的線程對象使用即可。當客戶端連接關(guān)閉以后,將該線程對象重新放入到線程池中供其它的客戶端重復使用,這樣可以提高程序的執(zhí)行速度,優(yōu)化程序?qū)τ趦?nèi)存的占用等。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,406評論 6 538
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 99,034評論 3 423
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 177,413評論 0 382
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,449評論 1 316
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,165評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,559評論 1 325
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,606評論 3 444
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,781評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,327評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 41,084評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,278評論 1 371
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,849評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,495評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,927評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,172評論 1 291
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,010評論 3 396
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,241評論 2 375

推薦閱讀更多精彩內(nèi)容

  • 網(wǎng)絡(luò)編程 網(wǎng)絡(luò)編程對于很多的初學者來說,都是很向往的一種編程技能,但是很多的初學者卻因為很長一段時間無法進入網(wǎng)絡(luò)編...
    程序員歐陽閱讀 2,039評論 1 37
  • 計算機網(wǎng)絡(luò)概述 網(wǎng)絡(luò)編程的實質(zhì)就是兩個(或多個)設(shè)備(例如計算機)之間的數(shù)據(jù)傳輸。 按照計算機網(wǎng)絡(luò)的定義,通過一定...
    蛋炒飯_By閱讀 1,238評論 0 10
  • Socket編程 1基礎(chǔ)知識 協(xié)議 端口號(辨別不同應(yīng)用) TCP/IP協(xié)議 是目前世界上應(yīng)用最廣泛的協(xié)議是以TC...
    __豆約翰__閱讀 1,101評論 0 3
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 134,807評論 18 139
  • 姓名:張亞妮 公司:慈星股份 【知~學習】 《六項精進》大綱背誦1遍 共44遍 《六項精進》通篇誦讀1遍 共...
    宇嘟嘟閱讀 125評論 0 0