成人无码视频,亚洲精品久久久久av无码,午夜精品久久久久久毛片,亚洲 中文字幕 日韩 无码

資訊專欄INFORMATION COLUMN

重拾Java Network Programming(一)IO流

Lycheeee / 2310人閱讀

摘要:不同類型的流入,往往對應于不同類型的流數(shù)據(jù)。所以通常會將字節(jié)緩存到一定數(shù)量后再發(fā)送。如果是,則將兩個標記都拋棄并且將之前的內(nèi)容作為一行返回。因此二者陷入死鎖。因此推出了和類。

前言

最近在重拾Java網(wǎng)絡(luò)編程,想要了解一些JAVA語言基本的實現(xiàn),這里記錄一下學習的過程。

閱讀之前,你需要知道

網(wǎng)絡(luò)節(jié)點(node):位于網(wǎng)絡(luò)上的互相連通的設(shè)備,通常為計算機,也可以是打印機,網(wǎng)橋,路由器等等燒入網(wǎng)卡,從而確保沒有任何兩臺設(shè)備的MAC地址是相同的。
IP地址:網(wǎng)絡(luò)地址,由ISP供應商決定。
包交換網(wǎng)絡(luò)(packet-switched network):數(shù)據(jù)被拆解為多個包并分別在線路上傳輸,每個包包含發(fā)送者和接收者的信息。
協(xié)議:節(jié)點之間進行交流所遵循的規(guī)定
網(wǎng)絡(luò)分層模型

當我們試圖訪問一個網(wǎng)站時,瀏覽器實際上直接訪問的是本地的傳輸層(Transport Layer)。傳輸層將HTTP報文分段為TCP報文并繼續(xù)向下傳遞給IP層。IP層封裝信息并且將其傳送到物理鏈路上。服務器對收到的數(shù)據(jù)以相反的順序解包并讀取里面的請求內(nèi)容。

端口:一臺計算機在傳輸層的每一個協(xié)議上通常有65,535個邏輯端口。HTTP通常使用80端口
C/S模型:客戶端與服務器模型。通常是客戶端向服務器主動發(fā)送請求并等待服務器的響應。

基于流的JAVA Socket 通信

Java的IO最初是基于流的。從輸入流中讀取數(shù)據(jù),向輸出流中寫入數(shù)據(jù)。不同類型的流入java.io.FileInputStreamsun.net.TelnetOutput Stream往往對應于不同類型的流數(shù)據(jù)。但是,讀取和寫入流的方法本質(zhì)上是相同的。Java還提供了ReadersWriters系列來支持對字符流的輸入輸出。

流是同步的。同步的流是指當程序(通常是某個線程)要求從流中讀取或是向流中寫入一段數(shù)據(jù)時,在獲取到數(shù)據(jù)或是完成數(shù)據(jù)寫入之前,該程序?qū)恢蓖T谶@一步,不會進行任何工作。Java也提供了非阻塞的I/O。我們將在后面繼續(xù)了解。

OutputStream

先看一下所有輸出流的父類OutputStream的API

public abstract class OutputStream{
    //核心方法
    //各個不同的流將實現(xiàn)具體的寫入
    //如ByteArrayOutputStream可以直接寫入內(nèi)存,而FileOutputStream則需要根據(jù)操作系統(tǒng)調(diào)用底層函數(shù)來寫入文件
    public abstract void write(int b);
    public void write(byte[] data) throws IOException;
    public void write(byte[] data, int offset, int length) throws IOException;

    //將緩存的內(nèi)容強制寫入目標地點
    public void flush() throws IOException;
    public void close() throws IOException;
}

在這里write(int)是核心方法,它會將一個個ASCII碼寫入輸出流中。但是,每寫一個字節(jié)就傳送的浪費是巨大的,因為一次TCP/UDP傳輸需要攜帶額外的控制信息和路由信息。所以通常會將字節(jié)緩存到一定數(shù)量后再發(fā)送。

使用完輸出流后,及時的關(guān)閉它是一個很好的習慣,否則可能會造成資源的浪費或是內(nèi)存泄漏。我們通常使用finally塊來實現(xiàn):

OutputStream os = null;
try{
    os = new FileOutputStream("/tmp/data.txt");
}catch(IOException e){
    System.err.println(ex.getMessage());
}finally{
    if(os!=null){
        try{
            os.close();
        }catch(IOException e){
            //處理異常
        }
    }
}

上面的代碼風格被稱為dispose pattern,在使用需要回收資源的類時都會使用這個模式。Java7以后我們可以用另一種更加簡潔的方式來實現(xiàn)這個代碼:

try(OutputStream os = new FileOutputStream("/tmp/data.txt")){
 ...
}catch(IOException e){
    System.err.println(ex.getMessage());
}

