《Java網(wǎng)絡(luò)程序設(shè)計(jì)》課件-第5章_第1頁(yè)
《Java網(wǎng)絡(luò)程序設(shè)計(jì)》課件-第5章_第2頁(yè)
《Java網(wǎng)絡(luò)程序設(shè)計(jì)》課件-第5章_第3頁(yè)
《Java網(wǎng)絡(luò)程序設(shè)計(jì)》課件-第5章_第4頁(yè)
《Java網(wǎng)絡(luò)程序設(shè)計(jì)》課件-第5章_第5頁(yè)
已閱讀5頁(yè),還剩71頁(yè)未讀, 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(quán)說(shuō)明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)

文檔簡(jiǎn)介

第5章TCPSocket

5.1套接字5.2TCPSocket5.3多線程操作

5.1套接字

5.1.1端口的概念

在TCP/IP體系結(jié)構(gòu)中,傳輸層為應(yīng)用層提供傳輸服務(wù),可選擇TCP和UDP兩個(gè)傳輸協(xié)議充當(dāng)數(shù)據(jù)的承載協(xié)議。應(yīng)用層通過(guò)傳輸層進(jìn)行數(shù)據(jù)通信時(shí),TCP和UDP會(huì)遇到同時(shí)為多個(gè)應(yīng)用程序進(jìn)程提供并發(fā)服務(wù)的問(wèn)題。在TCP/IP協(xié)議中為應(yīng)用層和傳輸層之間提出了端口(port)的概念,用于標(biāo)識(shí)網(wǎng)絡(luò)主機(jī)上的唯一通信進(jìn)程。

端口指網(wǎng)絡(luò)體系結(jié)構(gòu)中應(yīng)用層與傳輸層之間的通信協(xié)議接口,也稱(chēng)為傳輸層服務(wù)訪問(wèn)接口。端口是一種抽象的軟件結(jié)構(gòu),包括一些數(shù)據(jù)結(jié)構(gòu)和I/O緩沖區(qū)。應(yīng)用進(jìn)程通過(guò)系統(tǒng)調(diào)用與某端口建立關(guān)聯(lián)(binding)后,傳輸層傳給該端口的數(shù)據(jù)都被相應(yīng)的應(yīng)用進(jìn)程所接收。TCP與UDP的端口如圖5-1所示。圖5-1TCP與UDP的端口(a)數(shù)據(jù)發(fā)送;(b)數(shù)據(jù)接收端口號(hào)使用16位表示,取值范圍為0~65?535,含義是一臺(tái)使用TCP/IP體系結(jié)構(gòu)的計(jì)算機(jī)上最多在應(yīng)用層上區(qū)別65?336個(gè)TCP網(wǎng)絡(luò)進(jìn)程和65?336個(gè)UDP網(wǎng)絡(luò)進(jìn)程,實(shí)際上不能達(dá)到這樣多的進(jìn)程數(shù)量。所有的端口僅具有本地意義,即端口僅能設(shè)置本地的網(wǎng)絡(luò)通信進(jìn)程,而不能設(shè)置通信對(duì)方的應(yīng)用進(jìn)程端口號(hào)。表5-1列出了部分基于TCP的常用應(yīng)用進(jìn)程的端口號(hào)。習(xí)慣上,端口號(hào)可分為三大類(lèi):

(1)公認(rèn)端口(WellKnownPorts):從0~1023,它們緊密綁定于一些服務(wù)。通常這些端口的通信明確表明了某種服務(wù)的協(xié)議。例如:80端口實(shí)際上總是綁定HTTP通信。

(2)注冊(cè)端口(RegisteredPorts):從1024~49?151,它們松散地綁定于一些服務(wù)。也就是說(shuō)有許多服務(wù)綁定于這些端口,這些端口同樣用于許多其他目的。例如:許多系統(tǒng)處理動(dòng)態(tài)端口從1024左右開(kāi)始。

(3)動(dòng)態(tài)或私有端口(DynamicandorPrivatePorts):從49?152~65?535。理論上,從1024起為服務(wù)分配動(dòng)態(tài)端口,但也有例外,如Sun的RPC端口從32?768開(kāi)始。5.1.2套接字的概念

