BIO TCP/IP協(xié)議 網(wǎng)絡通信

解釋:BIO → Block input output(阻塞式的輸入和輸出)
使用場景:一般應用于傳統(tǒng)的網(wǎng)絡通信型宝;請求 (客戶端)臭挽、響應 (服務端)兩方捂襟,約定好使用TCP/IP通信;雙方約定好報文格式 (報文頭+報文體)及編碼格式 (這里用UTF-8)欢峰,報文頭內(nèi)容 (約定好長度比如8位葬荷,不夠前面補零)里面內(nèi)容為報文體長度,再根據(jù)報文頭內(nèi)容纽帖,獲取后面的報問體的內(nèi)容宠漩。
例如:報文示例: 00000007aaaaaaa;報文體內(nèi)容為7個a懊直,所以報文頭長度為7不夠八位前面補零扒吁。

一、 作為服務端

SocketServerThread: 用來監(jiān)聽端口的服務類
ThreadOperate:處理每一個請求的新線程

/**
 *  serverSocket服務 
 *  
 *  此線程只負責接收客戶端請求室囊,接收到以后再創(chuàng)造新的線程去處理
 * @author zhb
 *
 */
public class SocketServerThread extends Thread{
    // 此服務端線程只會在啟動時創(chuàng)建一個
    private ServerSocket serversocket;

    /**
     * 創(chuàng)建服務端線程  
     * @param serversocket 服務端的一個socket
     * @throws IOException
     */
    public SocketServerThread(ServerSocket serversocket) throws IOException{
        if(serversocket==null){
            this.serversocket = new ServerSocket(Constant.serverSocketPort);
        }
    }

    // 線程運行體
    @Override
    public void run(){
        // 如果此服務線程沒有被中斷
        while(!this.isInterrupted()){           
            try {   
                System.out.println("服務線程開啟成功雕崩,正在等待客戶端的請求");
                // 服務線程一直處于阻塞狀態(tài),直到有客戶端請求
                Socket socket = serversocket.accept();
                System.err.println("請求來了");
                // 設置接收超時的時間 單位毫秒
                socket.setSoTimeout(Constant.reqTimeOut);   
                if(socket != null && !socket.isClosed()){
                    // 創(chuàng)建一個新線程去處理請求信息
                    new Thread(new ThreadOperate(socket)).start();
                }
            } catch (IOException e) {
                System.err.println("服務線程出現(xiàn)異常:" + e.getMessage());
            } 
        }   
    }

    // 開啟線程主函數(shù)
    public static void main(String[] args) throws IOException {
        new SocketServerThread(null).start();
    }

}
/**
 * 處理每一個請求開的新的線程
 * @author zhb
 */
public class ThreadOperate implements Runnable {

    // 處理某個客戶端請求的socket
    private Socket socket;
    public ThreadOperate(Socket socket){
        this.socket = socket;
    }
        
    public void run() {
        InputStream in = null;
        OutputStream out = null;    
        System.err.println("處理客戶請求的新線程開啟成功");
        try {
            in = socket.getInputStream();
            out = socket.getOutputStream();
            // 存放報文頭的字節(jié)數(shù)組
            byte[] reqHeadByte = new byte[Constant.reqHeadLength];
            //讀取報文頭內(nèi)容
            in.read(reqHeadByte);
            String reqHeadStr = new String(reqHeadByte, Constant.charset);

            //報文頭內(nèi)容是報問體的長度
            int reqBodylength = Integer.valueOf(reqHeadStr);
            
            // 創(chuàng)建容納報文體的字節(jié)數(shù)組
            byte[] bodyByte = new byte[reqBodylength];
            in.read(bodyByte);
            
            // 將報問體數(shù)組轉(zhuǎn)成報文字符串
            String reqBodyStr = new String(bodyByte, Constant.charset);
            System.err.print("接受報文內(nèi)容為:" + reqBodyStr);
            
            // 這里要處理接收到的請求報文內(nèi)容reqBodyStr融撞,完后返回處理結(jié)果
            
            //返回響應內(nèi)容體
            String resBodyStr = "這里是響應內(nèi)容→ "+ reqBodyStr;
            //返回響應內(nèi)容的字節(jié)數(shù)組
            byte[] respBodyByte = resBodyStr.getBytes(Constant.charset);
            
            //轉(zhuǎn)成約定好的報文頭長度盼铁,不足的前面補零
            StringBuilder format = new StringBuilder("%0").append(Constant.respHeadLength).append("d");
            byte[] respHeadByte = String.format(format.toString(), respBodyByte.length).getBytes();
            
            // 輸出報文頭信息
            out.write(respHeadByte);
            // 輸出報文體內(nèi)容
            out.write(respBodyByte);
            // 發(fā)送響應信息
            out.flush();
            
        } catch (IOException e) {
            System.err.println("讀取報文內(nèi)容報異常::" + e.getMessage());
        }finally{   
            //線程結(jié)束后的處理
            endHandler(in, out);
        }
    }
    
