Socket詳解

在客戶端/服務(wù)器通信模式中链方,Socket是雙方通信通道的抽象封裝,用戶可通過配置Socket的參數(shù)并構(gòu)建Socket來完成雙方的連接,并通過此通道進(jìn)行網(wǎng)絡(luò)通信叫潦。

1、構(gòu)造Socket

Socket的構(gòu)造方法主要有以下幾種重載形式:

  • Socket()
  • Socket(InetAddress address, int port) throws UnknowHostException, IOException
  • Socket(InetAddress address, int port, InetAddress localAddr, int localPort) throws IOException
  • Socket(String host, int port) throws UnknowHostException, IOException
  • Socket(String host, int port, InetAddress localAddr, int localPort) throws IOException

當(dāng)客戶端Socket構(gòu)造方法與服務(wù)器建立連接時(shí)官硝,需要等待一段時(shí)間矗蕊,默認(rèn)會(huì)一直等待下去短蜕,直到連接成功,或出現(xiàn)異常傻咖。受底層網(wǎng)絡(luò)傳輸速度等影響朋魔,連接可能處于長時(shí)間等待狀態(tài),可設(shè)置連接超時(shí)時(shí)間進(jìn)行限定卿操。

1.1警检、無參構(gòu)造方法

Socket()

使用無參構(gòu)造方法,創(chuàng)建一個(gè)無連接的Socket害淤,同時(shí)使用系統(tǒng)默認(rèn)的Socket實(shí)現(xiàn)扇雕;

Socket socket = new Socket();

SocketAddress remoteAddr = new InetSocketAddress("localhost",8000);

socket.connect(remoteAddr, 60000); //連接服務(wù)器并設(shè)置連接超時(shí)時(shí)間為1分鐘

1.2、設(shè)定服務(wù)器的地址

  • Socket(InetAddress address, int port) //參數(shù)address 表示主機(jī)的IP地址
  • Socket(String host, int port) //參數(shù)host 表示主機(jī)的名字窥摄,當(dāng)host為null時(shí)镶奉,連接本地回環(huán)地址;

InetAddress 類表示服務(wù)器的IP地址, InetAddress 類提供了一系列靜態(tài)工廠方法, 用于構(gòu)造自身的實(shí)例:

  • 返回本地主機(jī)的IP地址

InetAddress addr1 = InetAddress.getLocalHost();

  • 返回代表 "222.34.5.7"的 IP地址

InetAddress addr2 = InetAddress.getByName("222.34.5.7");

InetAddress addr3 = InetAddress.getByName("www.javathinker.org");

1.3崭放、設(shè)定客戶端地址

  • Socket(InetAddress address, int port, InetAddress localAddr, int localPort)throws IOException
  • Socket(String host, int port, InetAddress localAddr, int localPort) throws IOException

一個(gè)Socket中既包含服務(wù)器地址也包含客戶端地址腮鞍,默認(rèn)情況下,客戶端地址為客戶端所在主機(jī)的ip莹菱,端口為系統(tǒng)隨機(jī)分配的空閑端口移国。以上兩種構(gòu)造方法支持應(yīng)用顯式設(shè)置客戶端ip和端口;

2道伟、客戶端連接服務(wù)器的異常類

當(dāng)Socket的構(gòu)造方法連接服務(wù)器時(shí)迹缀,可能拋出如下異常:

  • UnknownHostException:如果無法識(shí)別主機(jī)的名字或IP地址,就會(huì)拋出此種異常蜜徽。
  • ConnectException:如果沒有服務(wù)器進(jìn)程監(jiān)聽指定的端口祝懂,或者服務(wù)器進(jìn)程拒絕連接,就會(huì)拋出這種異常拘鞋。
  • SocketTimeoutException:如果等待連接超時(shí)砚蓬,就會(huì)拋出這種異常。
  • BindException:如果無法把Socket對(duì)象與指定的本地IP地址或端口綁定盆色,就會(huì)拋出這種異常灰蛙。

類繼承關(guān)系圖