套接字(Socket)原意是“插座”,在計(jì)算機(jī)網(wǎng)絡(luò)中指支持TCP/IP的網(wǎng)絡(luò)通信的基本操作單元,可以被看做是兩個(gè)進(jìn)程在通信連接中的一個(gè)端點(diǎn),是連接應(yīng)用程序和網(wǎng)絡(luò)驅(qū)動(dòng)程序的橋梁。Socket在應(yīng)用進(jìn)程中創(chuàng)建,通過(guò)綁定與網(wǎng)絡(luò)驅(qū)動(dòng)建立關(guān)系。此后,應(yīng)用進(jìn)程送給Socket的數(shù)據(jù),由Socket傳遞給網(wǎng)絡(luò)驅(qū)動(dòng)程序向網(wǎng)絡(luò)上發(fā)送出去。計(jì)算機(jī)從網(wǎng)絡(luò)上收到與該Socket綁定IP地址和端口號(hào)相關(guān)的數(shù)據(jù)后,由網(wǎng)絡(luò)驅(qū)動(dòng)進(jìn)程交給Socket,應(yīng)用進(jìn)程便可從該Socket中提取接收到的數(shù)據(jù),即網(wǎng)絡(luò)應(yīng)用進(jìn)程通過(guò)Socket實(shí)現(xiàn)數(shù)據(jù)的發(fā)送與接收。在同一臺(tái)主機(jī)上可能會(huì)同時(shí)運(yùn)行多個(gè)應(yīng)用進(jìn)程,為了區(qū)分不同應(yīng)用進(jìn)程的網(wǎng)絡(luò)通信和連接,Socket通過(guò)三個(gè)參數(shù)唯一地標(biāo)識(shí)本地主機(jī)上的通信進(jìn)程,實(shí)現(xiàn)與網(wǎng)絡(luò)進(jìn)程的綁定關(guān)系,這三個(gè)參數(shù)分別是:通信的目的IP地址、使用的傳輸層協(xié)議(TCP或UDP)以及使用的端口號(hào)。此時(shí),應(yīng)用層和傳輸層就可以通過(guò)Socket,區(qū)分來(lái)自不同應(yīng)用程序進(jìn)程或網(wǎng)絡(luò)連接的通信,實(shí)現(xiàn)數(shù)據(jù)傳輸?shù)牟l(fā)服務(wù)。

以最常見(jiàn)的WWW訪問(wèn)為例,例如:在瀏覽器地址中輸入了網(wǎng)頁(yè)URL地址:

/index.html該URL就包含了套接字的三個(gè)參數(shù),由于采用超文本傳輸協(xié)議(HyperTextTransferProtocol,HTTP)說(shuō)明使用的是TCP協(xié)議;根據(jù)目標(biāo)網(wǎng)頁(yè)的域名“”,可以通過(guò)域名服務(wù)器查找到該WWW服務(wù)器的IP地址為;通常在訪問(wèn)URL時(shí),如端口無(wú)特殊指定時(shí),默認(rèn)使用80;index.html說(shuō)明訪問(wèn)的文件資源。所以,訪問(wèn)該網(wǎng)頁(yè)時(shí)的套接字為{TCP,,80}。5.1.3Netstat的應(yīng)用

由于在TCP/IP體系結(jié)構(gòu)中通過(guò)端口唯一標(biāo)識(shí)主機(jī)上的應(yīng)用通信進(jìn)程,因而可以通過(guò)查看本地端口信息掌握本地主機(jī)的網(wǎng)絡(luò)進(jìn)程運(yùn)行情況;即:用戶可以知道本地計(jì)算機(jī)開(kāi)放了哪些通信端口,都是什么軟件進(jìn)程開(kāi)的;可以掌握開(kāi)放的端口處于什么狀態(tài),是等待連接還是已經(jīng)連接,如果是已經(jīng)連接,需要特別注意看連接是個(gè)正常連接還是非正常連接,例如木馬等;可以知道目前本機(jī)是不是正在和其他計(jì)算機(jī)交換數(shù)據(jù),是正常的程序訪問(wèn)到一個(gè)正常網(wǎng)站還是訪問(wèn)到一個(gè)陷阱,更重要的是因?yàn)槎丝谥荒芪ㄒ坏貥?biāo)識(shí)通信進(jìn)程,通過(guò)查看本地的端口使用情況,可以避免產(chǎn)生端口使用沖突的問(wèn)題。在Windows操作系統(tǒng)中,提供了工具軟件Netstat.exe。它可以用于顯示與IP,TCP,UDP和ICMP等協(xié)議相關(guān)的統(tǒng)計(jì)數(shù)據(jù),通常被用于檢驗(yàn)本機(jī)各端口的網(wǎng)絡(luò)連接情況。在這里列舉Netstat常用的參數(shù)?-a和?-n,如:

●?Netstat-a 本選項(xiàng)顯示一個(gè)有效連接信息列表,包括已建立的TCP連接(ESTABLISHED),也包括TCP監(jiān)聽(tīng)連接請(qǐng)求(LISTENING)的連接。

●?Netstat-n 顯示所有已建立的有效連接,包括基于TCP和UDP的應(yīng)用進(jìn)程。

通常,將?-a和?-n聯(lián)合執(zhí)行,即Netstat-an,即可列出本地計(jì)算機(jī)所有的占用端口情況。所有的端口必然處于下列狀態(tài)之一:●?LISTENING狀態(tài),表示某個(gè)端口是個(gè)開(kāi)放的TCP端口,正處于監(jiān)聽(tīng)狀態(tài),等待遠(yuǎn)程用戶的連接。意味著這是一個(gè)使用TCP的協(xié)議服務(wù)器端程序,用于監(jiān)聽(tīng)客戶機(jī)端的連接。例如:

TCP:21:0LISTENING

從上例可以看出,本地開(kāi)啟端口21,通常為FTP服務(wù)進(jìn)程。因?yàn)镮P地址顯示均為,表示該端口還處于監(jiān)聽(tīng)狀態(tài),未建立連接。

●?ESTABLISHED狀態(tài),表示已建立通信連接,兩臺(tái)機(jī)器正在交換數(shù)據(jù),例如:

TCP0:21:3009ESTABLISHED說(shuō)明主機(jī)0上開(kāi)啟了FTP服務(wù),與主機(jī)上端口為3009的進(jìn)程建立了連接。當(dāng)主機(jī)53使用瀏覽器訪問(wèn)某網(wǎng)站時(shí),發(fā)現(xiàn)會(huì)有許多源IP地址和目的IP地址相同,并與處于ESTABLISHED狀態(tài)的套接字連接。這由網(wǎng)頁(yè)的構(gòu)成形式?jīng)Q定,網(wǎng)頁(yè)本身是純文本格式,它通過(guò)嵌入的方法鏈接引入各種格式的數(shù)據(jù),所以如果要查看完整的網(wǎng)頁(yè),就需要將嵌入的所有數(shù)據(jù)都下載,所以圖片、flash動(dòng)畫(huà)等都要單獨(dú)建立連接,如圖5-2所示。圖5-2當(dāng)訪問(wèn)某網(wǎng)頁(yè)時(shí)建立的連接

5.2TCPSocket

由于TCP是面向連接的傳輸控制協(xié)議,因而在Java的包中,為實(shí)現(xiàn)TCPSocket提供了兩個(gè)基礎(chǔ)類(lèi),分別是Socket類(lèi)和ServerSocket類(lèi)。

●?Socket類(lèi):用于建立一個(gè)客戶機(jī)端通信對(duì)象,并向服務(wù)器端發(fā)起連接請(qǐng)求,在TCP連接成功后,通信雙方利用自有的Socket實(shí)例實(shí)現(xiàn)會(huì)話;

●?ServerSocket類(lèi):建立一個(gè)服務(wù)器端對(duì)象,專(zhuān)門(mén)用于監(jiān)聽(tīng)客戶機(jī)端的連接請(qǐng)求,其通過(guò)accept()方法實(shí)現(xiàn)與客戶端的連接,完成TCP連接的三次握手。5.2.1Socket類(lèi)

Socket類(lèi)是TCPSocket中重要的類(lèi)。該類(lèi)有兩個(gè)作用:其一是作為客戶機(jī)端向指定的服務(wù)器發(fā)起連接請(qǐng)求,其二是在服務(wù)器端生成一個(gè)與客戶機(jī)端對(duì)等的通信實(shí)體,實(shí)現(xiàn)一對(duì)一的通信功能。其類(lèi)定義如圖5-3所示。圖5-3Socket類(lèi)的定義其常用構(gòu)造方法:

●?protectedSocket()throwsIOException,建立一個(gè)默認(rèn)的TCP客戶端套接字;