    /**
     * 線程結(jié)束后的處理
     * @param in
     * @param out
     */
    private void endHandler(InputStream in, OutputStream out) { 
        if(in != null){
            try {
                in.close();
            } catch (IOException e) {
                System.err.println("關(guān)閉輸入流出現(xiàn)異常:" + e.getMessage());
            }
        }   
        if(out != null){
            try {
                out.close();
            } catch (IOException e) {
                System.err.println("關(guān)閉輸出流出現(xiàn)異常:" + e.getMessage());
            }
        }
        if(socket != null){
            try {
                socket.close();
            } catch (IOException e) {
                System.err.println("關(guān)閉線程出現(xiàn)異常:" + e.getMessage());
            }
        }
    }


}

一、 作為客戶端

ClientTest : 模擬客戶端請求開啟線程
ClientThread:具體的某個客戶端線程的請求和響應
Constant : 服務端和客戶端都用到的常量參數(shù)

/**
 *  測試客戶端發(fā)送請求
 * @author zhb
 */
public class ClientTest {

    // 用于測試并發(fā)用
    private static CountDownLatch cdl = new CountDownLatch(1);
    
    public static void main(String[] args) throws IOException, InterruptedException {
        
        for(int i=0; i < 10 ; i++){
            Socket socket = new Socket("localhost", Constant.serverSocketPort);
            new Thread(new ClientThread(socket, i, cdl)).start();
        }
        Thread.sleep(3000);
        cdl.countDown();
    }
    
}


/**
 * 客戶端發(fā)送請求線程懦铺,并接受服務器響應信息 
 * @author zhb
 *
 */
public class ClientThread implements Runnable {
    
    // 客戶端的socket
    private Socket socket;
    // 用于標記第幾個請求
    private int i;
    // 用于模擬 客戶端的并發(fā)
    private CountDownLatch cdl;

    public ClientThread(Socket socket) {
        this.socket = socket;
    }   
    
    public ClientThread(Socket socket, int i) {
        this.socket = socket;
        this.i =  i;    
    }

    public ClientThread(Socket socket, int i, CountDownLatch cdl) {
        this.socket = socket;
        this.i =  i;
        this.cdl = cdl;
    }

    public void run() {
    
        try {
            //并發(fā)測試信息號
            cdl.await();
        } catch (InterruptedException e1) {
            e1.printStackTrace();
        }       
        
        OutputStream out = null;
        InputStream in = null;
        try {   
            // 客戶端請求信息
            clientReqSend(out);
            // 接收服務端返回的信息
            clientRespReceive(in);  
    
        } catch (IOException e) {
            e.printStackTrace();
        }finally{   
            //線程結(jié)束后的處理
            endHandler(in, out);
        }
    }

    /**
     * 客戶端發(fā)送請求信息
     * 
     * @param out
     * @throws IOException
     * @throws UnsupportedEncodingException
     */
    private void clientReqSend(OutputStream out) throws IOException, UnsupportedEncodingException {
        out = socket.getOutputStream();
        String reqBodyStr = "測試請求內(nèi)容"+String.valueOf(i);
        System.err.println(reqBodyStr);
        // 請求報文體 字節(jié)數(shù)組
        byte[] reqBodyByte = reqBodyStr.getBytes(Constant.charset);
        // 請求報文頭字節(jié)數(shù)組捉貌,不夠位數(shù)前面補零
        StringBuilder format = new StringBuilder("%0").append(Constant.respHeadLength).append("d");
        byte[] reqHeadByte = String.format(format.toString(), reqBodyByte.length).getBytes(Constant.charset);
        // 輸出報文頭內(nèi)容
        out.write(reqHeadByte);
        // 輸出報文體內(nèi)容
        out.write(reqBodyByte);
        // 發(fā)送
        out.flush();
    }   

    /**
     * 客戶端接收響應信息
     * 
     * @param in
     * @throws IOException
     * @throws UnsupportedEncodingException
     */
    private void clientRespReceive(InputStream in) throws IOException, UnsupportedEncodingException {
        
        in = socket.getInputStream();       
        // 存放報文頭的字節(jié)數(shù)組
        byte[] reqHeadByte = new byte[Constant.reqHeadLength];
        //讀取報文頭內(nèi)容
        in.read(reqHeadByte);
        String reqHeadStr = new String(reqHeadByte, Constant.charset);
        
        //報文頭內(nèi)容是報問體的長度
        int reqBodylength = Integer.valueOf(reqHeadStr);
        
        // 創(chuàng)建容納報文體的字節(jié)數(shù)組
        byte[] bodyByte = new byte[reqBodylength];
        
        int off = 0;
        int nReadLength = 0;
        //循環(huán)讀取直到讀取到報文體長度
        while(off < reqBodylength){
            nReadLength = in.read(bodyByte, off, reqBodylength - off);
            if(nReadLength > 0){
                off = off + nReadLength;
            }else{
                break;
            }
        }
        
        // 將報問體數(shù)組轉(zhuǎn)成報文字符串
        String reqBodyStr = new String(bodyByte, Constant.charset);
        System.err.println("接受服務端的請求信息內(nèi)容為:" +i+ reqBodyStr);
    }
    