|---- IOException------- UnknownHostException

                    |---- InterruptedIOException ----------- SocketTimeoutException

                    |---- SocketException        ----------- BindException

                                                 |---------- ConnectException

3、獲取Socket信息

以下方法用于獲取Socket的有關(guān)信息:

  • getInetAddress():獲得連接的遠(yuǎn)程服務(wù)器的IP地址隔躲。
  • getRemoteSocketAddress():獲取連接的遠(yuǎn)程服務(wù)器地址摩梧;
  • getLocalSocketAddress():獲取本地綁定的地址;
  • getPort():獲得遠(yuǎn)程服務(wù)器的端口宣旱。
  • getLocalAddress():獲得客戶本地的IP地址仅父。
  • getLocalPort():獲得客戶本地的端口。
  • getInputStream():獲得輸入流。如果Socket還沒有連接笙纤,或者已經(jīng)關(guān)閉耗溜,或者已經(jīng)通過shutdownInput()方法關(guān)閉輸入流,那么此方法會(huì)拋出IOException省容。
  • getOutputStream():獲得輸出流强霎。如果Socket還沒有連接,或者已經(jīng)關(guān)閉蓉冈,或者已經(jīng)通過shutdownOutput()方法關(guān)閉輸出流,那么此方法會(huì)拋出IOException轩触。

4寞酿、關(guān)閉Socket

4.1 、Socket.close()

當(dāng)客戶與服務(wù)器的通信結(jié)束脱柱,應(yīng)該及時(shí)關(guān)閉Socket伐弹,以釋放Socket占用的包括端口在內(nèi)的各種資源。當(dāng)一個(gè)Socket對(duì)象被關(guān)閉榨为,就不能再通過它的輸入輸出流進(jìn)行I/O操作惨好,否則會(huì)導(dǎo)致IOException。Socket的close()方法負(fù)責(zé)關(guān)閉Socket随闺。推薦代碼如下:

Socket socket=null;
try{
  socket=new Socket("www.javathinker.org",80);
  //執(zhí)行接收和發(fā)送數(shù)據(jù)的操作
  …
}catch(IOException e){
  e.printStackTrace();
}finally{
  try{
    if(socket!=null)socket.close();
  }catch(IOException e){
    e.printStackTrace();
  }
}

4.2日川、Socket狀態(tài)

  • isClosed():如果Socket已經(jīng)連接到遠(yuǎn)程主機(jī),并且還沒有關(guān)閉矩乐,則返回true龄句,否則返回false;
  • isConnected():如果Socket曾經(jīng)連接到遠(yuǎn)程主機(jī)散罕,則返回true分歇,否則返回false。
  • isBound():如果Socket已經(jīng)與一個(gè)本地端口綁定欧漱,則返回true职抡,否則返回false。

如果要判斷一個(gè)Socket對(duì)象當(dāng)前是否處于連接狀態(tài):

boolean isConnected=socket.isConnected() && !socket.isClosed();

5误甚、半關(guān)閉Socket

有的時(shí)候缚甩,可能僅僅希望關(guān)閉輸出流或輸入流之一。此時(shí)可以采用Socket類提供的半關(guān)閉方法:

  • shutdownInput():關(guān)閉輸入流窑邦。
  • shutdownOutput():關(guān)閉輸出流蹄胰。

Socket類還提供了兩個(gè)狀態(tài)測(cè)試方法,用來判斷輸入流和輸出流是否關(guān)閉:

  • public boolean isInputShutdown()
  • public boolean isOutputShutdown()

6奕翔、Socket選項(xiàng)

Socket有以下選項(xiàng):

clipboard.png

6.1裕寨、TCP_NODELAY

設(shè)置該選項(xiàng):public void setTcpNoDelay(boolean on) throws SocketException //底層實(shí)現(xiàn)不支持,則拋出異常。
讀取該選項(xiàng):public boolean getTcpNoDelay() throws SocketException