●?publicSocket(InetAddressaddr,intport)throwsIOException,向指定InetAddress的目標(biāo)服務(wù)器的端口port發(fā)起連接請(qǐng)求;

●?publicSocket(Stringhost,intport)throwsIOException,向指定名稱(chēng)的目標(biāo)服務(wù)器的端口port發(fā)起連接請(qǐng)求。

Socket構(gòu)造方法的含義是,向某指定主機(jī)的指定端口發(fā)出連接請(qǐng)求,例如:

Socketsc1=newSocket("",80);//向主機(jī)上的標(biāo)識(shí)為80的服務(wù)進(jìn)程發(fā)起連接請(qǐng)求。

Socketsc2=newSocket(“”,80);//向主機(jī)上的標(biāo)識(shí)為80的服務(wù)進(jìn)程發(fā)起連接請(qǐng)求。

Socketsc3=newSocket(InetAddress.getByName(“”),80);參數(shù)使用了InetAddress對(duì)象,向目標(biāo)的80端口發(fā)起連接請(qǐng)求。

實(shí)際上,上面三個(gè)連接請(qǐng)求都是實(shí)現(xiàn)了向相同的Web服務(wù)器發(fā)起連接請(qǐng)求的功能,只不過(guò)是參數(shù)的形式不同而已。

Socket類(lèi)常用的方法有:

●?publicvoidclose(),關(guān)閉當(dāng)前套接字;

●?intgetPort(),獲得對(duì)方Socket端口號(hào);

●?intgetLocalPort(),獲得本地Socket端口號(hào);

●?InetAddressgetInetAddress(),獲得對(duì)方InetAddress對(duì)象;

●?InetAddressgetLocalAddress(),獲得本地InetAddress對(duì)象;

●?InputStreamgetInputStream(), 獲得輸入流對(duì)象;

●?OutputStreamgetOutputStream(),獲得輸出流對(duì)象。代碼注釋如下:

①第1~2行在網(wǎng)絡(luò)編程時(shí)都需要引用java.io和類(lèi)庫(kù)包;

②第5~6行設(shè)定要訪問(wèn)的主機(jī)名稱(chēng)為,端口為80;

③第7行聲明客戶端套接字變量cs;

④第9行向預(yù)先設(shè)定好的主機(jī)上的端口發(fā)起連接請(qǐng)求,如果連接成功的話則對(duì)客戶端套接字cs進(jìn)行賦值;

⑤第11~12行獲得本次連接套接字信息。

運(yùn)行結(jié)果如圖5-4所示。圖5-4Socket常用方法演示代碼注釋如下:

①第6行設(shè)定目標(biāo)計(jì)算機(jī)名稱(chēng);

②第7行設(shè)定端口掃描范圍,應(yīng)在0~65?535之間。

③第8~16行嘗試連接指定主機(jī)的指定端口,并給出提示信息。

由于在測(cè)試遠(yuǎn)程主機(jī)端口時(shí),等待應(yīng)答的時(shí)間可能會(huì)比較長(zhǎng),可以縮小探測(cè)端口的范圍。也可以將hostName設(shè)置為localhost或臨近的主機(jī)。運(yùn)行結(jié)果如圖5-5所示。

當(dāng)利用Socket實(shí)現(xiàn)通信時(shí),在實(shí)現(xiàn)與服務(wù)器連接后,客戶機(jī)端套接字上分別獲得輸入與輸出流,并且通過(guò)相應(yīng)方法從網(wǎng)絡(luò)獲取數(shù)據(jù)和向網(wǎng)絡(luò)發(fā)送數(shù)據(jù)。圖5-5測(cè)試主機(jī)的端口開(kāi)放情況

代碼注釋如下:

①第6~7行分別聲明了輸入和輸出流對(duì)象;

②第9行向指定服務(wù)器localhost的80端口發(fā)起連接請(qǐng)求,如果連接成功則給客戶端套接字cs復(fù)制;

③第10~11行在套接字cs上通過(guò)getInputStream()和getOutputStream()方法分別獲得輸入和輸出流;

④第19~21行要求客戶端通過(guò)鍵盤(pán)輸入一個(gè)用戶名;

⑤第22~28行實(shí)現(xiàn)客戶端與服務(wù)器的通信;

⑥第22行定義了用于接收和發(fā)送數(shù)據(jù)的字符串變量fromServer和fromUser;