    /**
     * 線程結(jié)束后的處理
     * @param in
     * @param out
     */
    private void endHandler(InputStream in, OutputStream out) { 
        if(in != null){
            try {
                in.close();
            } catch (IOException e) {
                System.err.println("關(guān)閉輸入流出現(xiàn)異常:" + e.getMessage());
            }
        }
        if(out != null){
            try {
                out.close();
            } catch (IOException e) {
                System.err.println("關(guān)閉輸出流出現(xiàn)異常:" + e.getMessage());
            }
        }   
        if(socket != null){
            try {
                socket.close();
            } catch (IOException e) {
                System.err.println("關(guān)閉線程出現(xiàn)異常:" + e.getMessage());
            }
        }
    }


}
/**
 * 系統(tǒng)常量
 * 
 * @author zhb
 *
 */
public class Constant {
    
    // 請求報文頭的位數(shù)
    public static final int reqHeadLength = 8;
    // 返回響應報文頭的長度
    public static final int resHeadLength = 8;  
    // 字節(jié)數(shù)組和字符創(chuàng)之間的轉(zhuǎn)換時的編碼 
    public static final String charset = "UTF-8";
    //接收請求的最長時間 單位毫秒
    public static final int reqTimeOut = 5000;
    //接收請求的服務端口
    public static final int serverSocketPort = 8080;    
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市冬念,隨后出現(xiàn)的幾起案子趁窃,更是在濱河造成了極大的恐慌,老刑警劉巖急前,帶你破解...
    沈念sama閱讀 218,525評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件醒陆,死亡現(xiàn)場離奇詭異,居然都是意外死亡裆针,警方通過查閱死者的電腦和手機刨摩,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,203評論 3 395
  • 文/潘曉璐 我一進店門寺晌,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人澡刹,你說我怎么就攤上這事呻征。” “怎么了罢浇?”我有些...
    開封第一講書人閱讀 164,862評論 0 354
  • 文/不壞的土叔 我叫張陵陆赋,是天一觀的道長。 經(jīng)常有香客問我嚷闭,道長攒岛,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,728評論 1 294
  • 正文 為了忘掉前任胞锰,我火速辦了婚禮灾锯,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘嗅榕。我一直安慰自己顺饮,他們只是感情好,可當我...
    茶點故事閱讀 67,743評論 6 392
  • 文/花漫 我一把揭開白布誊册。 她就那樣靜靜地躺著领突,像睡著了一般暖璧。 火紅的嫁衣襯著肌膚如雪案怯。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,590評論 1 305
  • 那天澎办,我揣著相機與錄音嘲碱,去河邊找鬼。 笑死局蚀,一個胖子當著我的面吹牛麦锯,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播琅绅,決...
    沈念sama閱讀 40,330評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼扶欣,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了千扶?” 一聲冷哼從身側(cè)響起料祠,我...
    開封第一講書人閱讀 39,244評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎澎羞,沒想到半個月后髓绽,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,693評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡妆绞,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,885評論 3 336
  • 正文 我和宋清朗相戀三年顺呕,在試婚紗的時候發(fā)現(xiàn)自己被綠了枫攀。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,001評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡株茶,死狀恐怖来涨,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情启盛,我是刑警寧澤扫夜,帶...
    沈念sama閱讀 35,723評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站驰徊,受9級特大地震影響笤闯,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜棍厂,卻給世界環(huán)境...
    茶點故事閱讀 41,343評論 3 330
  • 文/蒙蒙 一颗味、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧牺弹,春花似錦浦马、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,919評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至航攒,卻和暖如春磺陡,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背漠畜。 一陣腳步聲響...
    開封第一講書人閱讀 33,042評論 1 270
  • 我被黑心中介騙來泰國打工币他, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人憔狞。 一個月前我還...
    沈念sama閱讀 48,191評論 3 370
  • 正文 我出身青樓蝴悉,卻偏偏與公主長得像,于是被迫代替她去往敵國和親瘾敢。 傳聞我的和親對象是個殘疾皇子拍冠,可洞房花燭夜當晚...
    茶點故事閱讀 44,955評論 2 355

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