默認(rèn)情況下宾袜,發(fā)送數(shù)據(jù)采用Negale算法捻艳。Negale算法是指發(fā)送方發(fā)送的數(shù)據(jù)不會(huì)立刻發(fā)出,而是先放在緩沖區(qū)內(nèi)庆猫,等待緩沖區(qū)滿了在發(fā)出认轨。 發(fā)送完一批數(shù)據(jù)后,會(huì)等待接收方對(duì)這批數(shù)據(jù)的回應(yīng)月培,然后在發(fā)送下一批數(shù)據(jù)嘁字。(適用于發(fā)送方需要發(fā)送大批量數(shù)據(jù),并且接收方會(huì)及時(shí)作出回應(yīng)的場(chǎng)合杉畜,而如果持續(xù)發(fā)送小批量數(shù)據(jù)纪蜒,且接收方不一定會(huì)看立即發(fā)送響應(yīng)數(shù)據(jù),則使發(fā)送方會(huì)運(yùn)行很慢此叠。)

TCP_NODELAY的默認(rèn)值為false纯续,表示使用Negale算法, 若 setTcpNoDelay(true)方法灭袁,就會(huì)關(guān)閉Socket的緩沖猬错,讓數(shù)據(jù)及時(shí)發(fā)送。

if( !socket.getTcpNoDelay() ){ socket.setTcpNoDelay(true); }

6.2茸歧、SO_REUSEADDR

設(shè)置該選項(xiàng):public void setResuseAddress(boolean on) throws SocektException
讀取該選項(xiàng):public boolean getResuseAddress() throws SocketException

當(dāng)接收方通過Socket的colse()方法關(guān)閉Socket時(shí)倦炒,如果網(wǎng)絡(luò)上還有發(fā)送到這個(gè)Socket的數(shù)據(jù),那么底層的Socket不會(huì)立刻釋放而本地端口软瞎,而是會(huì)等待一段時(shí)間析校,確保接收到了網(wǎng)絡(luò)上發(fā)送過來的延遲數(shù)據(jù),然后再釋放端口铜涉。但接收到延遲數(shù)據(jù)后智玻,不會(huì)對(duì)這些數(shù)據(jù)做任何處理,只是確保這些數(shù)據(jù)不會(huì)被其他碰巧綁定到同樣端口的新進(jìn)程接收到芙代。

而當(dāng)服務(wù)器程序關(guān)閉后吊奢,有可能它的端口還會(huì)被占用一段時(shí)間,如果此時(shí)立刻在同一個(gè)主機(jī)上重啟服務(wù)器程序纹烹,由于端口已經(jīng)被占用页滚,使得服務(wù)器程序無法綁定到該端口,啟動(dòng)失敗铺呵。

為使同一個(gè)主機(jī)上的其他進(jìn)程還可以立刻重用該端口裹驰,可以調(diào)用Socket的setReuseAddress(true);

if( !socket.getReuseAddress() ){ socket.setReuseAddress(true); }

但值得注意的是 socket.setReuseAddress(true)方法必須在Socket還沒有綁定到一個(gè)本地端口之前調(diào)用,否則執(zhí)行該方法就無效片挂。因此必須按下方式創(chuàng)建Socket對(duì)象幻林,在連接遠(yuǎn)程服務(wù)器贞盯。

Socket socket = new Socket(); //此時(shí)Socket對(duì)象未綁定本地端口,并且未連接遠(yuǎn)程服務(wù)器 socket.setReuseAddress(true); // 會(huì)拋出SocketException
// 如果不寫以下這句話沪饺,則客戶程序一般采用隨機(jī)端口躏敢,那么救護(hù)綁定匿名的本地端口。 SocketAddress localAddr = new InetSocketAddress("localhost",9999); SocketAddress remoteAddr = new InetSocketAddress("remotehost",8888);
socket.bind(localAddr); //與本地端口綁定
socket.connect(remoteAddr); // 連接遠(yuǎn)程服務(wù)器
//兩個(gè)共用同一個(gè)端口的進(jìn)程必須都調(diào)用該方法才能使得一個(gè)進(jìn)程關(guān)閉Socket后整葡,另一個(gè)進(jìn)程的Socket能夠立刻重用相同的端口件余。

