Java網(wǎng)絡(luò)編程



Java對(duì)于網(wǎng)絡(luò)通訊有著非常強(qiáng)大的支持。不僅可以獲取網(wǎng)絡(luò)資源,傳遞參數(shù)到遠(yuǎn)程服務(wù)器,還可以通過(guò)Socket對(duì)象實(shí)現(xiàn)TCP協(xié)議,通過(guò)DatagramSocket對(duì)象實(shí)現(xiàn)UDP協(xié)議。同時(shí),對(duì)于多點(diǎn)廣播以及代理服務(wù)器也有著非常強(qiáng)大的支持。以下是本人在學(xué)習(xí)過(guò)程中的總結(jié)和歸納。


1. Java的基本網(wǎng)絡(luò)支持


1.1 InetAddress
    Java中的InetAddress是一個(gè)代表IP地址的對(duì)象。IP地址可以由字節(jié)數(shù)組和字符串來(lái)分別表示,InetAddress將IP地址以對(duì)象的形式進(jìn)行封裝,可以更方便的操作和獲取其屬性。InetAddress沒(méi)有構(gòu)造方法,可以通過(guò)兩個(gè)靜態(tài)方法獲得它的對(duì)象。代碼如下:


 

  1. //根據(jù)主機(jī)名來(lái)獲取對(duì)應(yīng)的InetAddress實(shí)例  
  2.         InetAddress ip = InetAddress.getByName("www.oneedu.cn");  
  3.         //判斷是否可達(dá)  
  4.         System.out.println("oneedu是否可達(dá):" + ip.isReachable(2000));   
  5.         //獲取該InetAddress實(shí)例的IP字符串  
  6.         System.out.println(ip.getHostAddress());  
  7.         //根據(jù)原始IP地址(字節(jié)數(shù)組形式)來(lái)獲取對(duì)應(yīng)的InetAddress實(shí)例  
  8.         InetAddress local = InetAddress.getByAddress(new byte[]  
  9.         {127,0,0,1});  
  10.         System.out.println("本機(jī)是否可達(dá):" + local.isReachable(5000));   
  11.         //獲取該InetAddress實(shí)例對(duì)應(yīng)的全限定域名  
  12.         System.out.println(local.getCanonicalHostName()); 


1.2 URLDecoder和URLEncoder
    這兩個(gè)類可以別用于將application/x-www-form-urlencoded MIME類型的字符串轉(zhuǎn)換為普通字符串,將普通字符串轉(zhuǎn)換為這類特殊型的字符串。使用URLDecoder類的靜態(tài)方法decode()用于解碼,URLEncoder類的靜態(tài)方法encode()用于編碼。具體使用方法如下。


 

  1. //將application/x-www-form-urlencoded字符串  
  2.         //轉(zhuǎn)換成普通字符串  
  3.                 String keyWord = URLDecoder.decode(  
  4.             "%E6%9D%8E%E5%88%9A+j2ee""UTF-8");  
  5.         System.out.println(keyWord);  
  6.         //將普通字符串轉(zhuǎn)換成  
  7.         //application/x-www-form-urlencoded字符串  
  8.         String urlStr = URLEncoder.encode(  
  9.             "ROR敏捷開(kāi)發(fā)最佳指南" , "GBK");  
  10.         System.out.println(urlStr); 