Java會自動調(diào)用實現(xiàn)AutoCloseable對象的close()方法,因此我們無需再寫ugly的finally塊。

InputStream

同樣,看一下輸入流的基類InputStream的API

public abstract class InputStream{
    //核心方法
    //從輸入流中讀取一個字節(jié)并將其作為int值(0~255)返回
    //當讀到輸入流末尾時,會返回-1
    public abstract int read() throws IOException;
    public int read(byte[] input) throws IOException;
    public int read(byte[] input, int offset, int length) throws IOException;
    public long skip(long n) throws IOException;
    public int available() throws IOException;
    public void close() throws IOException;
}

輸入流也是阻塞式的,它在讀取到字節(jié)之前會等待,因此其后的代碼將不會執(zhí)行知道讀取結(jié)束。
為了解決阻塞式讀寫的問題,可以將IO操作交給多帶帶的線程來完成。

read()方法返回的應該是一個byte,但是它卻返回了int類型,從而導致其返回值變?yōu)?b>-128~127之間而不是0~255。我們需要通過一定的轉(zhuǎn)化來獲取正確的byte類型:

byte[] input = new byte[10];
for (int i = 0; i < input.length; i++) {
    int b = in.read(); 
    if (b == -1) break; 
    input[i] = (byte) (b>=0? b : b+256);
}

有時候我們無法在一次讀取中獲得所有的輸入,所以最好將讀取放在一個循環(huán)中,在讀取完成之后再跳出循環(huán)。

int bytesToRead = 1024;
int bytesRead = 0;
byte[] buffer = new byte[bytesToRead];
while(bytesRead < bytesToRead){
    int tmp = inputStream.read(buffer, bytesRead, bytesToRead-bytesRead);
    //當流結(jié)束時,會返回-1
    if(tmp == -1) break;
    bytesRead += tmp;
}
Filters

Java IO采用了裝飾者模式,我們可以將一個又一個裝飾器加到當前流上,賦予該流新的解析。

在這里,先通過TelnetInputStream從網(wǎng)絡(luò)上獲取Telnet數(shù)據(jù)流,然后再逐個經(jīng)過多個過濾器從而獲得流中的數(shù)據(jù)。將過濾器相連的方法很簡單:

FileInputStream fin = new FileInputStream("data.txt"); BufferedInputStream bin = new BufferedInputStream(fin);

Buffered Stream
先將數(shù)據(jù)緩存至內(nèi)存,再在flush或是緩存滿了以后寫入底層。
大多數(shù)情況下,緩存輸出流可以提高性能,但是在網(wǎng)絡(luò)IO的場景下不一定,因為此時的性能瓶頸取決于網(wǎng)速,而不是網(wǎng)卡將數(shù)據(jù)傳到上層的速度或是應用程序運行的速度。

構(gòu)造器如下:

public BufferedInputStream(InputStream in);
public BufferedInputStream(InputStream in, int bufferSize);
public BufferedOutputStream(OutputStream out);
public BufferedOutputStream(OutputStream out, int bufferSize);

PrintStream
輸出流,System.out就是一個PrintStream。默認情況下,我們需要強制flush將PrintStream中的內(nèi)容寫出。但是,如果我們在構(gòu)造函數(shù)中將自動flush設(shè)置為true,則每次寫入一個byte數(shù)組或是寫入換行符或是調(diào)用println操作都會flush該流。

public PrintStream(OutputStream out)
public PrintStream(OutputStream out, boolean autoFlush)

但是,在網(wǎng)絡(luò)環(huán)境中應當盡可能不使用PrintStream,因為在不同的操作系統(tǒng)上,println的行為不同(因為換行符的標記不同)。因此同樣的數(shù)據(jù)在不同的操作系統(tǒng)上可能不一致。

除此以外,PrintStream依賴于平臺的默認編碼。但是,這個編碼和服務器端期待的編碼格式很可能是不一樣的。PrintStream不提供改變編碼的接口。

而且,PrintStream會吞掉所有的異常。我們無法對異常進行相應的編碼。

Date Stream
以二進制數(shù)據(jù)的格式讀取和寫入Java的基本數(shù)據(jù)類型和String類型。

DataOutputStream的API:

public final void writeBoolean(boolean b) throws IOException 
public final void writeByte(int b) throws IOException 
public final void writeShort(int s) throws IOException 
public final void writeChar(int c) throws IOException 
public final void writeInt(int i) throws IOException
public final void writeLong(long l) throws IOException 
public final void writeFloat(float f) throws IOException 
public final void writeDouble(double d) throws IOException 

//根據(jù)UTF-16編碼將其轉(zhuǎn)化為長度為兩個字節(jié)的字符
public final void writeChars(String s) throws IOException 

//只存儲關(guān)鍵的信息,任何超出Latin-1編碼范圍的內(nèi)容都將會丟失
public final void writeBytes(String s) throws IOException 

