成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

重拾Java Network Programming(一)IO流

Lycheeee / 2234人閱讀

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

前言

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

閱讀之前,你需要知道

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

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

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

基于流的JAVA Socket 通信

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

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

OutputStream

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

public abstract class OutputStream{
    //核心方法
    //各個不同的流將實(shí)現(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)容強(qiáng)制寫入目標(biāo)地點(diǎn)
    public void flush() throws IOException;
    public void close() throws IOException;
}

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

使用完輸出流后,及時的關(guān)閉它是一個很好的習(xí)慣,否則可能會造成資源的浪費(fèi)或是內(nèi)存泄漏。我們通常使用finally塊來實(shí)現(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){
            //處理異常
        }
    }
}

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

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

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

InputStream

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

public abstract class InputStream{
    //核心方法
    //從輸入流中讀取一個字節(jié)并將其作為int值(0~255)返回
    //當(dāng)讀到輸入流末尾時,會返回-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()方法返回的應(yīng)該是一個byte,但是它卻返回了int類型,從而導(dǎo)致其返回值變?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);
    //當(dāng)流結(jié)束時,會返回-1
    if(tmp == -1) break;
    bytesRead += tmp;
}
Filters

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

在這里,先通過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的場景下不一定,因?yàn)榇藭r的性能瓶頸取決于網(wǎng)速,而不是網(wǎng)卡將數(shù)據(jù)傳到上層的速度或是應(yīng)用程序運(yùn)行的速度。

構(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。默認(rèn)情況下,我們需要強(qiáng)制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)境中應(yīng)當(dāng)盡可能不使用PrintStream,因?yàn)樵诓煌牟僮飨到y(tǒng)上,println的行為不同(因?yàn)閾Q行符的標(biāo)記不同)。因此同樣的數(shù)據(jù)在不同的操作系統(tǒng)上可能不一致。

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

而且,PrintStream會吞掉所有的異常。我們無法對異常進(jìn)行相應(yīng)的編碼。

Date Stream
以二進(jìn)制數(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格式編碼,并且記錄的字符串的長度
//它應(yīng)當(dāng)只用來和其它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屬性來讀取相應(yīng)長度的body
public final void readFully(byte[] input) throws IOException
public final void readFully(byte[] input, int offset, int length) throws IOException

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

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

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

Readers Writers

文本中的字符并不能和ASCII碼完全劃等號。很多國家的語言如中文,日文,韓文等都遠(yuǎn)遠(yuǎn)超出了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)化為相應(yīng)的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對應(yīng)的0~65535之間的整數(shù)
//如果到達(dá)了流的末尾,則返回-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è)置標(biāo)記與重置下標(biāo)至標(biāo)記處
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因?yàn)檎缜懊嫣岬降?,PrintStream依賴于當(dāng)前平臺的編碼,并且無法修改。

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://systransis.cn/yun/76348.html

相關(guān)文章

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

    摘要:從而一方面減少了響應(yīng)時間,另一方面減少了服務(wù)器的壓力。表明響應(yīng)只能被單個用戶緩存,不能作為共享緩存即代理服務(wù)器不能緩存它。這種情況稱為服務(wù)器再驗(yàn)證。否則會返回響應(yīng)。 前言 本文將根據(jù)最近所學(xué)的Java網(wǎng)絡(luò)編程實(shí)現(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

    摘要:從而一方面減少了響應(yīng)時間,另一方面減少了服務(wù)器的壓力。表明響應(yīng)只能被單個用戶緩存,不能作為共享緩存即代理服務(wù)器不能緩存它。這種情況稱為服務(wù)器再驗(yàn)證。否則會返回響應(yīng)。 前言 本文將根據(jù)最近所學(xué)的Java網(wǎng)絡(luò)編程實(shí)現(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)的類。這類主機(jī)通常不需要外部互聯(lián)網(wǎng)服務(wù),僅有主機(jī)間相互通訊的需求。可以通過該接口獲取所有本地地址,并根據(jù)這些地址創(chuàng)建。在這里我們使用阻塞隊(duì)列實(shí)現(xiàn)主線程和打印線程之間的通信。 前言 今天,我將梳理在Java網(wǎng)絡(luò)編程中很重要的一個類InetAddress以及其相關(guān)的類NetworkInterface。在這篇文章中將會涉及: InetA...

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

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

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

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

    X_AirDu 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<