国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

重拾Java Network Programming(一)IO流

Lycheeee / 2224人閱讀

摘要:不同類型的流入,往往對應(yīng)于不同類型的流數(shù)據(jù)。所以通常會(huì)將字節(jié)緩存到一定數(shù)量后再發(fā)送。如果是,則將兩個(gè)標(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ī),也可以是打印機(jī),網(wǎng)橋,路由器等等燒入網(wǎng)卡,從而確保沒有任何兩臺(tái)設(shè)備的MAC地址是相同的。
IP地址:網(wǎng)絡(luò)地址,由ISP供應(yīng)商決定。
包交換網(wǎng)絡(luò)(packet-switched network):數(shù)據(jù)被拆解為多個(gè)包并分別在線路上傳輸,每個(gè)包包含發(fā)送者和接收者的信息。
協(xié)議:節(jié)點(diǎn)之間進(jìn)行交流所遵循的規(guī)定
網(wǎng)絡(luò)分層模型

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

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

基于流的JAVA Socket 通信

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

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

OutputStream

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

public abstract class OutputStream{
    //核心方法
    //各個(gè)不同的流將實(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)是核心方法,它會(huì)將一個(gè)個(gè)ASCII碼寫入輸出流中。但是,每寫一個(gè)字節(jié)就傳送的浪費(fèi)是巨大的,因?yàn)橐淮蜹CP/UDP傳輸需要攜帶額外的控制信息和路由信息。所以通常會(huì)將字節(jié)緩存到一定數(shù)量后再發(fā)送。

使用完輸出流后,及時(shí)的關(guān)閉它是一個(gè)很好的習(xí)慣,否則可能會(huì)造成資源的浪費(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,在使用需要回收資源的類時(shí)都會(huì)使用這個(gè)模式。Java7以后我們可以用另一種更加簡潔的方式來實(shí)現(xiàn)這個(gè)代碼:

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

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

InputStream

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

public abstract class InputStream{
    //核心方法
    //從輸入流中讀取一個(gè)字節(jié)并將其作為int值(0~255)返回
    //當(dāng)讀到輸入流末尾時(shí),會(huì)返回-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é)之前會(huì)等待,因此其后的代碼將不會(huì)執(zhí)行知道讀取結(jié)束。
為了解決阻塞式讀寫的問題,可以將IO操作交給多帶帶的線程來完成。

read()方法返回的應(yīng)該是一個(gè)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);
}

有時(shí)候我們無法在一次讀取中獲得所有的輸入,所以最好將讀取放在一個(gè)循環(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é)束時(shí),會(huì)返回-1
    if(tmp == -1) break;
    bytesRead += tmp;
}
Filters

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

在這里,先通過TelnetInputStream從網(wǎng)絡(luò)上獲取Telnet數(shù)據(jù)流,然后再逐個(gè)經(jīng)過多個(gè)過濾器從而獲得流中的數(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(shí)的性能瓶頸取決于網(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就是一個(gè)PrintStream。默認(rèn)情況下,我們需要強(qiáng)制flush將PrintStream中的內(nèi)容寫出。但是,如果我們在構(gòu)造函數(shù)中將自動(dòng)flush設(shè)置為true,則每次寫入一個(gè)byte數(shù)組或是寫入換行符或是調(diào)用println操作都會(huì)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依賴于平臺(tái)的默認(rèn)編碼。但是,這個(gè)編碼和服務(wù)器端期待的編碼格式很可能是不一樣的。PrintStream不提供改變編碼的接口。

而且,PrintStream會(huì)吞掉所有的異常。我們無法對異常進(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)化為長度為兩個(gè)字節(jié)的字符
public final void writeChars(String s) throws IOException 

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

//上面兩個(gè)方法都沒有將字符串的長度寫入輸出流,所以無法分辨究竟原始字符還是構(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報(bào)文。
我們可以使用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方法識(shí)別一行末尾的方法是通過 或是 。當(dāng)readLine遇到 時(shí),它會(huì)判斷下一個(gè)字符是不是 。如果是,則將兩個(gè)標(biāo)記都拋棄并且將之前的內(nèi)容作為一行返回。如果不是,則拋棄 并將之前的內(nèi)容返回。問題在于,如果流中最后一個(gè)字符為 ,那么讀取一行的方法會(huì)掛起,并等待下一個(gè)字符。

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

Readers Writers

文本中的字符并不能和ASCII碼完全劃等號(hào)。很多國家的語言如中文,日文,韓文等都遠(yuǎn)遠(yuǎn)超出了ASCII碼編碼的范圍。用ASCII碼是無法識(shí)別這些字節(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個(gè)字符
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)檎缜懊嫣岬降模琍rintStream依賴于當(dāng)前平臺(tái)的編碼,并且無法修改。

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)注我的微信公眾號(hào)!將會(huì)不定期的發(fā)放福利哦~

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

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

相關(guān)文章

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

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

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

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

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

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

    daryl 評(píng)論0 收藏0
  • 重拾JAVA線程之獲取另個(gè)線程的返回

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

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

    摘要:為解決這問題,我們發(fā)現(xiàn)元兇處在一線程一請求上,如果一個(gè)線程能同時(shí)處理多個(gè)請求,那么在高并發(fā)下性能上會(huì)大大改善。這樣一個(gè)線程可以同時(shí)發(fā)起多個(gè)調(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 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<