⑦第23行通過(guò)in.readUTF()從網(wǎng)絡(luò)接收數(shù)據(jù);

⑧第25行判斷收到的字符是否為“bye”,如果是則退出循環(huán)結(jié)束程序;⑨第27行從本地鍵盤(pán)輸入信息;

⑩第28行通過(guò)os.writeUTF()實(shí)現(xiàn)向網(wǎng)絡(luò)發(fā)送數(shù)據(jù);

??第29行通過(guò)調(diào)用os.flush()將存儲(chǔ)在本地發(fā)送緩存的數(shù)據(jù)立即向網(wǎng)絡(luò)發(fā)送,保證消息的實(shí)時(shí)性;

??第31~34行關(guān)閉流和套接字,釋放資源。

該代碼與例5-4配合執(zhí)行,運(yùn)行結(jié)果如圖5-6所示。圖5-6客戶機(jī)端顯示5.2.2ServerSocket類(lèi)

ServerSocket類(lèi)用于在TCP傳輸?shù)姆?wù)器端建立一個(gè)監(jiān)聽(tīng)端口,監(jiān)聽(tīng)本地服務(wù)器是否接收到客戶機(jī)端的連接請(qǐng)求。當(dāng)接收到客戶機(jī)端連接請(qǐng)求,采用accept()方法確認(rèn)連接,并在本地返回一個(gè)Socket對(duì)象,利用該Socket對(duì)象與客戶機(jī)端Socket實(shí)現(xiàn)建立通信連接。其類(lèi)定義如圖5-7所示。圖5-7ServerSocket類(lèi)的定義其常用構(gòu)造方法:

●?ServerSocket()throwsIOException,建立一個(gè)服務(wù)器端標(biāo)識(shí);

●?ServerSocket(intport)throwsIOException,在指定的端口建立一個(gè)服務(wù)器端標(biāo)識(shí)。

其端口取值范圍在0~65535之間,當(dāng)取值為0,表示使用本地任何空閑端口建立監(jiān)聽(tīng),監(jiān)聽(tīng)本地某個(gè)指定的端口,例如:

ServerSocketss0=newServerSocket(80);//監(jiān)聽(tīng)TCP80端口是否有連接請(qǐng)求

ServerSocketss1=newServerSocket(0);//監(jiān)聽(tīng)某一空閑端口是否有連接請(qǐng)求

ServerSocket常用的方法有:

●?publicSocketaccept(),服務(wù)器接收客戶機(jī)端連接請(qǐng)求;

●?intgetLocalPort(),當(dāng)使用ServerSocket(0)構(gòu)造對(duì)象時(shí),可獲得自動(dòng)分配得到的監(jiān)聽(tīng)端口號(hào);●?publicvoidclose(),停止監(jiān)聽(tīng)指定端口,關(guān)閉ServerSocket套接字。

由于TCP是面向連接的協(xié)議,提供可靠的通信服務(wù),因而經(jīng)常被用于文件傳輸?shù)扔锌煽啃砸蟮膱?chǎng)景中。當(dāng)使用Java進(jìn)行TCP通信編程時(shí),首先由服務(wù)器端利用ServerSocket開(kāi)啟服務(wù)端口等待客戶機(jī)端的連接請(qǐng)求。其次,由客戶機(jī)端的Socket發(fā)起連接請(qǐng)求,服務(wù)器端使用accept()方法接收客戶機(jī)端,生成與客戶機(jī)端對(duì)應(yīng)的Socket,完成TCP連接建立。接下來(lái)服務(wù)器和客戶機(jī)端分別獲得對(duì)應(yīng)的輸入/輸出流,即可進(jìn)行通信。最后,通信結(jié)束分別關(guān)閉兩端的數(shù)據(jù)流和套接字,釋放系統(tǒng)資源。

圖5-8所示為T(mén)CPSocket通信的流程。圖5-8TCPSocket通信的流程

代碼注釋如下:

①第5~12行啟動(dòng)監(jiān)聽(tīng)本地的8080端口;

②第13~19行當(dāng)收到客戶端連接請(qǐng)求,通過(guò)accept()實(shí)現(xiàn)與客戶端的連接,并生成對(duì)應(yīng)的套接字cst;

③第20~21行在套接字cst上獲得輸入和輸出流;

④第24~35行實(shí)現(xiàn)與客戶端的通信,直至輸入“bye”為止。

