<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    Java學(xué)習(xí)

    java,spring,structs,hibernate,jsf,ireport,jfreechart,jasperreport,tomcat,jboss -----本博客已經(jīng)搬家了,新的地址是 http://www.javaly.cn 如果有對(duì)文章有任何疑問(wèn)或者有任何不懂的地方,歡迎到www.javaly.cn (Java樂(lè)園)指出,我會(huì)盡力幫助解決。一起進(jìn)步

     

    網(wǎng)絡(luò)編程技術(shù)3

        為了一步一步的掌握網(wǎng)絡(luò)編程,下面再研究網(wǎng)絡(luò)編程中的兩個(gè)基本問(wèn)題,通過(guò)解決這兩個(gè)問(wèn)題將對(duì)網(wǎng)絡(luò)編程的認(rèn)識(shí)深入一層。

    1、如何復(fù)用Socket連接?

    在前面的示例中,客戶端中建立了一次連接,只發(fā)送一次數(shù)據(jù)就關(guān)閉了,這就相當(dāng)于撥打電話時(shí),電話打通了只對(duì)話一次就關(guān)閉了,其實(shí)更加常用的應(yīng)該是撥通一次電話以后多次對(duì)話,這就是復(fù)用客戶端連接。

    那 么如何實(shí)現(xiàn)建立一次連接,進(jìn)行多次數(shù)據(jù)交換呢?其實(shí)很簡(jiǎn)單,建立連接以后,將數(shù)據(jù)交換的邏輯寫到一個(gè)循環(huán)中就可以了。這樣只要循環(huán)不結(jié)束則連接就不會(huì)被關(guān) 閉。按照這種思路,可以改造一下上面的代碼,讓該程序可以在建立連接一次以后,發(fā)送三次數(shù)據(jù),當(dāng)然這里的次數(shù)也可以是多次,示例代碼如下:

    package tcp;

    import java.io.*;

    import java.net.*;

    /**

     * 復(fù)用連接的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ù)器端端口號(hào)

                       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ù)交換部分的邏輯寫在一個(gè)for循環(huán)的內(nèi)容,這樣就可以建立一次連接,依次將data數(shù)組中的數(shù)據(jù)按照順序發(fā)送給服務(wù)器端了。

                       如果還是使用前面示例代碼中的服務(wù)器端程序運(yùn)行該程序,則該程序的結(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

    顯然,客戶端在實(shí)際運(yùn)行時(shí)出現(xiàn)了異常,出現(xiàn)異常的原因是什么呢?如果仔細(xì)閱讀前面的代碼,應(yīng)該還記得前面示例代碼中的服務(wù)器端是對(duì)話一次數(shù)據(jù)以后就關(guān)閉了連接,如果服務(wù)器端程序關(guān)閉了,客戶端繼續(xù)發(fā)送數(shù)據(jù)肯定會(huì)出現(xiàn)異常,這就是出現(xiàn)該問(wèn)題的原因。

    按照客戶端實(shí)現(xiàn)的邏輯,也可以復(fù)用服務(wù)器端的連接,實(shí)現(xiàn)的原理也是將服務(wù)器端的數(shù)據(jù)交換邏輯寫在循環(huán)中即可,按照該種思路改造以后的服務(wù)器端代碼為:

             package tcp;

    import java.io.*;

    import java.net.*;

    /**

     * 復(fù)用連接的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)聽(tīng)端口號(hào)

                       int port = 10000;

                       try {

                                //建立連接

                                serverSocket = new ServerSocket(port);

                                System.out.println("服務(wù)器已啟動(dòng):");

                                //獲得連接

                                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ā)送和接收的邏輯寫在了一個(gè)for循環(huán)內(nèi)部,只是在實(shí)現(xiàn)時(shí)硬性的將循環(huán)次數(shù)規(guī)定成了3次,這樣代碼雖然比較簡(jiǎn)單,但是通用性比較差。

                       以該服務(wù)器端代碼實(shí)現(xiàn)為基礎(chǔ)運(yùn)行前面的客戶端程序時(shí),客戶端的輸出為:

                                服務(wù)器反饋:First

    服務(wù)器反饋:Second

    服務(wù)器反饋:Third

           服務(wù)器端程序的輸出結(jié)果為:

               服務(wù)器已啟動(dòng):

    客戶端發(fā)送內(nèi)容為:First

    客戶端發(fā)送內(nèi)容為:Second

    客戶端發(fā)送內(nèi)容為:Third

    在該程序中,比較明顯的體現(xiàn)出了“請(qǐng)求-響應(yīng)”模型,也就是在客戶端發(fā)起連接以后,首先發(fā)送字符串“First”給服務(wù)器端,服務(wù)器端輸出客戶端發(fā)送的內(nèi)容“First”,然后將客戶端發(fā)送的內(nèi)容再反饋給客戶端,這樣客戶端也輸出服務(wù)器反饋“First”,這樣就完成了客戶端和服務(wù)器端的一次對(duì)話,緊接著客戶端發(fā)送“Second”給服務(wù)器端,服務(wù)端輸出“Second”,然后將“Second”再反饋給客戶端,客戶端再輸出“Second”,從而完成第二次會(huì)話,第三次會(huì)話的過(guò)程和這個(gè)一樣。在這個(gè)過(guò)程中,每次都是客戶端程序首先發(fā)送數(shù)據(jù)給服務(wù)器端,服務(wù)器接收數(shù)據(jù)以后,將結(jié)果反饋給客戶端,客戶端接收到服務(wù)器端的反饋,從而完成一次通訊過(guò)程。

    在該示例中,雖然解決了多次發(fā)送的問(wèn)題,但是客戶端和服務(wù)器端的次數(shù)控制還不夠靈活,如果客戶端的次數(shù)不固定怎么辦呢?是否可以使用某個(gè)特殊的字符串,例如quit,表示客戶端退出呢,這就涉及到網(wǎng)絡(luò)協(xié)議的內(nèi)容了,會(huì)在后續(xù)的網(wǎng)絡(luò)應(yīng)用示例部分詳細(xì)介紹。下面開(kāi)始介紹另外一個(gè)網(wǎng)絡(luò)編程的突出問(wèn)題。

    2、如何使服務(wù)器端支持多個(gè)客戶端同時(shí)工作?

             前面介紹的服務(wù)器端程序,只是實(shí)現(xiàn)了概念上的服務(wù)器端,離實(shí)際的服務(wù)器端程序結(jié)構(gòu)距離還很遙遠(yuǎn),如果需要讓服務(wù)器端能夠?qū)嶋H使用,那么最需要解決的問(wèn)題就是——如何支持多個(gè)客戶端同時(shí)工作。

             一個(gè)服務(wù)器端一般都需要同時(shí)為多個(gè)客戶端提供通訊,如果需要同時(shí)支持多個(gè)客戶端,則必須使用前面介紹的線程的概念。簡(jiǎn)單來(lái)說(shuō),也就是當(dāng)服務(wù)器端接收到一個(gè)連接時(shí),啟動(dòng)一個(gè)專門的線程處理和該客戶端的通訊。

             按照這個(gè)思路改寫的服務(wù)端示例程序?qū)⒂蓛蓚€(gè)部分組成,MulThreadSocketServer類實(shí)現(xiàn)服務(wù)器端控制,實(shí)現(xiàn)接收客戶端連接,然后開(kāi)啟專門的邏輯線程處理該連接,LogicThread類實(shí)現(xiàn)對(duì)于一個(gè)客戶端連接的邏輯處理,將處理的邏輯放置在該類的run方法中。該示例的代碼實(shí)現(xiàn)為:

                       package tcp;

    import java.net.ServerSocket;

    import java.net.Socket;

    /**

     * 支持多客戶端的服務(wù)器端實(shí)現(xiàn)

     */

    public class MulThreadSocketServer {

             public static void main(String[] args) {

                       ServerSocket serverSocket = null;

                       Socket socket = null;

                       //監(jiān)聽(tīng)端口號(hào)

                       int port = 10000;

                       try {

                                //建立連接

                                serverSocket = new ServerSocket(port);

                                System.out.println("服務(wù)器已啟動(dòng):");

                                while(true){

                                         //獲得連接

                                         socket = serverSocket.accept();

                                         //啟動(dòng)線程

                                         new LogicThread(socket);

                                }

                       } catch (Exception e) {

                                e.printStackTrace();

                       }finally{

                                try{

                                         //關(guān)閉連接

                                         serverSocket.close();

                                }catch(Exception e){}

                       }

             }

    }

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

             下面再看一下LogicThread線程類的源代碼實(shí)現(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(); //啟動(dòng)線程

             }

            

             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){}

             }

            

             /**

              * 邏輯處理方法,實(shí)現(xiàn)echo邏輯

              * @param b 客戶端發(fā)送數(shù)據(jù)緩沖區(qū)

              * @param off 起始下標(biāo)

              * @param len 有效數(shù)據(jù)長(zhǎng)度

              * @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è)連接對(duì)象構(gòu)造該線程,該連接對(duì)象就是該線程需要處理的連接,在線程構(gòu)造完成以后,該線程就被啟動(dòng)起來(lái)了,然后在run方法內(nèi)部對(duì)客戶端連接進(jìn)行處理,數(shù)據(jù)交換的邏輯和前面的示例代碼一致,只是這里將接收到客戶端發(fā)送過(guò)來(lái)的數(shù)據(jù)并進(jìn)行處理的邏輯封裝成了logic方法,按照前面介紹的IO編程的內(nèi)容,客戶端發(fā)送過(guò)來(lái)的內(nèi)容存儲(chǔ)在數(shù)組b的起始下標(biāo)為0,長(zhǎng)度為n個(gè)中,這些數(shù)據(jù)是客戶端發(fā)送過(guò)來(lái)的有效數(shù)據(jù),將有效的數(shù)據(jù)傳遞給logic方法,logic方法實(shí)現(xiàn)的是echo服務(wù)的邏輯,也就是將客戶端發(fā)送的有效數(shù)據(jù)形成以后新的response數(shù)組,并作為返回值反饋。

             在線程中將logic方法的返回值反饋給客戶端,這樣就完成了服務(wù)器端的邏輯處理模擬,其他的實(shí)現(xiàn)和前面的介紹類似,這里就不在重復(fù)了。

             這里的示例還只是基礎(chǔ)的服務(wù)器端實(shí)現(xiàn),在實(shí)際的服務(wù)器端實(shí)現(xiàn)中,由于硬件和端口數(shù)的限制,所以不能無(wú)限制的創(chuàng)建線程對(duì)象,而且頻繁的創(chuàng)建線程對(duì)象效率也比較低,所以程序中都實(shí)現(xiàn)了線程池來(lái)提高程序的執(zhí)行效率。

             這里簡(jiǎn)單介紹一下線程池的概念,線程池(Thread pool)是池技術(shù)的一種,就是在程序啟動(dòng)時(shí)首先把需要個(gè)數(shù)的線程對(duì)象創(chuàng)建好,例如創(chuàng)建5000個(gè)線程對(duì)象,然后當(dāng)客戶端連接到達(dá)時(shí)從池中取出一個(gè)已經(jīng)創(chuàng)建完成的線程對(duì)象使用即可。當(dāng)客戶端連接關(guān)閉以后,將該線程對(duì)象重新放入到線程池中供其它的客戶端重復(fù)使用,這樣可以提高程序的執(zhí)行速度,優(yōu)化程序?qū)τ趦?nèi)存的占用等。

             關(guān)于基礎(chǔ)的TCP方式的網(wǎng)絡(luò)編程就介紹這么多,下面介紹UDP方式的網(wǎng)絡(luò)編程在Java語(yǔ)言中的實(shí)現(xiàn)。

    posted on 2009-06-15 13:28 找個(gè)美女做老婆 閱讀(322) 評(píng)論(0)  編輯  收藏


    只有注冊(cè)用戶登錄后才能發(fā)表評(píng)論。


    網(wǎng)站導(dǎo)航:
     

    導(dǎo)航

    統(tǒng)計(jì)

    公告

    本blog已經(jīng)搬到新家了, 新家:www.javaly.cn
     http://www.javaly.cn

    常用鏈接

    留言簿(6)

    隨筆檔案

    文章檔案

    搜索

    最新評(píng)論

    閱讀排行榜

    評(píng)論排行榜

    主站蜘蛛池模板: 91香蕉国产线在线观看免费| 又硬又粗又长又爽免费看 | 99精品视频免费在线观看| 亚洲国产精彩中文乱码AV| 韩日电影在线播放免费版| 亚洲人成网站在线播放vr| 国产做国产爱免费视频| 亚洲精品中文字幕乱码三区| 免费福利在线视频| 亚洲视频一区二区三区| 全免费毛片在线播放| 亚洲日韩精品无码专区加勒比☆| 四虎影视大全免费入口| 国产成人亚洲精品蜜芽影院| 亚洲男人在线无码视频| CAOPORN国产精品免费视频| 亚洲av无码一区二区三区不卡| 免费国产黄网站在线观看| 亚洲国产中文在线视频| 成人黄动漫画免费网站视频 | 精品久久久久久久免费人妻| 亚洲第一街区偷拍街拍| 亚洲女人被黑人巨大进入| 久久精品国产免费| 亚洲一卡2卡4卡5卡6卡在线99| 色视频色露露永久免费观看| 一级毛片免费在线播放| 亚洲成色www久久网站夜月| 成人免费在线看片| 色www免费视频| 久久亚洲AV午夜福利精品一区| 免费人成在线观看69式小视频| 亚洲变态另类一区二区三区| 国产成人毛片亚洲精品| 24小时免费看片| 国产亚洲精彩视频| 久久国产亚洲精品无码| 在线看片无码永久免费aⅴ| 黄网站色视频免费在线观看的a站最新 | 国产亚洲视频在线| 亚洲va中文字幕无码久久|