1.3 URL和URLConnection
    URL可以被認(rèn)為是指向互聯(lián)網(wǎng)資源的“指針”,通過(guò)URL可以獲得互聯(lián)網(wǎng)資源相關(guān)信息,包括獲得URL的InputStream對(duì)象獲取資源的信息,以及一個(gè)到URL所引用遠(yuǎn)程對(duì)象的連接URLConnection。
    URLConnection對(duì)象可以向所代表的URL發(fā)送請(qǐng)求和讀取URL的資源。通常,創(chuàng)建一個(gè)和URL的連接,需要如下幾個(gè)步驟:
    a. 創(chuàng)建URL對(duì)象,并通過(guò)調(diào)用openConnection方法獲得URLConnection對(duì)象;
    b. 設(shè)置URLConnection參數(shù)和普通請(qǐng)求屬性;
    c. 向遠(yuǎn)程資源發(fā)送請(qǐng)求;
    d. 遠(yuǎn)程資源變?yōu)榭捎,程序可以訪問(wèn)遠(yuǎn)程資源的頭字段和通過(guò)輸入流來(lái)讀取遠(yuǎn)程資源返回的信息。
    這里需要重點(diǎn)討論一下第三步:如果只是發(fā)送GET方式請(qǐng)求,使用connect方法建立和遠(yuǎn)程資源的連接即可;如果是需要發(fā)送POST方式的請(qǐng)求,則需要獲取URLConnection對(duì)象所對(duì)應(yīng)的輸出流來(lái)發(fā)送請(qǐng)求。這里需要注意的是,由于GET方法的參數(shù)傳遞方式是將參數(shù)顯式追加在地址后面,那么在構(gòu)造URL對(duì)象時(shí)的參數(shù)就應(yīng)當(dāng)是包含了參數(shù)的完整URL地址,而在獲得了URLConnection對(duì)象之后,就直接調(diào)用connect方法即可發(fā)送請(qǐng)求。
而POST方法傳遞參數(shù)時(shí)僅僅需要頁(yè)面URL,而參數(shù)通過(guò)需要通過(guò)輸出流來(lái)傳遞。另外還需要設(shè)置頭字段。以下是兩種方式的代碼。


 

  1. //1. 向指定URL發(fā)送GET方法的請(qǐng)求  
  2. String urlName = url + "?" + param;  
  3.             URL realUrl = new URL(urlName);  
  4.             //打開(kāi)和URL之間的連接  
  5.             URLConnection conn = realUrl.openConnection();  
  6.             //設(shè)置通用的請(qǐng)求屬性  
  7.             conn.setRequestProperty("accept""*/*");   
  8.             conn.setRequestProperty("connection""Keep-Alive");   
  9.             conn.setRequestProperty("user-agent",   
  10.                 "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1)");   
  11.             //建立實(shí)際的連接  
  12.             conn.connect();   
  13.  
  14. //2. 向指定URL發(fā)送POST方法的請(qǐng)求  
  15. URL realUrl = new URL(url);  
  16.             //打開(kāi)和URL之間的連接  
  17.             URLConnection conn = realUrl.openConnection();  
  18.             //設(shè)置通用的請(qǐng)求屬性  
  19.             conn.setRequestProperty("accept""*/*");   
  20.             conn.setRequestProperty("connection""Keep-Alive");   
  21.             conn.setRequestProperty("user-agent",   
  22.                 "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1)");   
  23.             //發(fā)送POST請(qǐng)求必須設(shè)置如下兩行  
  24.             conn.setDoOutput(true);  
  25.             conn.setDoInput(true);  
  26.             //獲取URLConnection對(duì)象對(duì)應(yīng)的輸出流  
  27.             out = new PrintWriter(conn.getOutputStream());  
  28.             //發(fā)送請(qǐng)求參數(shù)  
  29.             out.print(param);  
  30.  


    另外需要注意的是,如果既需要讀取又需要發(fā)送,一定要先使用輸出流,再使用輸入流。因?yàn)檫h(yuǎn)程資源不會(huì)主動(dòng)向本地發(fā)送請(qǐng)求,必須要先請(qǐng)求資源。


2. 基于TCP協(xié)議的網(wǎng)絡(luò)編程
    TCP協(xié)議是一種可靠的通絡(luò)協(xié)議,通信兩端的Socket使得它們之間形成網(wǎng)絡(luò)虛擬鏈路,兩端的程序可以通過(guò)虛擬鏈路進(jìn)行通訊。Java使用socket對(duì)象代表兩端的通信端口,并通過(guò)socket產(chǎn)生的IO流來(lái)進(jìn)行網(wǎng)絡(luò)通信。