6.3、SO_TIMEOUT

設(shè)置該選項(xiàng):public void setSoTimeout(int timeout) throws SocketException
讀取該選項(xiàng):public int getSoTimeOut() throws SocketException

當(dāng)通過Socket的輸入流讀數(shù)據(jù)時(shí)遭居,如果還沒有數(shù)據(jù)啼器,就會(huì)等待。例如:

byte[] buff = new byte[1024]; InputStream in = socket.getInputStream(); in.read(buff);

對(duì)于上述代碼俱萍,如果輸入流中沒有數(shù)據(jù)端壳,in.read(buff)就會(huì)等待對(duì)方發(fā)送數(shù)據(jù),知道滿足以下情況才結(jié)束等待:

  • 輸入流中有1024個(gè)字節(jié)鼠次,read()方法吧這些字節(jié)讀入到buff中,在返回讀取的字節(jié)數(shù)芋齿。

  • 當(dāng)寂靜塊接近輸入流的末尾腥寇,距離末尾還有小于1024個(gè)字節(jié)時(shí),read()方法會(huì)把這些字節(jié)讀入到buff中觅捆,在返回讀取的字節(jié)數(shù)赦役。

  • 已經(jīng)讀到輸入流的末尾,正常結(jié)束栅炒,返回-1.

  • 連接已經(jīng)斷開掂摔,拋出IOException。

  • 如果通過Socket的setSoTimeout()方法設(shè)置了等待超時(shí)時(shí)間赢赊,單位為毫秒乙漓,超過該時(shí)間后就拋出SocketTimeoutException。 它的默認(rèn)值為0释移,表示會(huì)無限等待叭披,永遠(yuǎn)不會(huì)超時(shí)。

public class ReceiveServer { 
    public static void main(String[] args) throws IOException,SocketException{    
        ServerSocket server = new ServerSocket(9999); 
        Socket s = server.accept(); s.setSoTimeout(1000*20); //設(shè)置超時(shí)時(shí)間為5秒     
        InputStream in = s.getInputStream(); 
        ByteArrayOutputStream buffer = new ByteArrayOutputStream(); 
        byte[] buff = new byte[1024]; 
        int length = -1; 
        do{ 
            try {
               length = in.read(buff); 
              if( length != -1 ){ 
              buffer.write( buff,0,length ); 
              } 
            } catch (SocketTimeoutException e) { 
                //e.printStackTrace(); 
                System.out.println("等待超時(shí)..."); 
            } 
        }while( length != -1 ); 
        System.out.println(   new String( buffer.toByteArray()) ); // 將字節(jié)數(shù)組轉(zhuǎn)換為字符串 
    } 
} ////////////////////////
 
public class SendClient { 
    public static void main(String[] args) throws Exception { 
        Socket socket = new Socket("localhost",9999); 
        OutputStream out = socket.getOutputStream(); 
        out.write( "Hello ".getBytes() ); 
        out.write("EveryOne".getBytes() ); 
        Thread.sleep(1000*60); socket.close(); 
    } 
}

6.4玩讳、SO_LINGER

設(shè)置該選項(xiàng):public void setSoLinger(boolean on,int seconds) throws SocketException // 注意第二個(gè)參數(shù)以秒為單位涩蜘,并非毫秒
讀取該選項(xiàng):public int getSoLinger() throws SocketException

SO_LINGER選項(xiàng)用來控制Socket關(guān)閉時(shí)的行為,默認(rèn)情況下熏纯,當(dāng)執(zhí)行Socket的close()方法同诫,該方法會(huì)立即返回,但底層的Socket實(shí)際上并不立即關(guān)閉樟澜,它會(huì)延遲一段時(shí)間知道發(fā)送完所有剩余的數(shù)據(jù)误窖,才會(huì)真正關(guān)閉Socket叮盘,才斷開連接。

但對(duì)于以下情況:

socket.setSoLinger(true,0);