該例程的運(yùn)行結(jié)果與例5-3配合執(zhí)行,將實(shí)現(xiàn)一問(wèn)一答式通信。運(yùn)行結(jié)果如圖5-9所示。圖5-9服務(wù)器端顯示

5.3多?線?程?操?作

5.3.1多線程的概念

在上一節(jié)的例子中,實(shí)現(xiàn)的是通信最基本的形式“點(diǎn)到點(diǎn)(PointtoPoint,P2P)”通信,其特點(diǎn)是只有2人參與,進(jìn)行一問(wèn)一答形式的信息交互。如果要實(shí)現(xiàn)多于2人參與的互動(dòng)會(huì)話聊天室,在軟件設(shè)計(jì)時(shí)需要考慮如何接受多個(gè)參與者的申請(qǐng),加入到同一個(gè)會(huì)話場(chǎng)景。

通常,在基于TCP的網(wǎng)絡(luò)編程中采用按照多線程的解決方案,以不同線程來(lái)與不同的用戶建立Socket連接,分別進(jìn)行通信。這樣的解決方案可以很容易在生活中發(fā)現(xiàn),例如火車(chē)票銷(xiāo)售系統(tǒng)、銀行存取系統(tǒng)或者是自來(lái)水供應(yīng)系統(tǒng)均能為多個(gè)客戶同時(shí)提供服務(wù)。在計(jì)算機(jī)中被執(zhí)行的程序稱(chēng)為“進(jìn)程(Process)”,它是在計(jì)算機(jī)中依次執(zhí)行的指令,獨(dú)立占有系統(tǒng)資源,代表主動(dòng)對(duì)象。線程(Thread)指進(jìn)程概念中的程序代碼的執(zhí)行位置。線程是屬于進(jìn)程的,一個(gè)進(jìn)程往往存在多個(gè)相似的線程。另一種更加形象的比喻是“線程是程序中的一條執(zhí)行路徑”,則多線程表示程序中包含多條執(zhí)行路徑。當(dāng)多個(gè)線程同時(shí)使用同一資源的時(shí)候,將會(huì)產(chǎn)生沖突。

【例5-5】

實(shí)現(xiàn)一組數(shù)1~30的累加,共啟用3個(gè)線程,每個(gè)線程都是從該數(shù)據(jù)集合中取10個(gè)數(shù)分別累加,最后將各線程的和再累加在一起。

代碼注釋如下:

①第1行因?yàn)橐獙?shí)現(xiàn)數(shù)字1~30的累加,所以定義一個(gè)共享資源類(lèi);

②第2行成員變量count為共享資源,為了避免外部直接引用,使用了private修飾符;

③第3~6行定義count的自增方法為?+1,并且通過(guò)synchronized聲明該方法在某個(gè)時(shí)刻只能被一個(gè)線程所調(diào)用,即排他行;

④第8~31行定義實(shí)現(xiàn)累加的線程;

⑤第34行定義計(jì)數(shù)器對(duì)象;⑥第35~40行定義三個(gè)累加的線程對(duì)象,并將這些對(duì)象作為參數(shù)傳遞為T(mén)hread類(lèi)對(duì)象;

⑦第41~43行通過(guò)start()啟動(dòng)累加線程;

⑧第44行通過(guò)join()將這些子線程加入到主線程中,保證當(dāng)子線程結(jié)束后,主線程才能結(jié)束;

⑨第45行輸出累加結(jié)果。

運(yùn)行結(jié)果如圖5-10所示。

該例在第9章遠(yuǎn)程方法接口修改為分布式累加。圖5-10多線程累計(jì)結(jié)果5.3.2Java的多線程

Java中為用戶自定義線程類(lèi)提供了兩種方法,分別是:

●?繼承Thread類(lèi),例如:

publicclassmyThreadextendsThread{}

●?實(shí)現(xiàn)Runnable接口,例如:

publicclassmyThreadimplmentsRunnable{}

這兩種方法都需要調(diào)用run()方法使線程運(yùn)行。所以,自定義線程時(shí)必須覆蓋run()方法。由于Java的單繼承特性,通常建議在自定義線程類(lèi)時(shí)采用Runnable接口。

Thread類(lèi)提供了七種重載的構(gòu)造方法來(lái)實(shí)現(xiàn)線程類(lèi)的實(shí)例化,分別是:●?publicThread(),默認(rèn)的構(gòu)造方法;