2.1 ServerSocket
    在兩個(gè)通信端沒(méi)有建立虛擬鏈路之前,必須有一個(gè)通信實(shí)體首先主動(dòng)監(jiān)聽(tīng)來(lái)自另一端的請(qǐng)求。ServerSocket對(duì)象使用accept()方法用于監(jiān)聽(tīng)來(lái)自客戶端的Socket連接,如果收到一個(gè)客戶端Socket的連接請(qǐng)求,該方法將返回一個(gè)與客戶端Socket對(duì)應(yīng)的Socket對(duì)象。如果沒(méi)有連接,它將一直處于等待狀態(tài)。通常情況下,服務(wù)器不應(yīng)只接受一個(gè)客戶端請(qǐng)求,而應(yīng)該通過(guò)循環(huán)調(diào)用accept()不斷接受來(lái)自客戶端的所有請(qǐng)求。
    這里需要注意的是,對(duì)于多次接收客戶端數(shù)據(jù)的情況來(lái)說(shuō),一方面可以每次都在客戶端建立一個(gè)新的Socket對(duì)象然后通過(guò)輸入輸出通訊,這樣對(duì)于服務(wù)器端來(lái)說(shuō),每次循環(huán)所接收的內(nèi)容也不一樣,被認(rèn)為是不同的客戶端。另外,也可以只建立一次,然后在這個(gè)虛擬鏈路上通信,這樣在服務(wù)器端一次循環(huán)的內(nèi)容就是通信的全過(guò)程。
    服務(wù)器端的示例代碼:


 

  1. //創(chuàng)建一個(gè)ServerSocket,用于監(jiān)聽(tīng)客戶端Socket的連接請(qǐng)求  
  2.         ServerSocket ss = new ServerSocket(30000);  
  3.         //采用循環(huán)不斷接受來(lái)自客戶端的請(qǐng)求  
  4.         while (true)  
  5.         {  
  6.             //每當(dāng)接受到客戶端Socket的請(qǐng)求,服務(wù)器端也對(duì)應(yīng)產(chǎn)生一個(gè)Socket  
  7.             Socket s = ss.accept();  
  8.             //將Socket對(duì)應(yīng)的輸出流包裝成PrintStream  
  9.             PrintStream ps = new PrintStream(s.getOutputStream());  
  10.             //進(jìn)行普通IO操作  
  11.             ps.println("您好,您收到了服務(wù)器的新年祝福!");  
  12.             //關(guān)閉輸出流,關(guān)閉Socket  
  13.             ps.close();  
  14.             s.close();  
  15.         } 
   


2.2 Socket
    使用Socket可以主動(dòng)連接到服務(wù)器端,使用服務(wù)器的IP地址和端口號(hào)初始化之后,服務(wù)器端的accept便可以解除阻塞繼續(xù)向下執(zhí)行,這樣就建立了一對(duì)互相連接的Socket。
    客戶端示例代碼:


 

  1. Socket socket = new Socket("127.0.0.1" , 30000);  
  2.         //將Socket對(duì)應(yīng)的輸入流包裝成BufferedReader  
  3.         BufferedReader br = new BufferedReader(  
  4.             new InputStreamReader(socket.getInputStream()));  
  5.         //進(jìn)行普通IO操作  
  6.         String line = br.readLine();  
  7.         System.out.println("來(lái)自服務(wù)器的數(shù)據(jù):" + line);  
  8.         //關(guān)閉輸入流、socket  
  9.         br.close();  
  10.         socket.close(); 


