我的漫漫程序之旅

          專注于JavaWeb開發(fā)
          隨筆 - 39, 文章 - 310, 評論 - 411, 引用 - 0
          數(shù)據(jù)加載中……

          網(wǎng)絡(luò)數(shù)據(jù)流的java處理

          前言:java程序要處理很多的網(wǎng)絡(luò)數(shù)據(jù),網(wǎng)絡(luò)數(shù)據(jù)發(fā)送和接收以及數(shù)據(jù)流的處理是java程序要特別關(guān)注的方面,隨著java的發(fā)展,這些方法也越來越得到重視和加強。本文從幾個方面解釋了java正確處理網(wǎng)絡(luò)數(shù)據(jù)流的要素,這些也是java程序員必須了解的基本的知識。

          1:龐大的java流處理

          首先,之所以說java流的龐大,是因為java中的流處理比其他語言的流處理在內(nèi)容上多的多。

          java流在處理上分為字符流和字節(jié)流。字符流處理的單元為2個字節(jié)的Unicode字符,分別操作字符、字符數(shù)組或字符串,而字節(jié)流處理單元為1個字節(jié),操作字節(jié)和字節(jié)數(shù)組。

          Java內(nèi)用Unicode編碼存儲字符,字符流處理類負責(zé)將外部的其他編碼的字符流和java內(nèi)Unicode字符流之間的轉(zhuǎn)換。而類InputStreamReader和OutputStreamWriter處理字符流和字節(jié)流的轉(zhuǎn)換。字符流(一次可以處理一個緩沖區(qū))一次操作比字節(jié)流(一次一個字節(jié))效率高。

          對應(yīng)不同的流,需要不同的流構(gòu)建器或流過濾實現(xiàn)。java目前依然在逐漸增加其流處理方法,雖然java類庫的創(chuàng)作人員可以列舉出很多理由來說明這要做的優(yōu)點,但我還是覺得java開始變得向其他語言一樣復(fù)雜起來。






          2:網(wǎng)絡(luò)數(shù)據(jù)流的收發(fā)

          java對網(wǎng)絡(luò)數(shù)據(jù)的發(fā)送和接收處理,也借用了一般流處理的方法。我們知道,在幾乎其他所有語言中,網(wǎng)絡(luò)數(shù)據(jù)的收發(fā)在利用類似send(或write)和recv(或read)的方法時并沒有明顯的流處理。但是java和這些語言的收發(fā)方法有較大區(qū)別,要借助流才可以完成:

          .......
                      sock = new Socket(addr, port);
                      OutputStream os = sock.getOutputStream();
                      InputStream is = sock.getInputStream();
                      os.write(byte[] b);
                      is.read(byte[] b);

          這些方法總給人一種不太舒服的感覺。不過從Jdk1.4開始彌補了這一點。JDK1.4中新增加了新的I/O流處理,在緩沖區(qū)管理、可伸縮網(wǎng)絡(luò)和文件IO、字符集支持、正規(guī)表達式匹配方面做了新的處理。其中緩沖區(qū)管理和通道(Channel)概念則是對網(wǎng)絡(luò)數(shù)據(jù)流的收發(fā)處理支持的強化。緩沖區(qū)管理中ByteBuffer類更好的支持了網(wǎng)絡(luò)數(shù)據(jù)流處理。在網(wǎng)絡(luò)連接中,通道代表了sockets的連接。基于這些新的IO處理,以上代碼可以改寫為:

          ......
                      ByteBuffer bytebuf = ByteBuffer.allocate(2048); // 創(chuàng)建一個指定大小的緩沖區(qū)
                      InetSocketAddress isa = new InetSocketAddress(hostname,port);
                      sc = SocketChannel.open(); // 建立一個socket通道
                      sc.connect( isa);  // 建立一個socket連接
                      …
                      sc.write(bytebuf); // 發(fā)送數(shù)據(jù)
                      …
                      sc.read(bytebuf); // 接收數(shù)據(jù)
                      這樣的程序似乎要流暢的多。





          回頁首


          3:java對網(wǎng)絡(luò)數(shù)據(jù)流的處理

          java程序?qū)W(wǎng)絡(luò)數(shù)據(jù)流的處理要關(guān)注四個基本方面:數(shù)據(jù)流的編碼,字節(jié)順序,數(shù)據(jù)格式對應(yīng)和取數(shù)。這是四個不同的問題,但是都影響到網(wǎng)絡(luò)數(shù)據(jù)的正確接收。

          3.1 網(wǎng)絡(luò)數(shù)據(jù)流的解碼和編碼

          網(wǎng)絡(luò)數(shù)據(jù)流的編碼和解碼主要針對流中出現(xiàn)的字符串。網(wǎng)絡(luò)數(shù)據(jù)流中的字符串均為原始的字節(jié)流形式。

          要正確接收網(wǎng)絡(luò)數(shù)據(jù)流中的字符串,首先要知道該字符串的編碼方案。然后才可以調(diào)用解碼的方法獲得java能夠認識的Unicode編碼字符串??梢杂萌缦麓a處理網(wǎng)絡(luò)數(shù)據(jù)流中字符串的編碼和解碼:

          // 獲得編碼對象,即網(wǎng)絡(luò)對等方的認識的字符串編碼。
                      Charset charset = Charset.forName("--?"); // --?為對等方的編碼名,java必須支持。
                      // 生成編碼器和解碼器對象。
                      CharsetDecoder decoder = charset.newDecoder();
                      CharsetEncoder encoder = charset.newEncoder();
                      .......
                      // 對從網(wǎng)絡(luò)數(shù)據(jù)流中獲得的字節(jié)流解碼取得java字符串
                      CharBuffer charbuf = decoder.decode(bytebuff);
                      .......
                      // 將java字符串編碼成指定編碼的字節(jié)流,以便網(wǎng)絡(luò)發(fā)送
                      Bytebuff  bytebuff = encoder.encode(CharBuffer.wrap("Test String");
                      .......
                      

          3.2 網(wǎng)絡(luò)數(shù)據(jù)流的字節(jié)順序

          目前的字節(jié)順序有兩類:BIG_ENGIAN和LITTLE_ENDIAN。各個平臺所支持的字節(jié)序不同,例如AIX、Tru64Unix、Windows等操作系統(tǒng)平臺采用LITTLE_ENDIAN字節(jié)序,Solaris等操作系統(tǒng)平臺采用BIG_ENGIAN。Java自身采用的是BIG_ENGIAN字節(jié)序,當java和運行在其他平臺上的其他語言編寫的通信程序通信時,則必須考慮到數(shù)據(jù)的字節(jié)序。

          Jkd1.4新增加的包NIO中的類ByteOrder則帶來了一定的方便。針對從網(wǎng)絡(luò)數(shù)據(jù)流的字節(jié)序,我們只要增加一行就可以輕松的處理字節(jié)序了:
          bytebuf.order(ByteOrder.LITTLE_ENDIAN); //按照LITTLE_ENDIAN字節(jié)序收發(fā)數(shù)據(jù)
          sc.read(bytebuf); // 接收數(shù)據(jù)

          上面的方法雖然簡化了我們的編程,但沒有真正處理好分布式應(yīng)用的網(wǎng)絡(luò)數(shù)據(jù)字節(jié)序問題。例如,java同時和在Tru64Unix、Solaris平臺上的應(yīng)用通信時,上述方法就不能解決問題。因為同一數(shù)據(jù)包,可能無法判斷其字節(jié)序是那一種。此時要求網(wǎng)絡(luò)數(shù)據(jù)包內(nèi)攜帶附加的字節(jié)序信息顯然是不現(xiàn)實的。這種情況下,java語言需要提供對XDR(外部數(shù)據(jù)表達)的支持,目前XDR已經(jīng)為事實上的網(wǎng)絡(luò)數(shù)據(jù)流的標準格式,分布式應(yīng)用的網(wǎng)絡(luò)數(shù)據(jù)流基本都遵循了這種格式,如果java語言提供了對XDR的支持,就可以解決通用性的問題。對于分布式應(yīng)用中的網(wǎng)絡(luò)數(shù)據(jù)流的處理就無需再根據(jù)其平臺判斷其字節(jié)序,只要按照XDR格式進行處理就可以了。

          3.3 網(wǎng)絡(luò)數(shù)據(jù)流中數(shù)據(jù)格式的對應(yīng)

          C/C++語言編寫的網(wǎng)絡(luò)程序中一般采用數(shù)據(jù)結(jié)構(gòu)的緩沖區(qū)發(fā)送數(shù)據(jù),在java端接收數(shù)據(jù)時,會出現(xiàn)一些因數(shù)據(jù)組織引起的問題:

          如結(jié)構(gòu) typedef struct {
                      int   id;
                      char  name[32];
                      short  val;
                      float  fval;
                      } SendData

          在32位操作系統(tǒng)中,它的大小并不是42,而是44!數(shù)據(jù)的組織如下圖所示:



          當通過網(wǎng)絡(luò)發(fā)送到客戶端時,客戶端也接收到44個字節(jié),如果按照順序依次取相應(yīng)的值,則會發(fā)現(xiàn)最后取得的浮點值不正確。這是因為把短整型數(shù)據(jù)后沒有意義的兩位作為了浮點數(shù)中的其中兩位。如果想正確接收該數(shù)據(jù),則必須跳過短整型數(shù)據(jù)后沒有意義的兩位,再取浮點值。

          而如果以上的結(jié)構(gòu)變?yōu)椋?/span>

          typedef struct {
                      int   id;
                      char  name[32];
                      float  fval;
                      short  val;
                      }


          則java端按照順序依次接收數(shù)據(jù)就不會發(fā)生問題。

          所以,在編寫程序時,對數(shù)據(jù)的正確組織也是非常重要的。

          3.4從網(wǎng)絡(luò)數(shù)據(jù)流中取得需要的數(shù)據(jù)

          在C/C++的Socket編程時,采用數(shù)據(jù)結(jié)構(gòu)收發(fā)數(shù)據(jù)很方便,特別是接收數(shù)據(jù)時,可以由數(shù)據(jù)結(jié)構(gòu)的數(shù)據(jù)類型自動獲得網(wǎng)絡(luò)數(shù)據(jù)流相應(yīng)的數(shù)據(jù)。但是在java中,目前我們必須對流進行分析,逐一的取得自己所需要的數(shù)據(jù),并且由于網(wǎng)絡(luò)數(shù)據(jù)流是原始的數(shù)據(jù)流,還要根據(jù)程序所需要的數(shù)據(jù)類型對網(wǎng)絡(luò)數(shù)據(jù)流進行解碼處理。發(fā)送網(wǎng)絡(luò)數(shù)據(jù)時同樣需要對數(shù)據(jù)進行封裝。這個過程也增加了java程序的煩瑣性。例如上述結(jié)構(gòu),要用如下代碼獲取相應(yīng)數(shù)據(jù):

          1. int id = bytebuf.getInt(); // 獲得整數(shù)型值
          2. int limit = bytebuf.limit(); // 獲得字節(jié)緩沖區(qū)的限值
          3. bytebuf.limit(36); // 設(shè)置字節(jié)緩沖區(qū)的限值,為字符串后面的第一個字節(jié)位置
          4. CharBuffer charbuf = decoder.decode(bytebuf); // 解碼獲得字符串
          5. Bytebuf.limit(limit); // 恢復(fù)字節(jié)緩沖區(qū)原來的限值
          6. float fval = bytebuf.getfloat(); // 獲得浮點型值
          7. short val = bytebuf.getshort(); // 獲得短整型數(shù)值





          4:結(jié)束語

          從上面的介紹可以看出,java程序中對網(wǎng)絡(luò)數(shù)據(jù)流的處理涉及的問題較多。在編寫網(wǎng)絡(luò)程序時,必須注意這些問題,以使得程序正確的處理通信的內(nèi)容。



          參考資料



          posted on 2008-12-20 11:21 々上善若水々 閱讀(751) 評論(0)  編輯  收藏 所屬分類: J2SE

          主站蜘蛛池模板: 桂阳县| 云浮市| 宁河县| 延寿县| 呼玛县| 新乐市| 金平| 曲松县| 蓝山县| 德江县| 法库县| 双牌县| 浑源县| 恩平市| 普兰县| 孟村| 宁夏| 罗江县| 蕉岭县| 弥勒县| 衡阳县| 耒阳市| 溧水县| 彭水| 云林县| 南涧| 库尔勒市| 陕西省| 平远县| 东丽区| 扎囊县| 微山县| 中方县| 定兴县| 阿拉尔市| 桂东县| 伊宁县| 友谊县| 周宁县| 化州市| 红安县|