//上面兩個方法都沒有將字符串的長度寫入輸出流,所以無法分辨究竟原始字符還是構(gòu)成字符串的最終字符
//該方法采用UTF-8格式編碼,并且記錄的字符串的長度
//它應當只用來和其它Java的程序交換信息
public final void writeUTF(String s) throws IOException

DataInputStream的API:

public final boolean readBoolean() throws IOException 
public final byte readByte() throws IOException 
public final char readChar() throws IOException 
public final short readShort() throws IOException 
public final int readInt() throws IOException
public final long readLong() throws IOException 
public final float readFloat() throws IOException 
public final double readDouble() throws IOException 
public final String readUTF() throws IOException

//讀取別的程序?qū)懙膗nsigned類型數(shù)據(jù),如C
public final int readUnsignedByte() throws IOException 
public final int readUnsignedShort() throws IOException

public final int read(byte[] input) throws IOException 
public final int read(byte[] input, int offset, int length) throws IOException

//完整的讀取一定長度的字符串,如果可讀的長度不足,將拋出IOException
//可用于已知讀取長度的場景,如讀取HTTP報文。
我們可以使用Header中的content-length屬性來讀取相應長度的body
public final void readFully(byte[] input) throws IOException
public final void readFully(byte[] input, int offset, int length) throws IOException

//讀取一行 但是最好不要使用,因為它無法正確的將非ASCII碼轉(zhuǎn)化為字符串
public final String readLine() throws IOException

這里需要強調(diào)一下為什么不要使用readLine()方法。因為readLine方法識別一行末尾的方法是通過 或是 。當readLine遇到 時,它會判斷下一個字符是不是 。如果是,則將兩個標記都拋棄并且將之前的內(nèi)容作為一行返回。如果不是,則拋棄 并將之前的內(nèi)容返回。問題在于,如果流中最后一個字符為 ,那么讀取一行的方法會掛起,并等待下一個字符。

這個問題在網(wǎng)絡(luò)IO中特別明顯,因為當一次數(shù)據(jù)發(fā)送結(jié)束之后,客戶端在關(guān)閉連接之前會等待服務器端的響應。服務器端卻在等待一個不存在的輸入。因此二者陷入死鎖。如果幸運的話,客戶端會因為超時斷開連接,使得死鎖結(jié)束,同時你丟失了最后一行數(shù)據(jù)。也有可能這個程序無限死鎖下去。

Readers Writers

文本中的字符并不能和ASCII碼完全劃等號。很多國家的語言如中文,日文,韓文等都遠遠超出了ASCII碼編碼的范圍。用ASCII碼是無法識別這些字節(jié)的。因此JAVA推出了Reader和Writer類。它將根據(jù)特定的編碼來解讀字節(jié)。

Writer類API

protected Writer()
protected Writer(Object lock)
public abstract void write(char[] text, int offset, int length) throws IOException
public void write(int c) throws IOException
public void write(char[] text) throws IOException
public void write(String s) throws IOException
public void write(String s, int offset, int length) throws IOException public abstract void flush() throws IOException
public abstract void close() throws IOException

這里和之前的區(qū)別在于將根據(jù)選擇的編碼轉(zhuǎn)化為相應的byte。

OutputStreamWriter
將字節(jié)流根據(jù)選擇的編碼轉(zhuǎn)化為字符流。

public OutputStreamWriter(OutputStream out, String encoding) throws UnsupportedEncodingException
public void write(String s) throws IOException;

Reader類API

protected Reader()
protected Reader(Object lock)
public abstract int read(char[] text, int offset, int length) throws IOException

//返回unicode對應的0~65535之間的整數(shù)
//如果到達了流的末尾,則返回-1
public int read() throws IOException
public int read(char[] text) throws IOException 

//跳過n個字符
public long skip(long n) throws IOException 
public boolean ready()
public boolean markSupported()

//設(shè)置標記與重置下標至標記處
public void mark(int readAheadLimit) throws IOException 
public void reset() throws IOException
public abstract void close() throws IOException

InputStreamReader

public InputStreamReader(InputStream in)

//如果沒有可以匹配的編碼,則拋出UnsupportedEncodingException
public InputStreamReader(InputStream in, String encoding)
throws UnsupportedEncodingException

使用InputStreamReader的范例:

public static String getMacCyrillicString(InputStream in) throws IOException {
    InputStreamReader r = new InputStreamReader(in, "MacCyrillic");            
    StringBuilder sb = new StringBuilder();
    int c;
    while ((c = r.read()) != -1) sb.append((char) c);
    return sb.toString();
}

PrintWriter
PrintStream的字符形式閱讀,盡量使用PrintWriter而非PrintStream因為正如前面提到的,PrintStream依賴于當前平臺的編碼,并且無法修改。