2.3 使用多線程
    在復(fù)雜的通訊中,使用多線程非常必要。對(duì)于服務(wù)器來(lái)說(shuō),它需要接收來(lái)自多個(gè)客戶端的連接請(qǐng)求,處理多個(gè)客戶端通訊需要并發(fā)執(zhí)行,那么就需要對(duì)每一個(gè)傳過(guò)來(lái)的Socket在不同的線程中進(jìn)行處理,每條線程需要負(fù)責(zé)與一個(gè)客戶端進(jìn)行通信。以防止其中一個(gè)客戶端的處理阻塞會(huì)影響到其他的線程。對(duì)于客戶端來(lái)說(shuō),一方面要讀取來(lái)自服務(wù)器端的數(shù)據(jù),另一方面又要向服務(wù)器端輸出數(shù)據(jù),它們同樣也需要在不同的線程中分別處理。
具體代碼如下,服務(wù)器端:


 

  1. public class MyServer  
  2. {  
  3.     //定義保存所有Socket的ArrayList  
  4.     public static ArrayList<Socket> socketList = new ArrayList<Socket>();  
  5.     public static void main(String[] args)   
  6.         throws IOException  
  7.     {  
  8.         ServerSocket ss = new ServerSocket(30000);  
  9.         while(true)  
  10.         {  
  11.             //此行代碼會(huì)阻塞,將一直等待別人的連接  
  12.             Socket s = ss.accept();  
  13.             socketList.add(s);  
  14.             //每當(dāng)客戶端連接后啟動(dòng)一條ServerThread線程為該客戶端服務(wù)  
  15.             new Thread(new ServerThread(s)).start();  
  16.         }  
  17.     }  

    客戶端:


 

  1. public class MyClient  
  2. {  
  3.     public static void main(String[] args)  
  4.         throws IOException   
  5.     {  
  6.         Socket s = s = new Socket("127.0.0.1" , 30000);  
  7.         //客戶端啟動(dòng)ClientThread線程不斷讀取來(lái)自服務(wù)器的數(shù)據(jù)  
  8.         new Thread(new ClientThread(s)).start();  
  9.         //獲取該Socket對(duì)應(yīng)的輸出流  
  10.         PrintStream ps = new PrintStream(s.getOutputStream());  
  11.         String line = null;  
  12.         //不斷讀取鍵盤輸入  
  13.         BufferedReader br = new BufferedReader(new InputStreamReader(System.in));  
  14.         while ((line = br.readLine()) != null)  
  15.         {  
  16.             //將用戶的鍵盤輸入內(nèi)容寫入Socket對(duì)應(yīng)的輸出流  
  17.             ps.println(line);  
  18.         }  
  19.     }  

2.4 使用協(xié)議字符
    協(xié)議字符用于標(biāo)識(shí)一些字段的特定功能,用于說(shuō)明傳輸內(nèi)容的特性。它可以由用戶自定義。一般情況下,可以定義一個(gè)存放這些協(xié)議字符的接口。如下:

   

  1. public interface YeekuProtocol  
  2. {  
  3.     //定義協(xié)議字符串的長(zhǎng)度  
  4.     int PROTOCOL_LEN = 2;  
  5.     //下面是一些協(xié)議字符串,服務(wù)器和客戶端交換的信息  
  6.     //都應(yīng)該在前、后添加這種特殊字符串。  
  7.     String MSG_ROUND = "§γ";  
  8.     String USER_ROUND = "∏∑";  
  9.     String LOGIN_SUCCESS = "1";  
  10.     String NAME_REP = "-1";  
  11.     String PRIVATE_ROUND = "★【";  
  12.     String SPLIT_SIGN = "※";  


    在字段時(shí)可以加上這些字符,如下代碼:


 

  1. while(true)  
  2.             {  
  3.                 String userName = JOptionPane.showInputDialog(tip + "輸入用戶名");  
  4.                 //將用戶輸入的用戶名的前后增加協(xié)議字符串后發(fā)送  
  5.                 ps.println(YeekuProtocol.USER_ROUND + userName  
  6.                     + YeekuProtocol.USER_ROUND);  
  7.                 //讀取服務(wù)器的響應(yīng)  
  8.                 String result = brServer.readLine();  
  9.                 //如果用戶重復(fù),開(kāi)始下次循環(huán)  
  10.                 if (result.equals(YeekuProtocol.NAME_REP))  
  11.                 {  
  12.                     tip = "用戶名重復(fù)!請(qǐng)重新";  
  13.                     continue;  
  14.                 }  
  15.                 //如果服務(wù)器返回登陸成功,結(jié)束循環(huán)  
  16.                 if (result.equals(YeekuProtocol.LOGIN_SUCCESS))  
  17.                 {  
  18.                     break;  
  19.                 }  
  20.             } 


    收到發(fā)送來(lái)的字段時(shí)候,也再次拆分成所需要的部分,如下代碼:


 

  1. if (line.startsWith(YeekuProtocol.PRIVATE_ROUND)   
  2.                     && line.endsWith(YeekuProtocol.PRIVATE_ROUND))  
  3.                 {  
  4.                     //得到真實(shí)消息  
  5.                     String userAndMsg = getRealMsg(line);  
  6.                     //以SPLIT_SIGN來(lái)分割字符串,前面部分是私聊用戶,后面部分是聊天信息  
  7.                     String user = userAndMsg.split(YeekuProtocol.SPLIT_SIGN)[0];  
  8.                     String msg = userAndMsg.split(YeekuProtocol.SPLIT_SIGN)[1];  
  9.                     //獲取私聊用戶對(duì)應(yīng)的輸出流,并發(fā)送私聊信息  
  10.                     Server.clients.get(user).println(  
  11.                         Server.clients.getKeyByValue(ps) + "悄悄地對(duì)你說(shuō):" + msg);  
  12.                 } 


3. UDP協(xié)議的網(wǎng)絡(luò)編程
    UDP協(xié)議是一種不可靠的網(wǎng)絡(luò)協(xié)議,它在通訊實(shí)例的兩端個(gè)建立一個(gè)Socket,但這兩個(gè)Socket之間并沒(méi)有虛擬鏈路,這兩個(gè)Socket只是發(fā)送和接受數(shù)據(jù)報(bào)的對(duì)象,Java提供了DatagramSocket對(duì)象作為基于UDP協(xié)議的Socket,使用DatagramPacket代表DatagramSocket發(fā)送和接收的數(shù)據(jù)報(bào)。


3.1 使用DatagramSocket發(fā)送、接收數(shù)據(jù)
    DatagramSocket本身并不負(fù)責(zé)維護(hù)狀態(tài)和產(chǎn)生IO流。它僅僅負(fù)責(zé)接收和發(fā)送數(shù)據(jù)報(bào)。使用receive(DatagramPacket p)方法接收,使用send(DatagramPacket p)方法發(fā)送。
    這里需要首先明確的是,DatagramPacket對(duì)象的構(gòu)造。DatagramPacket的內(nèi)部實(shí)際上采用了一個(gè)字節(jié)型數(shù)組來(lái)保存數(shù)據(jù),它的初始化方法如下:

  1. //接收端的DatagaramSocket內(nèi)部包含一個(gè)空的數(shù)組,接收傳遞過(guò)來(lái)的數(shù)據(jù)報(bào)中的數(shù)組信息。可以通過(guò)DatagaramSocket對(duì)象的getData()方法返回的數(shù)組來(lái)獲取其中的包含的數(shù)組。  
  2. Private DatagaramSocket udpSocket=new DatagaramSocket(buf,buf.length);  
  3. //發(fā)送端的DatagaramSocket內(nèi)部包含一個(gè)將要傳遞的數(shù)組,同時(shí)需要包含目標(biāo)IP和端口。如果初始化時(shí)傳遞的數(shù)組參數(shù)是空,可以通過(guò)調(diào)用DatagaramSocket對(duì)象的setData()方法設(shè)置內(nèi)容。  
  4. Private DatagaramSocket udpSocket=new DatagaramSocket(buf,buf.length,IP,PORT);  
  5. udpSocket。setData(outBuf); 

    作為這兩個(gè)方法的參數(shù),作用和構(gòu)造不同的。作為接收方法中的參數(shù),DatagramPacket中的數(shù)組一個(gè)空的數(shù)組,用來(lái)存放接收到的DatagramPacket對(duì)象中的數(shù)組;而作為發(fā)送方法參數(shù),DatagramPacket本身含有了目的端的IP和端口,以及存儲(chǔ)了要發(fā)送內(nèi)容的指定了長(zhǎng)度的字節(jié)型數(shù)組。
    另外,DatagramPacket對(duì)象還提供了setData(Byte[] b)和Byte[] b= getData()方法,用于設(shè)置DatagramPacket中包含的數(shù)組內(nèi)容和獲得其中包含數(shù)組的內(nèi)容。
    使用TCP和UDP通訊的編碼區(qū)別:
    a. 在TCP中,目標(biāo)IP和端口由Socket指定包含;UDP中,目標(biāo)IP由DatagramPacket包含指定,DatagramSocket只負(fù)責(zé)發(fā)送和接受。
    b. 在TCP中,通訊是通過(guò)Socket獲得的IO流來(lái)實(shí)現(xiàn);在UDP中,則通過(guò)DatagramSocket的send和receive方法。
 

3.2 使用MulticastSocket實(shí)現(xiàn)多點(diǎn)廣播
    MulticastSocket是DatagramSocket的子類,可以將數(shù)據(jù)報(bào)以廣播形式發(fā)送到數(shù)量不等的多個(gè)客戶端。實(shí)現(xiàn)策略就是定義一個(gè)廣播地址,使得每個(gè)MulticastSocket都加入到這個(gè)地址中。從而每次使用MulticastSocket發(fā)送數(shù)據(jù)報(bào)(包含的廣播地址)時(shí),所有加入了這個(gè)廣播地址的MulticastSocket對(duì)象都可以收到信息。
    MulticastSocket的初始化需要傳遞端口號(hào)作為參數(shù),特別對(duì)于需要接受信息的端來(lái)說(shuō),它的端口號(hào)需要與發(fā)送端數(shù)據(jù)報(bào)中包含的端口號(hào)一致。具體代碼如下:


 

  1. //創(chuàng)建用于發(fā)送、接收數(shù)據(jù)的MulticastSocket對(duì)象  
  2.             //因?yàn)樵揗ulticastSocket對(duì)象需要接收,所以有指定端口  
  3.             socket = new MulticastSocket(BROADCAST_PORT);  
  4.             broadcastAddress = InetAddress.getByName(BROADCAST_IP);  
  5.             //將該socket加入指定的多點(diǎn)廣播地址  
  6.             socket.joinGroup(broadcastAddress);  
  7.             //設(shè)置本MulticastSocket發(fā)送的數(shù)據(jù)報(bào)被回送到自身  
  8.             socket.setLoopbackMode(false);  
  9.             //初始化發(fā)送用的DatagramSocket,它包含一個(gè)長(zhǎng)度為0的字節(jié)數(shù)組  
  10.             outPacket = new DatagramPacket(new byte[0] , 0 ,  
  11.                 broadcastAddress , BROADCAST_PORT); 


4. 使用代理服務(wù)器
    Java中可以使用Proxy直接創(chuàng)建連接代理服務(wù)器,具體使用方法如下:


 

  1. public class ProxyTest  
  2. {  
  3.     Proxy proxy;  
  4.     URL url;  
  5.     URLConnection conn;  
  6.     //從網(wǎng)絡(luò)通過(guò)代理讀數(shù)據(jù)  
  7.     Scanner scan;  
  8.     PrintStream ps ;  
  9.     //下面是代理服務(wù)器的地址和端口,  
  10.     //換成實(shí)際有效的代理服務(wù)器的地址和端口  
  11.     String proxyAddress = "202.128.23.32";  
  12.     int proxyPort;  
  13.     //下面是你試圖打開(kāi)的網(wǎng)站地址  
  14.     String urlStr = "http://www.oneedu.cn";  
  15.  
  16.     public void init()  
  17.     {  
  18.         try 
  19.         {  
  20.             url = new URL(urlStr);  
  21.             //創(chuàng)建一個(gè)代理服務(wù)器對(duì)象  
  22.             proxy = new Proxy(Proxy.Type.HTTP,  
  23.                 new InetSocketAddress(proxyAddress , proxyPort));  
  24.             //使用指定的代理服務(wù)器打開(kāi)連接  
  25.             conn = url.openConnection(proxy);  
  26.             //設(shè)置超時(shí)時(shí)長(zhǎng)。  
  27.             conn.setConnectTimeout(5000);  
  28.             scan = new Scanner(conn.getInputStream());  
  29.             //初始化輸出流  
  30.             ps = new PrintStream("Index.htm");  
  31.             while (scan.hasNextLine())  
  32.             {  
  33.                 String line = scan.nextLine();  
  34.                 //在控制臺(tái)輸出網(wǎng)頁(yè)資源內(nèi)容  
  35.                 System.out.println(line);  
  36.                 //將網(wǎng)頁(yè)資源內(nèi)容輸出到指定輸出流  
  37.                 ps.println(line);  
  38.             }  
  39.         }  
  40.         catch(MalformedURLException ex)  
  41.         {  
  42.             System.out.println(urlStr + "不是有效的網(wǎng)站地址!");  
  43.         }  
  44.         catch(IOException ex)  
  45.         {  
  46.             ex.printStackTrace();  
  47.         }  
  48.         //關(guān)閉資源  
  49.         finally 
  50.         {  
  51.             if (ps != null)  
  52.             {  
  53.                 ps.close();  
  54.             }  
  55.         }  
  56.     }  
  57.  
  58.     

5. 編碼中的問(wèn)題總結(jié)

    a. 雙方初始化套接字以后,就等于建立了鏈接,表示雙方互相可以知曉對(duì)方的狀態(tài)。服務(wù)器端可以調(diào)用接收到的客戶端套接字進(jìn)行輸入輸出流操作,客戶端可以調(diào)用自身內(nèi)部的套接字對(duì)象進(jìn)行輸入輸出操作。這樣可以保持輸入輸出的流暢性。例如,客戶端向服務(wù)器端發(fā)送消息時(shí),可以隔一段的時(shí)間輸入一段信息,然后服務(wù)器端使用循環(huán)不斷的讀取傳過(guò)來(lái)的輸入流。
    b. 對(duì)于可能出現(xiàn)阻塞的方法,例如客戶端進(jìn)行循環(huán)不斷讀取來(lái)自服務(wù)器端的響應(yīng)信息時(shí),如果此時(shí)服務(wù)器端并沒(méi)有向客戶端進(jìn)行輸出,那么讀取的方法將處于阻塞狀態(tài),直到收到信息為止才向下執(zhí)行代碼。那么對(duì)于這樣容易產(chǎn)生阻塞的代碼,就需要將它放在一個(gè)單獨(dú)的線程中處理。
    c. 有一些流是順承的。例如,服務(wù)器端在收到客戶端的消息以后,就將消息再通過(guò)輸出流向其他所有服務(wù)器發(fā)送。那么,這個(gè)來(lái)自客戶端的輸入流和發(fā)向客戶端的輸出流就是順接的關(guān)系,不必對(duì)它們分在兩個(gè)不同的線程。
    d. println()方法對(duì)應(yīng)readLine()。
    e. 在JFrame類中,一般不要將自己的代碼寫進(jìn)main方法中,可以將代碼寫到自定義的方法中,然后在main方法中調(diào)用。
 

北大青鳥(niǎo)網(wǎng)上報(bào)名
北大青鳥(niǎo)招生簡(jiǎn)章