●?publicThread(Stringname),指定了線程名稱(chēng)的構(gòu)造方法;

●?publicThread(Runnabletarget),帶有Runnable參數(shù)的構(gòu)造方法;

●?publicThread(Runnabletarget,Stringname),有Runnable參數(shù)和名稱(chēng)的構(gòu)造方法;

●?publicThread(ThreadGroupgroup,Stringname),帶有線程組名和線程名的構(gòu)造方法;

●?publicThread(ThreadGroupgroup,Runnabletarget),帶有線程組名和Runnable參數(shù)的構(gòu)造方法; ●?publicThread(ThreadGroupgroup,Runnabletaget,Stringname)帶有線程組名和Runnable參數(shù),以及設(shè)定了線程名的構(gòu)造方法;

在利用Thread實(shí)現(xiàn)多線程時(shí),需要先自定義線程類(lèi),然后聲明線程實(shí)例對(duì)象,最后調(diào)用start()使線程運(yùn)行。在利用Runnable實(shí)現(xiàn)多線程時(shí),不能直接創(chuàng)建線程類(lèi),必須先聲明一個(gè)Runnable實(shí)例對(duì)象,再將該實(shí)例傳遞給Thread類(lèi),才能調(diào)用start()使線程運(yùn)行。代碼注釋如下:

①第2~7行與例5~6中的相同;

②第9~10行先聲明一個(gè)Runnable對(duì)象,再將該對(duì)象帶入Thread構(gòu)造方法;

③第11行通過(guò)start()啟動(dòng)線程。

當(dāng)多個(gè)線程同時(shí)訪問(wèn)一個(gè)資源的時(shí)候,就會(huì)出現(xiàn)系統(tǒng)運(yùn)行異常,類(lèi)似于數(shù)據(jù)庫(kù)操作中的臟讀和臟寫(xiě),所以引入線程同步的概念來(lái)避免它。同步的基本思想是避免多個(gè)線程訪問(wèn)同一資源,可以通過(guò)“鎖”的形式實(shí)現(xiàn)。Java同步機(jī)制的作用就是力圖避免對(duì)“對(duì)象”訪問(wèn)的沖突。為此,提供了一種信號(hào)量monitor來(lái)控制訪問(wèn)對(duì)象的同步,使用關(guān)鍵字synchronized來(lái)修飾方法或者程序段,實(shí)現(xiàn)信號(hào)量的控制。Synchronized既可以用來(lái)鎖完整的成員方法,也可以用來(lái)鎖方法中某個(gè)程序段。5.3.3多線程與TCPSocket

聊天室(chatroom)是一個(gè)典型的多線程通信應(yīng)用程序。如果是基于TCP協(xié)議的通信,則服務(wù)器端必須利用多線程與每個(gè)客戶機(jī)端進(jìn)行單獨(dú)的連接,并采用線性表或鏈表保存每個(gè)客戶機(jī)端的Socket信息,然后由服務(wù)器以逐次轉(zhuǎn)發(fā)消息形式,實(shí)現(xiàn)消息的發(fā)布。如果是基于UDP通信,則可以選擇與TCP相類(lèi)似的方法,也可以使用D類(lèi)IP多播地址進(jìn)行群發(fā)信息。

在TCP中,首先服務(wù)器開(kāi)啟指定的端口,用于監(jiān)聽(tīng)客戶機(jī)端的連接請(qǐng)求??蛻魴C(jī)端發(fā)起連接請(qǐng)求,服務(wù)器端通過(guò)accept()接收,生成一個(gè)單獨(dú)的線程與每一個(gè)客戶機(jī)端進(jìn)行獨(dú)立的通信,如圖5-11所示。圖5-11基于TCP的多線程通信在程序設(shè)計(jì)時(shí),可保持在客戶機(jī)端程序(例5-4)不改變的情況,在原單線程的服務(wù)器(例5-4)上改進(jìn),增加以下部分功能:

(1)設(shè)立主程序線程;

(2)增加while循環(huán),接受客戶機(jī)端的連接請(qǐng)求;

(3)設(shè)立對(duì)應(yīng)于客戶機(jī)端的對(duì)話線程;

(4)創(chuàng)建線程對(duì)象,并啟動(dòng)該線程。

代碼注釋如下:

①第3行聲明主程序?yàn)榫€程;