//該方法也會(huì)立即返回贩猎,但底層的Socket也會(huì)立即關(guān)閉熊户,所有未發(fā)送完的剩余數(shù)據(jù)被丟棄。 //還在網(wǎng)絡(luò)上傳輸吭服,但是未被接收方接收的數(shù)據(jù)嚷堡, socket.setSoLinger(true,1000); // 而該方法不會(huì)立即返回,而是進(jìn)入阻塞狀態(tài)艇棕。 只有當(dāng)?shù)讓拥腟oket發(fā)送完所有的剩余數(shù)據(jù)或阻塞時(shí)間已經(jīng)超過了1000秒蝌戒,也回返回,但是剩余未發(fā)送的數(shù)據(jù)被丟棄沼琉。

//對(duì)于該選項(xiàng)北苟,嘗試著讓服務(wù)器端先睡眠一會(huì),再開始接受數(shù)據(jù):

服務(wù)器端程序:

public class SimpleServer { 
    public static void main(String[] args) throws Exception { 
        ServerSocket server = new ServerSocket(7777); 
        Socket socket = server.accept(); 
        System.out.println("服務(wù)器困了打瘪,于是決定休息會(huì)..."); 
        Thread.sleep( 1000*10 );   // 睡眠10秒友鼻。 
        System.out.println("服務(wù)器終于睡醒了,然后開始接受數(shù)據(jù):"); 
        InputStream in  = socket.getInputStream(); 
        ByteArrayOutputStream buffer = new ByteArrayOutputStream(); 
        byte[] buff = new byte[1024]; 
        int length = -1; 
        while( ( length = in.read(buff) ) != -1){ 
            buffer.write(buff,0,length); 
        } 
        System.out.println( new String( buffer.toByteArray() )); //把字節(jié)數(shù)組轉(zhuǎn)換成字符串。 
    } 
} //客戶端程序: 

public class SimpleClient { 
    public static void main(String[] args) throws Exception { 
        Socket socket = new Socket("localhost",7777); //socket.setSoLinger(true, 0);    
        //(1)  該條件下闺骚,會(huì)強(qiáng)行關(guān)閉底層的Socket彩扔,導(dǎo)致所有未發(fā)送數(shù)據(jù)丟失,而服務(wù)器算接受方睡醒后接受數(shù)據(jù)僻爽,由于底層Socket關(guān)閉虫碉,則會(huì)拋出SocketException: Connection reset. //socket.setSoLinger(true, 300);  
        //(2) 注意Socket關(guān)閉時(shí)間,實(shí)際上會(huì)等待一段時(shí)間在關(guān)閉 
        OutputStream out = socket.getOutputStream(); 
        StringBuffer sb = new StringBuffer(); 
        String str = "關(guān)于對(duì)setSoLinger選項(xiàng)的設(shè)置:"; 
        sb.append(  str ); 
        for( int i=1;i<=10000;i++){  
            // 需注意jvm堆棧的設(shè)置。 
            sb.append( i ); }   
            System.out.println( sb ); 
            out.write( sb.toString().getBytes() ); 
            System.out.println("開始關(guān)閉Socket: "); 
            long start = System.currentTimeMillis(); 
            socket.close();  // 注意不同條件下(1)與(2)的關(guān)閉的時(shí)間 
            long end = System.currentTimeMillis(); 
            System.out.println("關(guān)閉Socket所用時(shí)間為: " + (end - start ) +"ms" ); 
        } 
}

6.5.SO_RCVBUF選項(xiàng):

該選項(xiàng)用于 輸入數(shù)據(jù) 的緩沖區(qū)的大小。

設(shè)置該選項(xiàng):public void setReceiveBufferSize(int size) throws SocketException //
讀取該選項(xiàng):public int getReceiveBufferSize() throws SocketException

6.6.SO_SNDBUF選項(xiàng):

該選項(xiàng)用于 輸出數(shù)據(jù)的緩沖區(qū)的大小吃谣。

設(shè)置該選項(xiàng):* public void setSendBufferSize(int size) throws SocketException; //
讀取該選項(xiàng): public void getSendBufferSize() throws SocketException;