public PrintWriter(Writer out)
public PrintWriter(Writer out, boolean autoFlush) 
public PrintWriter(OutputStream out)
public PrintWriter(OutputStream out, boolean autoFlush) 
public void flush()
public void close()
public boolean checkError()
public void write(int c)
public void write(char[] text, int offset, int length) 
public void write(char[] text)
public void write(String s, int offset, int length) 
public void write(String s)
public void print(boolean b)
public void print(char c)
public void print(int i)
public void print(long l)
public void print(float f)
public void print(double d)
public void print(char[] text)
public void print(String s)
public void print(Object o)
public void println()
public void println(boolean b)
public void println(char c)
public void println(int i)
public void println(long l)
public void println(float f)
public void println(double d)
public void println(char[] text)
public void println(String s)
public void println(Object o)
參考書籍
Java Network Prograing 4th edition
HTTP權(quán)威指南


想要了解更多開發(fā)技術(shù),面試教程以及互聯(lián)網(wǎng)公司內(nèi)推,歡迎關(guān)注我的微信公眾號!將會不定期的發(fā)放福利哦~

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://m.hztianpu.com/yun/76348.html

相關(guān)文章

  • 重拾Java Network Programming(四)URLConnection & C

    摘要:從而一方面減少了響應時間,另一方面減少了服務器的壓力。表明響應只能被單個用戶緩存,不能作為共享緩存即代理服務器不能緩存它。這種情況稱為服務器再驗證。否則會返回響應。 前言 本文將根據(jù)最近所學的Java網(wǎng)絡(luò)編程實現(xiàn)一個簡單的基于URL的緩存。本文將涉及如下內(nèi)容: HTTP協(xié)議 HTTP協(xié)議中與緩存相關(guān)的內(nèi)容 URLConnection 和 HTTPURLConnection Respo...

    Guakin_Huang 評論0 收藏0
  • 重拾Java Network Programming(四)URLConnection & C

    摘要:從而一方面減少了響應時間,另一方面減少了服務器的壓力。表明響應只能被單個用戶緩存,不能作為共享緩存即代理服務器不能緩存它。這種情況稱為服務器再驗證。否則會返回響應。 前言 本文將根據(jù)最近所學的Java網(wǎng)絡(luò)編程實現(xiàn)一個簡單的基于URL的緩存。本文將涉及如下內(nèi)容: HTTP協(xié)議 HTTP協(xié)議中與緩存相關(guān)的內(nèi)容 URLConnection 和 HTTPURLConnection Respo...

    魏明 評論0 收藏0
  • 重拾Java Network Programming(二)InetAddress

    摘要:前言今天,我將梳理在網(wǎng)絡(luò)編程中很重要的一個類以及其相關(guān)的類。這類主機通常不需要外部互聯(lián)網(wǎng)服務,僅有主機間相互通訊的需求。可以通過該接口獲取所有本地地址,并根據(jù)這些地址創(chuàng)建。在這里我們使用阻塞隊列實現(xiàn)主線程和打印線程之間的通信。 前言 今天,我將梳理在Java網(wǎng)絡(luò)編程中很重要的一個類InetAddress以及其相關(guān)的類NetworkInterface。在這篇文章中將會涉及: InetA...

    daryl 評論0 收藏0
  • 重拾JAVA線程之獲取另個線程的返回

    摘要:它將管理線程的創(chuàng)建銷毀和復用,盡最大可能提高線程的使用效率。如果我們在另一個線程中需要使用這個結(jié)果,則這個線程會掛起直到另一個線程返回該結(jié)果。我們無需再在另一個線程中使用回調(diào)函數(shù)來處理結(jié)果。 前言 Java的多線程機制允許我們將可以并行的任務分配給不同的線程同時完成。但是,如果我們希望在另一個線程的結(jié)果之上進行后續(xù)操作,我們應該怎么辦呢? 注:本文的代碼沒有經(jīng)過具體實踐的檢驗,純屬為了...

    liuchengxu 評論0 收藏0
  • JAVA NIO 步步構(gòu)建I/O多路復用的請求模型

    摘要:為解決這問題,我們發(fā)現(xiàn)元兇處在一線程一請求上,如果一個線程能同時處理多個請求,那么在高并發(fā)下性能上會大大改善。這樣一個線程可以同時發(fā)起多個調(diào)用,并且不需要同步等待數(shù)據(jù)就緒。表示當前就緒的事件類型。 JAVA NIO 一步步構(gòu)建I/O多路復用的請求模型 摘要:本文屬于原創(chuàng),歡迎轉(zhuǎn)載,轉(zhuǎn)載請保留出處:https://github.com/jasonGeng88/blog 文章一:JAVA ...

    X_AirDu 評論0 收藏0

發(fā)表評論

0條評論

閱讀需要支付1元查看
<