②第5~13行在構(gòu)造方法中實(shí)現(xiàn)監(jiān)聽(tīng)本地的指定端口;

③第16~20行采用while循環(huán)的方法無(wú)限地接收客戶端的連接請(qǐng)求;

④第25~53行定義客戶端的通信線程類(lèi),該類(lèi)實(shí)現(xiàn)了Runnable接口;

⑤第54~58行啟動(dòng)主程序。

例5-8是一個(gè)簡(jiǎn)化的多線程服務(wù)器端程序,實(shí)際上真實(shí)環(huán)境下需要為服務(wù)器主程序、與客戶端連接、接收客戶端消息、給客戶端發(fā)送消息等設(shè)置線程。5.3.4多客戶端信息存儲(chǔ)

在上節(jié)中僅討論了如何使用多線程連接多個(gè)客戶機(jī)端。而實(shí)際上要滿足一個(gè)簡(jiǎn)單聊天室的功能,還需要考慮以下重要的內(nèi)容:

●?如何保存客戶機(jī)端的信息,例如,客戶機(jī)端的Socket信息等,當(dāng)服務(wù)器端收到客戶機(jī)端發(fā)送的消息后,如何將消息向聊天室里所有用戶的分發(fā);

●?如何設(shè)計(jì)消息的格式,即在聊天室中發(fā)送的消息應(yīng)該包含哪些內(nèi)容,例如,信息的發(fā)送者、接收者目的地,以及內(nèi)容等;圖5-12Java中的線性表和鏈表類(lèi)通常,為了保存所有連接到服務(wù)器的客戶機(jī)端Socket信息,可以采用Java所提供的線性表Collection或鏈表Map等數(shù)據(jù)結(jié)構(gòu),這些結(jié)構(gòu)保存在java.util類(lèi)庫(kù)中,如圖5-12所示。其中,Java.util.Vector提供了向量(Vector)類(lèi)以實(shí)現(xiàn)動(dòng)態(tài)數(shù)組的功能,在多線程編程時(shí)經(jīng)常被使用。每個(gè)Vector實(shí)例都有一個(gè)容量(Capacity),用于存儲(chǔ)元素的數(shù)組,這個(gè)容量可隨著不斷添加新元素而自動(dòng)增加。

Vector的構(gòu)造方法有:

●?Vector();//構(gòu)造一個(gè)空的實(shí)例對(duì)象

●?Vector(int

initialCapacity);//構(gòu)造一個(gè)初始有一個(gè)向量的實(shí)例對(duì)象

●?Vector(int

initialCapacity,int

capacityIncrement);//構(gòu)造一個(gè)初始有一個(gè)向量,并且每次遞增一個(gè)的實(shí)例對(duì)象例如:

VectoruserList=newVector();

其常用方法有:

Vector.add(element);

//添加一個(gè)元素

VectoruserList.remove(socket);

//移除一個(gè)指定元素

Vector.size();

//獲取Vector中元素個(gè)數(shù)

在例5-8基礎(chǔ)上進(jìn)行修改,第一,需要在myServer類(lèi)中聲明一個(gè)Vector對(duì)象,如下:第三,修改客戶通信線類(lèi),這時(shí)將服務(wù)器端僅作為消息的轉(zhuǎn)發(fā)者,不允許通過(guò)鍵盤(pán)輸入與客戶機(jī)端對(duì)話:第四,為了客戶機(jī)端能及時(shí)地收到服務(wù)器轉(zhuǎn)發(fā)過(guò)來(lái)的消息,需要為客戶機(jī)端的接收和發(fā)送數(shù)據(jù)流分別設(shè)置2個(gè)線程。

【例5-9】多線程的客戶機(jī)端代碼。

代碼注釋如下:

①第3行聲明客戶端為線程;

②第4~9行聲明成員變量;

③第14~28行聲明客戶端線程的啟動(dòng)方法;

④第16行連接指定的服務(wù)器端;

⑤第23行啟動(dòng)消息的接收線程;

⑥第24行啟動(dòng)消息的發(fā)送線程;

⑦第29~48行聲明內(nèi)部接收線

溫馨提示

  • 1. 本站所有資源如無(wú)特殊說(shuō)明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁(yè)內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒(méi)有圖紙預(yù)覽就沒(méi)有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫(kù)網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。

最新文檔

評(píng)論

0/150

提交評(píng)論