public class SocketBufferDemo { 
  public static void main(String[] args) throws SocketException { 
    Socket socket = new Socket();//不帶參的構(gòu)造方法,不會(huì)試圖建立與服務(wù)器端的連接. 
    //默認(rèn)情況下的輸入輸出緩沖區(qū)的大芯ぢ选:
    int rcvbuf = socket.getReceiveBufferSize();  // 輸入緩沖區(qū)大小 
    int sndbuf = socket.getSendBufferSize();  //輸出緩沖區(qū)大小 
    System.out.println( rcvbuf  + "\t" + sndbuf ); //重新設(shè)置輸入輸出緩沖區(qū)的大小再輸出結(jié)果:
    socket.setReceiveBufferSize( 1024*32 ); 
    socket.setSendBufferSize( 1024*32 );   
    System.out.println( socket.getReceiveBufferSize()+"\t"+socket.getSendBufferSize() ); 
  } 
}

6.7.SO_KEEPALIVE選項(xiàng):

設(shè)置該選項(xiàng): public void setKeepAlive(boolean on) throws SocketException //
讀取該選項(xiàng): public int getKeepAlive() throws SocketException

當(dāng)該選項(xiàng)為true時(shí),表示底層的TCP實(shí)現(xiàn)會(huì)監(jiān)視該連接是否有效绪颖。當(dāng)連接處于空閑狀態(tài)(即連接的兩端沒有互相傳送數(shù)據(jù))超過2H济蝉,本地的TCP實(shí)現(xiàn)會(huì)發(fā)送一個(gè)數(shù)據(jù)包給遠(yuǎn)程的Socket。如果遠(yuǎn)程Socket沒有發(fā)回響應(yīng)菠发,TCP實(shí)現(xiàn)就會(huì)持續(xù)嘗試11分鐘王滤,直到接收到響應(yīng)為止。如果在12分鐘內(nèi)未收到響應(yīng)滓鸠,TCP實(shí)現(xiàn)就會(huì)自動(dòng)關(guān)閉本地Socket雁乡,斷開連接。(在不同的網(wǎng)絡(luò)平臺(tái)上糜俗,TCP實(shí)現(xiàn)嘗試與遠(yuǎn)程Socket對(duì)話的時(shí)限會(huì)有所差別)踱稍。

該選項(xiàng)默認(rèn)值為 false曲饱,即表示TCP不會(huì)監(jiān)視連接是否有效,不活動(dòng)的客戶端可能會(huì)永久存在下去珠月,而不會(huì)注意到服務(wù)器已經(jīng)崩潰扩淀。

對(duì)于KeepAlive這種系統(tǒng)底層的機(jī)制(用于系統(tǒng)維護(hù)每一個(gè)tcp連接的),可在認(rèn)識(shí)到另一種新的概念,即心跳線程:

鏈接網(wǎng)址:

[http://code.taobao.org/p/tfs/wiki/dataserver/background/] <心跳線程簡(jiǎn)單介紹>
[http://blog.csdn.net/xuyuefei1988/article/details/8279812] <關(guān)于心跳包機(jī)制>
[http://blog.sina.com.cn/s/blog_616e189f0100s3px.html] <這篇也不錯(cuò)啤挎,Socket緩沖區(qū)探討>

6.8.OOBINLINE選項(xiàng):

設(shè)置該選項(xiàng): public void setOOBInline(boolean on ) throws SocketException //
讀取該選項(xiàng): public void getOOBInline() throws SocketException public void sendUrgentData(int data) throws IOException //雖然sendUrgentData的參數(shù)data是int類型驻谆,但只有這個(gè)int類型的低字節(jié)被發(fā)送(即一個(gè)字節(jié),8位)庆聘,其它的三個(gè)字節(jié)被忽略胜臊。

該選項(xiàng)為true時(shí),表示支持發(fā)送一個(gè)字節(jié)的TCP緊急數(shù)據(jù)伙判。Socket類的sendUrgentData( int data ) 方法用于發(fā)送一個(gè)字節(jié)(8位)的TCP緊急數(shù)據(jù)象对。

而為false時(shí),表示當(dāng)接收方收到緊急數(shù)據(jù)時(shí)不作任何處理宴抚,直接丟棄勒魔。

但是問題是接收方會(huì)把接收到的緊急數(shù)據(jù)與普通數(shù)據(jù)放在同樣的隊(duì)列中,除非使用一些更高層次的協(xié)議菇曲,否則接收方處理緊急數(shù)據(jù)的能力非常有限冠绢,當(dāng)緊急數(shù)據(jù)到來時(shí),接收方不會(huì)得到任何的通知羊娃,因此接收方很難區(qū)分普通數(shù)據(jù)與緊急數(shù)據(jù)唐全,只好按同樣的方式處理他們埃跷。

服務(wù)端代碼:

public class OOBInlineServer { 
  public static void main(String[] args) throws Exception {      
    ServerSocket serverSocket = new ServerSocket(7777);             
    System.out.println("服務(wù)器已經(jīng)啟動(dòng)蕊玷,端口號(hào):7777");             
    while (true){                 
      Socket socket = serverSocket.accept();   // 須在服務(wù)端中也設(shè)置為true,否則無法接受到客戶端發(fā)送過來的緊急數(shù)據(jù):                 
      socket.setOOBInline(true);                  
      InputStream in = socket.getInputStream();     
      InputStreamReader inReader = new InputStreamReader(in);     
      BufferedReader bReader = new BufferedReader(inReader);   
      System.out.println(bReader.readLine());                 
      System.out.println(bReader.readLine());                 
      socket.close();             
    } 
  }
} //
**客戶端代碼:**
public class OOBInlineClient { 
  public static void main(String[] args) throws Exception {     
    Socket socket = new Socket("localhost", 7777);             
    socket.setOOBInline(true);             
    OutputStream out = socket.getOutputStream(); 
    OutputStreamWriter outWriter = new OutputStreamWriter(out);             
    outWriter.write(67);              // 向服務(wù)器發(fā)送字符"C"             
    outWriter.write(" Hello World\r\n ");             
    socket.sendUrgentData(65);        // "A"             
    socket.sendUrgentData(68);        // "D"             
    outWriter.flush();             
    socket.sendUrgentData(322);        // "B"  322分布在兩個(gè)字節(jié)上弥雹,但是其低位為:0100 0010 即剛好跟 66 一樣垃帅,             
    socket.close(); 
  } 
}

猜測(cè)下上述輸出結(jié)果: 猜測(cè)可能為:

C Hello World

A D B

但正常結(jié)果輸出為:

[圖片上傳失敗...(image-22cb57-1538206377829)]

由此可見:使用sendUrgentData()方法發(fā)送數(shù)據(jù)后,系統(tǒng)會(huì)立即將這些數(shù)據(jù)發(fā)送出去剪勿,而使用write()(首先是將數(shù)據(jù)存放在了緩沖區(qū))發(fā)送數(shù)據(jù)贸诚,必須要使用flush()方法才會(huì)真正發(fā)送數(shù)據(jù)。

另外注意的是:在使用 setOOBInline()方法時(shí)厕吉,要注意必須在客戶端和服務(wù)端程序同時(shí)使用該方法酱固,打開SO_OOBInline選項(xiàng),否則無法命名sendUrgentData來發(fā)送數(shù)據(jù)头朱。

相關(guān)閱讀:
ServerSocket詳解 【http://www.reibang.com/p/665994c2e784

參考博客:

https://my.oschina.net/gently/blog/531117

https://blog.csdn.net/swartz_lubel/article/details/79574472

https://blog.csdn.net/LastDays_L/article/details/48004547

http://elf8848.iteye.com/blog/1961194

參考書籍:

孫衛(wèi)琴 《java網(wǎng)絡(luò)編程精解》

代碼示例:

https://github.com/zhaozhou11/java-io.git
(com.zhaozhou.demo.socket包)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末运悲,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子项钮,更是在濱河造成了極大的恐慌班眯,老刑警劉巖希停,帶你破解...
    沈念sama閱讀 211,265評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異署隘,居然都是意外死亡宠能,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,078評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門磁餐,熙熙樓的掌柜王于貴愁眉苦臉地迎上來违崇,“玉大人,你說我怎么就攤上這事崖媚∫嗲福” “怎么了?”我有些...
    開封第一講書人閱讀 156,852評(píng)論 0 347
  • 文/不壞的土叔 我叫張陵畅哑,是天一觀的道長肴楷。 經(jīng)常有香客問我,道長荠呐,這世上最難降的妖魔是什么赛蔫? 我笑而不...
    開封第一講書人閱讀 56,408評(píng)論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮泥张,結(jié)果婚禮上呵恢,老公的妹妹穿的比我還像新娘。我一直安慰自己媚创,他們只是感情好渗钉,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,445評(píng)論 5 384
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著钞钙,像睡著了一般鳄橘。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上芒炼,一...
    開封第一講書人閱讀 49,772評(píng)論 1 290
  • 那天瘫怜,我揣著相機(jī)與錄音,去河邊找鬼本刽。 笑死鲸湃,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的子寓。 我是一名探鬼主播暗挑,決...
    沈念sama閱讀 38,921評(píng)論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼斜友!你這毒婦竟也來了炸裆?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,688評(píng)論 0 266
  • 序言:老撾萬榮一對(duì)情侶失蹤蝙寨,失蹤者是張志新(化名)和其女友劉穎晒衩,沒想到半個(gè)月后嗤瞎,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,130評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡听系,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,467評(píng)論 2 325
  • 正文 我和宋清朗相戀三年贝奇,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片靠胜。...
    茶點(diǎn)故事閱讀 38,617評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡掉瞳,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出浪漠,到底是詐尸還是另有隱情陕习,我是刑警寧澤,帶...
    沈念sama閱讀 34,276評(píng)論 4 329
  • 正文 年R本政府宣布址愿,位于F島的核電站该镣,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏响谓。R本人自食惡果不足惜损合,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,882評(píng)論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望娘纷。 院中可真熱鬧嫁审,春花似錦、人聲如沸赖晶。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,740評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽遏插。三九已至捂贿,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間涩堤,已是汗流浹背眷蜓。 一陣腳步聲響...
    開封第一講書人閱讀 31,967評(píng)論 1 265
  • 我被黑心中介騙來泰國打工分瘾, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留胎围,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,315評(píng)論 2 360
  • 正文 我出身青樓德召,卻偏偏與公主長得像白魂,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子上岗,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,486評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容

  • Socket的作用 在Internet上福荸,數(shù)據(jù)按有限大小的包傳輸,這些包稱為數(shù)據(jù)報(bào)肴掷。每個(gè)數(shù)據(jù)報(bào)包含一個(gè)首部和主體敬锐。...
    Samuel_Tom閱讀 2,819評(píng)論 1 1
  • 7.2 面向套接字編程我們已經(jīng)通過了解Socket的接口背传,知其所以然,下面我們就將通過具體的案例台夺,來熟悉Socke...
    lucas777閱讀 1,173評(píng)論 0 2
  • 這一塊屬于網(wǎng)絡(luò)編程径玖,主要是學(xué)習(xí)TCP/IP四層的網(wǎng)絡(luò)體系結(jié)構(gòu),學(xué)習(xí)TCP編程和UDP編程颤介。 java.net中 一...
    取名廢同學(xué)閱讀 643評(píng)論 0 0
  • 對(duì)于即時(shí)類應(yīng)用或者即時(shí)類的游戲梳星,HTTP協(xié)議很多時(shí)候無法滿足于我們的需求。這會(huì)滚朵,Socket對(duì)于我們來說就非常實(shí)用...
    育樹凌峰閱讀 1,989評(píng)論 0 1
  • socket詳解:https://blog.csdn.net/hdfqq188816190/article/det...
    wvqusrtg閱讀 1,732評(píng)論 0 10