Java基礎(chǔ)學(xué)習(xí)第二十六天——網(wǎng)絡(luò)編程總結(jié)

文檔版本 開發(fā)工具 測試平臺(tái) 工程名字 日期 作者 備注
V1.0 2016.04.03 lutianfei none

[TOC]


第十二章網(wǎng)絡(luò)編程

網(wǎng)絡(luò)編程概述

  • 就是用來實(shí)現(xiàn)網(wǎng)絡(luò)互連的不同計(jì)算機(jī)上運(yùn)行的程序間可以進(jìn)行數(shù)據(jù)交換。

網(wǎng)絡(luò)模型

  • 計(jì)算機(jī)網(wǎng)絡(luò)之間以何種規(guī)則進(jìn)行通信诵姜,就是網(wǎng)絡(luò)模型研究問題。
  • 網(wǎng)絡(luò)模型一般是指
    • OSI(Open System Interconnection開放系統(tǒng)互連)參考模型
    • TCP/IP參考模型

網(wǎng)絡(luò)模型7層概述

  • 1 . 物理層:主要定義物理設(shè)備標(biāo)準(zhǔn)蚌吸,如網(wǎng)線的接口類型赶舆、光纖的接口類型、各種傳輸介質(zhì)的傳輸速率等焙格。它的主要作用是傳輸比特流。這一層的數(shù)據(jù)叫做比特己英。

  • 2 . 數(shù)據(jù)鏈路層:主要將從物理層接收的數(shù)據(jù)進(jìn)行MAC地址(網(wǎng)卡的地址)的封裝解封裝间螟。常把這一層的數(shù)據(jù)叫做。在這一層工作的設(shè)備是交換機(jī)损肛,數(shù)據(jù)通過交換機(jī)來傳輸厢破。

  • 3 . 網(wǎng)絡(luò)層:主要將從下層接收到的數(shù)據(jù)進(jìn)行IP地址封裝解封裝。在這一層工作的設(shè)備是路由器治拿,常把這一層的數(shù)據(jù)叫做數(shù)據(jù)包摩泪。

  • 4 . 傳輸層:定義了一些傳輸數(shù)據(jù)的協(xié)議端口號(hào)。 主要是將從下層接收的數(shù)據(jù)進(jìn)行分段傳輸劫谅,到達(dá)目的地址后再進(jìn)行重組见坑。常常把這一層數(shù)據(jù)叫做嚷掠。

    • TCP:傳輸控制協(xié)議,傳輸效率低荞驴,可靠性強(qiáng)不皆,用于傳輸可靠性要求高,數(shù)據(jù)量大的數(shù)據(jù)熊楼。
    • UDP:用戶數(shù)據(jù)包協(xié)議霹娄,與TCP特性恰恰相反,用于傳輸可靠性要求不高鲫骗,數(shù)據(jù)量小的數(shù)據(jù)犬耻。
      • 如QQ聊天數(shù)據(jù)就是通過這種方式傳輸?shù)摹?/li>
  • 5 . 會(huì)話層:通過傳輸層建立數(shù)據(jù)傳輸?shù)耐贰V饕谀愕南到y(tǒng)之間發(fā)起會(huì)話或者接受會(huì)話請求(設(shè)備之間需要互相認(rèn)識(shí)可以是IP也可以是MAC或者是主機(jī)名)执泰。

  • 6 . 表示層:主要是進(jìn)行對接收的數(shù)據(jù)進(jìn)行解釋枕磁、加密解密壓縮解壓縮等(把計(jì)算機(jī)能夠識(shí)別的東西轉(zhuǎn)換成人能夠能識(shí)別的東西(如圖片术吝、聲音等)计济。

  • 7 . 應(yīng)用層: 主要是一些終端的應(yīng)用,比如說FTP(各種文件下載)顿苇,WEB(IE瀏覽)峭咒,QQ之類的(可以把它理解成我們在電腦屏幕上可以看到的東西.就是終端應(yīng)用)。

網(wǎng)絡(luò)通信三要素

  • IP地址:InetAddress
    • 網(wǎng)絡(luò)中設(shè)備的標(biāo)識(shí)纪岁,不易記憶,可用主機(jī)名
  • 端口號(hào)
    • 用于標(biāo)識(shí)進(jìn)程的邏輯地址则果,不同進(jìn)程的標(biāo)識(shí)
  • 傳輸協(xié)議
    • 通訊的規(guī)則
    • 常見協(xié)議:TCP幔翰,UDP

網(wǎng)絡(luò)通信模型

  • 第一個(gè)條件:我要先找到你 (IP)
  • 第二個(gè)條件:你得有接收數(shù)據(jù)的地方 耳朵 (端口)
  • 第三個(gè)條件:我跟你說話,你能接收到,咱按什么方式接收啊,我說英文你懂嗎,說韓文你懂嗎,不懂是吧,所以我還是說中文把.(協(xié)議)

IP地址

  • 要想讓網(wǎng)絡(luò)中的計(jì)算機(jī)能夠互相通信,必須為每臺(tái)計(jì)算機(jī)指定一個(gè)標(biāo)識(shí)號(hào)西壮,通過這個(gè)標(biāo)識(shí)號(hào)來指定要接受數(shù)據(jù)的計(jì)算機(jī)和識(shí)別發(fā)送的計(jì)算機(jī)遗增,在TCP/IP協(xié)議中,這個(gè)標(biāo)識(shí)號(hào)就是IP地址款青。

  • 如何獲取和操作IP地址呢做修?

    • 為了方便我們對IP地址的獲取和操作,java提供了一個(gè)類InetAddress 抡草。
  • 所謂IP地址就是給每個(gè)連接在Internet上的主機(jī)分配的一個(gè)32bit地址饰及。按照TCP/IP規(guī)定,IP地址用二進(jìn)制來表示康震,每個(gè)IP地址長32bit燎含,比特?fù)Q算成字節(jié),就是4個(gè)字節(jié)腿短。IP地址的這種表示法叫做“點(diǎn)分十進(jìn)制表示法”屏箍,這顯然比1和0容易記憶得多绘梦。

IP地址的組成
  • IP地址 = 網(wǎng)絡(luò)號(hào)碼+主機(jī)地址

  • A類IP地址 : 第一段號(hào)碼為網(wǎng)絡(luò)號(hào)碼剩下的三段號(hào)碼為本地計(jì)算機(jī)的號(hào)碼

  • B類IP地址:前二段號(hào)碼為網(wǎng)絡(luò)號(hào)碼赴魁,剩下的二段號(hào)碼為本地計(jì)算機(jī)的號(hào)碼

  • C類IP地址:前三段號(hào)碼為網(wǎng)絡(luò)號(hào)碼卸奉,剩下的一段號(hào)碼為本地計(jì)算機(jī)的號(hào)碼

  • 特殊地址:

    • 127.0.0.1 回環(huán)地址,可用于測試本機(jī)的網(wǎng)絡(luò)是否有問題。
      • eg : ping 127.0.0.1
    • xxx.xxx.xxx.0 網(wǎng)絡(luò)地址
    • xxx.xxx.xxx.255 廣播地址
InetAddress類的使用
  • 沒有構(gòu)造方法颖御,那么如何使類提供的功能呢择卦?
  • 要掌握的方法
    • 獲取任意主機(jī):getByName
    • 主機(jī)名:getHostName
    • 主機(jī)Ip地址:getHostAddress

端口號(hào)

  • 物理端口 網(wǎng)卡口
  • 邏輯端口 我們指的就是邏輯端口
    • A:每個(gè)網(wǎng)絡(luò)程序都會(huì)至少有一個(gè)邏輯端口
    • B:用于標(biāo)識(shí)進(jìn)程的邏輯地址,不同進(jìn)程的標(biāo)識(shí)
    • C:有效端口:065535郎嫁,其中01024系統(tǒng)使用或保留端口秉继。
    • 通過360可以查看端口號(hào)

協(xié)議UDP和TCP

UDP
  • 數(shù)據(jù)源目的封裝成數(shù)據(jù)包中,不需要建立連接泽铛;每個(gè)數(shù)據(jù)報(bào)的大小在限制在64k尚辑;因無連接,是不可靠協(xié)議盔腔;不需要建立連接杠茬,速度快
  • 舉例:
    • 聊天留言,在線視頻弛随,視頻會(huì)議瓢喉,發(fā)短信,郵局包裹舀透。
TCP
  • 建立連接栓票,形成傳輸數(shù)據(jù)的通道;在連接中進(jìn)行大數(shù)據(jù)量傳輸愕够;通過三次握手完成連接走贪,是可靠協(xié)議;必須建立連接惑芭,效率會(huì)稍低
  • 舉例:
    • 下載坠狡,打電話,QQ聊天(你在線嗎,在線,就回應(yīng)下,就開始聊天了)


Socket編程

  • 又稱網(wǎng)絡(luò)編程遂跟,套接字編程
  • Socket套接字:
    • 網(wǎng)絡(luò)上具有唯一標(biāo)識(shí)的IP地址端口號(hào)組合在一起才能構(gòu)成唯一能識(shí)別的標(biāo)識(shí)符套接字逃沿。
  • Socket原理機(jī)制:
    • 通信的兩端都有Socket。
    • 網(wǎng)絡(luò)通信其實(shí)就是Socket間的通信幻锁。
    • 數(shù)據(jù)在兩個(gè)Socket間通過IO傳輸凯亮。


UDP傳輸過程

  • DatagramSocketDatagramPacket
  • 建立發(fā)送端接收端越败。
  • 建立數(shù)據(jù)包触幼。
  • 調(diào)用Socket的發(fā)送接收方法。
  • 關(guān)閉Socket究飞。
  • 發(fā)送端與接收端是兩個(gè)獨(dú)立的運(yùn)行程序置谦。

UDP傳輸-發(fā)送端思路

  • 1:建立udp的socket服務(wù)

  • 2:將要發(fā)送的數(shù)據(jù)封裝成數(shù)據(jù)包

  • 3:通過udp的socket服務(wù),將數(shù)據(jù)包發(fā)送出

  • 4:關(guān)閉資源

  • UDP傳輸-發(fā)送端代碼

/*
 * UDP協(xié)議發(fā)送數(shù)據(jù):
 * A:創(chuàng)建發(fā)送端Socket對象
 * B:創(chuàng)建數(shù)據(jù)堂鲤,并把數(shù)據(jù)打包
 * C:調(diào)用Socket對象的發(fā)送方法發(fā)送數(shù)據(jù)包
 * D:釋放資源
 */
public class SendDemo {
    public static void main(String[] args) throws IOException {
        // 創(chuàng)建發(fā)送端Socket對象
        // DatagramSocket()
        DatagramSocket ds = new DatagramSocket();

        // 創(chuàng)建數(shù)據(jù),并把數(shù)據(jù)打包
        // DatagramPacket(byte[] buf, int length, InetAddress address, int port)
        // 創(chuàng)建數(shù)據(jù)
        byte[] bys = "hello,udp,我來了".getBytes();
        // 長度
        int length = bys.length;
        // IP地址對象
        InetAddress address = InetAddress.getByName("192.168.12.92");
        // 端口
        int port = 10086;
        DatagramPacket dp = new DatagramPacket(bys, length, address, port);

        // 調(diào)用Socket對象的發(fā)送方法發(fā)送數(shù)據(jù)包
        // public void send(DatagramPacket p)
        ds.send(dp);

        // 釋放資源
        ds.close();
    }
}


UDP傳輸-接收端思路

  • 1:建立udp的socket服務(wù).

  • 2:通過receive方法接收數(shù)據(jù)

  • 3:將收到的數(shù)據(jù)存儲(chǔ)到數(shù)據(jù)包對象中

  • 4:通過數(shù)據(jù)包對象的功能來完成對接收到數(shù)據(jù)進(jìn)行解析.

  • 5:可以對資源進(jìn)行關(guān)閉

  • UDP傳輸-接收端代碼

/*
 * UDP協(xié)議接收數(shù)據(jù):
 * A:創(chuàng)建接收端Socket對象
 * B:創(chuàng)建一個(gè)數(shù)據(jù)包(接收容器)
 * C:調(diào)用Socket對象的接收方法接收數(shù)據(jù)
 * D:解析數(shù)據(jù)包媒峡,并顯示在控制臺(tái)
 * E:釋放資源
 */
public class ReceiveDemo {
    public static void main(String[] args) throws IOException {
        // 創(chuàng)建接收端Socket對象
        // DatagramSocket(int port)
        DatagramSocket ds = new DatagramSocket(10086);

        // 創(chuàng)建一個(gè)數(shù)據(jù)包(接收容器)
        // DatagramPacket(byte[] buf, int length)
        byte[] bys = new byte[1024];
        int length = bys.length;
        DatagramPacket dp = new DatagramPacket(bys, length);

        // 調(diào)用Socket對象的接收方法接收數(shù)據(jù)
        // public void receive(DatagramPacket p)
        ds.receive(dp); // 阻塞式

        // 解析數(shù)據(jù)包瘟栖,并顯示在控制臺(tái)
        // 獲取對方的ip
        // public InetAddress getAddress()
        InetAddress address = dp.getAddress();
        String ip = address.getHostAddress();
        // public byte[] getData():獲取數(shù)據(jù)緩沖區(qū)
        // public int getLength():獲取數(shù)據(jù)的實(shí)際長度
        byte[] bys2 = dp.getData();
        int len = dp.getLength();
        String s = new String(bys2, 0, len);
        System.out.println(ip + "傳遞的數(shù)據(jù)是:" + s);

        // 釋放資源
        ds.close();
    }
}


  • 優(yōu)化后代碼
/*
 * 多次啟動(dòng)接收端:
 *         java.net.BindException: Address already in use: Cannot bind
 *         端口被占用。
 */
public class ReceiveDemo {
    public static void main(String[] args) throws IOException {
        // 創(chuàng)建接收端的Socket對象
        DatagramSocket ds = new DatagramSocket(12345);

        // 創(chuàng)建一個(gè)包裹
        byte[] bys = new byte[1024];
        DatagramPacket dp = new DatagramPacket(bys, bys.length);

        // 接收數(shù)據(jù)
        ds.receive(dp);

        // 解析數(shù)據(jù)
        String ip = dp.getAddress().getHostAddress();
        String s = new String(dp.getData(), 0, dp.getLength());
        System.out.println("from " + ip + " data is : " + s);

        // 釋放資源
        ds.close();
    }
}




public class SendDemo {
    public static void main(String[] args) throws IOException {
        // 創(chuàng)建發(fā)送端的Socket對象
        DatagramSocket ds = new DatagramSocket();

        // 創(chuàng)建數(shù)據(jù)并打包
        byte[] bys = "helloworld".getBytes();
        DatagramPacket dp = new DatagramPacket(bys, bys.length,
                InetAddress.getByName("192.168.12.92"), 12345);

        // 發(fā)送數(shù)據(jù)
        ds.send(dp);

        // 釋放資源
        ds.close();
    }
}


UDP案例

  • 從鍵盤錄入數(shù)據(jù)進(jìn)行發(fā)送谅阿,如果輸入的是886那么客戶端就結(jié)束輸入數(shù)據(jù)半哟。
/*
 * 多次啟動(dòng)接收端:
 *         java.net.BindException: Address already in use: Cannot bind
 *         端口被占用。
 */
public class ReceiveDemo {
    public static void main(String[] args) throws IOException {
        // 創(chuàng)建接收端的Socket對象
        DatagramSocket ds = new DatagramSocket(12345);

        while (true) {
            // 創(chuàng)建一個(gè)包裹
            byte[] bys = new byte[1024];
            DatagramPacket dp = new DatagramPacket(bys, bys.length);

            // 接收數(shù)據(jù)
            ds.receive(dp);

            // 解析數(shù)據(jù)
            String ip = dp.getAddress().getHostAddress();
            String s = new String(dp.getData(), 0, dp.getLength());
            System.out.println("from " + ip + " data is : " + s);
        }

        // 釋放資源
        // 接收端應(yīng)該一直開著等待接收數(shù)據(jù)签餐,是不需要關(guān)閉
        // ds.close();
    }
}




/*
 * 數(shù)據(jù)來自于鍵盤錄入
 * 鍵盤錄入數(shù)據(jù)要自己控制錄入結(jié)束寓涨。
 */
public class SendDemo {
    public static void main(String[] args) throws IOException {
        // 創(chuàng)建發(fā)送端的Socket對象
        DatagramSocket ds = new DatagramSocket();

        // 封裝鍵盤錄入數(shù)據(jù)
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        String line = null;
        while ((line = br.readLine()) != null) {
            if ("886".equals(line)) {
                break;
            }

            // 創(chuàng)建數(shù)據(jù)并打包
            byte[] bys = line.getBytes();
            // DatagramPacket dp = new DatagramPacket(bys, bys.length,
            // InetAddress.getByName("192.168.12.92"), 12345);
            DatagramPacket dp = new DatagramPacket(bys, bys.length,InetAddress.getByName("192.168.12.255"), 12345);

            // 發(fā)送數(shù)據(jù)
            ds.send(dp);
        }

        // 釋放資源
        ds.close();
    }
}


  • 把剛才發(fā)送和接收程序分別用線程進(jìn)行封裝,完成一個(gè)UDP的聊天程序氯檐。
/*
 * 通過多線程改進(jìn)剛才的聊天程序戒良,這樣我就可以實(shí)現(xiàn)在一個(gè)窗口發(fā)送和接收數(shù)據(jù)了
 */
public class ChatRoom {
    public static void main(String[] args) throws IOException {
        DatagramSocket dsSend = new DatagramSocket();
        DatagramSocket dsReceive = new DatagramSocket(12306);

        SendThread st = new SendThread(dsSend);
        ReceiveThread rt = new ReceiveThread(dsReceive);

        Thread t1 = new Thread(st);
        Thread t2 = new Thread(rt);

        t1.start();
        t2.start();
    }
}




public class ReceiveThread implements Runnable {
    private DatagramSocket ds;

    public ReceiveThread(DatagramSocket ds) {
        this.ds = ds;
    }

    @Override
    public void run() {
        try {
            while (true) {
                // 創(chuàng)建一個(gè)包裹
                byte[] bys = new byte[1024];
                DatagramPacket dp = new DatagramPacket(bys, bys.length);

                // 接收數(shù)據(jù)
                ds.receive(dp);

                // 解析數(shù)據(jù)
                String ip = dp.getAddress().getHostAddress();
                String s = new String(dp.getData(), 0, dp.getLength());
                System.out.println("from " + ip + " data is : " + s);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}



public class SendThread implements Runnable {

    private DatagramSocket ds;

    public SendThread(DatagramSocket ds) {
        this.ds = ds;
    }

    @Override
    public void run() {
        try {
            // 封裝鍵盤錄入數(shù)據(jù)
            BufferedReader br = new BufferedReader(new InputStreamReader(
                    System.in));
            String line = null;
            while ((line = br.readLine()) != null) {
                if ("886".equals(line)) {
                    break;
                }

                // 創(chuàng)建數(shù)據(jù)并打包
                byte[] bys = line.getBytes();
                // DatagramPacket dp = new DatagramPacket(bys, bys.length,
                // InetAddress.getByName("192.168.12.92"), 12345);
                DatagramPacket dp = new DatagramPacket(bys, bys.length,
                        InetAddress.getByName("192.168.12.255"), 12306);

                // 發(fā)送數(shù)據(jù)
                ds.send(dp);
            }

            // 釋放資源
            ds.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}


TCP傳輸

  • Socket和ServerSocket
  • 建立客戶端和服務(wù)器端
  • 建立連接后,通過Socket中的IO流進(jìn)行數(shù)據(jù)的傳輸
  • 關(guān)閉socket
  • 同樣冠摄,客戶端與服務(wù)器端是兩個(gè)獨(dú)立的應(yīng)用程序糯崎。

TCP傳輸-客戶端思路

  • 1:建立客戶端的Socket服務(wù),并明確要連接的服務(wù)器。

  • 2:如果連接建立成功,就表明已經(jīng)建立了數(shù)據(jù)傳輸?shù)耐ǖ篮佑尽>涂梢栽谠撏ǖ劳ㄟ^IO進(jìn)行數(shù)據(jù)的讀取和寫入沃呢。該通道稱為Socket流,Socket流中既有讀取流,也有寫入流

  • 3:通過Socket對象的方法,可以獲取這兩個(gè)流拆挥。

  • 4:通過流的對象可以對數(shù)據(jù)進(jìn)行傳輸

  • 5:如果傳輸數(shù)據(jù)完畢,關(guān)閉資源

  • TCP傳輸-客戶端代碼

/*
 * 連接被拒絕薄霜。TCP協(xié)議一定要先開服務(wù)器。
 * java.net.ConnectException: Connection refused: connect
 */
public class ClientDemo {
    public static void main(String[] args) throws IOException {
        // 創(chuàng)建發(fā)送端的Socket對象
        // Socket(InetAddress address, int port)
        // Socket(String host, int port)
        // Socket s = new Socket(InetAddress.getByName("192.168.12.92"), 8888);
        Socket s = new Socket("192.168.12.92", 8888);

        // 獲取輸出流竿刁,寫數(shù)據(jù)
        // public OutputStream getOutputStream()
        OutputStream os = s.getOutputStream();
        os.write("hello,tcp,我來了".getBytes());

        // 釋放資源
        s.close();
    }
}


TCP傳輸-服務(wù)器端思路

  • 1:建立服務(wù)器端的socket服務(wù)黄锤,需要一個(gè)端口
  • 2:服務(wù)端沒有直接流的操作,而是通過accept方法獲取客戶端對象,再通過獲取到的客戶端對象的流和客戶端進(jìn)行通信食拜。
  • 3:通過客戶端的獲取流對象的方法,讀取數(shù)據(jù)或者寫入數(shù)據(jù)
  • 4:如果服務(wù)完成,需要關(guān)閉客戶端,然后關(guān)閉服務(wù)器,但是,一般會(huì)關(guān)閉客戶端,不會(huì)關(guān)閉服務(wù)器,因?yàn)榉?wù)端是一直提供服務(wù)的副编。
  • TCP傳輸-服務(wù)器端代碼
/*
 * TCP協(xié)議接收數(shù)據(jù):
 * A:創(chuàng)建接收端的Socket對象
 * B:監(jiān)聽客戶端連接负甸。返回一個(gè)對應(yīng)的Socket對象
 * C:獲取輸入流,讀取數(shù)據(jù)顯示在控制臺(tái)
 * D:釋放資源
 */
public class ServerDemo {
    public static void main(String[] args) throws IOException {
        // 創(chuàng)建接收端的Socket對象
        // ServerSocket(int port)
        ServerSocket ss = new ServerSocket(8888);

        // 監(jiān)聽客戶端連接痹届。返回一個(gè)對應(yīng)的Socket對象
        // public Socket accept()
        Socket s = ss.accept(); // 偵聽并接受到此套接字的連接。此方法在連接傳入之前一直阻塞。

        // 獲取輸入流郑兴,讀取數(shù)據(jù)顯示在控制臺(tái)
        InputStream is = s.getInputStream();

        byte[] bys = new byte[1024];
        int len = is.read(bys); // 阻塞式方法
        String str = new String(bys, 0, len);

        String ip = s.getInetAddress().getHostAddress();

        System.out.println(ip + "---" + str);

        // 釋放資源
        s.close();
        // ss.close(); //這個(gè)不應(yīng)該關(guān)閉
    }
}


TCP傳輸案例

  • 服務(wù)器給客戶端反饋
public class ServerDemo {
    public static void main(String[] args) throws IOException {
        // 創(chuàng)建服務(wù)器Socket對象
        ServerSocket ss = new ServerSocket(9999);

        // 監(jiān)聽客戶端的連接
        Socket s = ss.accept(); // 阻塞

        // 獲取輸入流
        InputStream is = s.getInputStream();
        byte[] bys = new byte[1024];
        int len = is.read(bys); // 阻塞
        String server = new String(bys, 0, len);
        System.out.println("server:" + server);

        // 獲取輸出流
        OutputStream os = s.getOutputStream();
        os.write("數(shù)據(jù)已經(jīng)收到".getBytes());

        // 釋放資源
        s.close();
        // ss.close();
    }
}


public class ClientDemo {
    public static void main(String[] args) throws IOException {
        // 創(chuàng)建客戶端Socket對象
        Socket s = new Socket("192.168.12.92", 9999);

        // 獲取輸出流
        OutputStream os = s.getOutputStream();
        os.write("今天天氣很好,適合睡覺".getBytes());

        // 獲取輸入流
        InputStream is = s.getInputStream();
        byte[] bys = new byte[1024];
        int len = is.read(bys);// 阻塞
        String client = new String(bys, 0, len);
        System.out.println("client:" + client);

        // 釋放資源
        s.close();
    }
}


  • 客戶端鍵盤錄入薪前,服務(wù)器輸出到控制臺(tái)
public class ServerDemo {
    public static void main(String[] args) throws IOException {
        // 創(chuàng)建服務(wù)器Socket對象
        ServerSocket ss = new ServerSocket(22222);

        // 監(jiān)聽客戶端連接
        Socket s = ss.accept();

        // 包裝通道內(nèi)容的流
        BufferedReader br = new BufferedReader(new InputStreamReader(
                s.getInputStream()));
        String line = null;
        while ((line = br.readLine()) != null) {
            System.out.println(line);
        }

        // br.close();
        s.close();
        // ss.close();
    }
}



/*
 * 客戶端鍵盤錄入,服務(wù)器輸出到控制臺(tái)
 */
public class ClientDemo {
    public static void main(String[] args) throws IOException {
        // 創(chuàng)建客戶端Socket對象
        Socket s = new Socket("192.168.12.92", 22222);

        // 鍵盤錄入數(shù)據(jù)
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        // 把通道內(nèi)的流給包裝一下
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(
                s.getOutputStream()));

        String line = null;
        while ((line = br.readLine()) != null) {
            // 鍵盤錄入數(shù)據(jù)要自定義結(jié)束標(biāo)記
            if ("886".equals(line)) {
                break;
            }
            bw.write(line);
            bw.newLine();
            bw.flush();
        }

        // 釋放資源
        // bw.close();
        // br.close();
        s.close();
    }
}


  • 客戶端鍵盤錄入柴淘,服務(wù)器輸出文本文件
public class ServerDemo {
    public static void main(String[] args) throws IOException {
        // 創(chuàng)建服務(wù)器Socket對象
        ServerSocket ss = new ServerSocket(23456);

        // 監(jiān)聽客戶端連接
        Socket s = ss.accept();

        // 封裝通道內(nèi)的數(shù)據(jù)
        BufferedReader br = new BufferedReader(new InputStreamReader(
                s.getInputStream()));
        // 封裝文本文件
        BufferedWriter bw = new BufferedWriter(new FileWriter("a.txt"));

        String line = null;
        while ((line = br.readLine()) != null) {
            bw.write(line);
            bw.newLine();
            bw.flush();
        }

        bw.close();
        // br.close();
        s.close();
        // ss.close();
    }
}


  • 客戶端文本文件迫淹,服務(wù)器輸出到控制臺(tái)
/*
 * 客戶端文本文件秘通,服務(wù)器輸出到控制臺(tái)
 */
public class ClientDemo {
    public static void main(String[] args) throws IOException {
        // 創(chuàng)建Socket對象
        Socket s = new Socket("192.168.12.92", 34567);

        // 封裝文本文件
        BufferedReader br = new BufferedReader(new FileReader(
                "InetAddressDemo.java"));
        // 封裝通道內(nèi)的流
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(
                s.getOutputStream()));

        String line = null;
        while ((line = br.readLine()) != null) {
            bw.write(line);
            bw.newLine();
            bw.flush();
        }

        br.close();
        s.close();
    }
}




public class ServerDemo {
    public static void main(String[] args) throws IOException {
        // 創(chuàng)建服務(wù)器Socket對象
        ServerSocket ss = new ServerSocket(34567);

        // 監(jiān)聽客戶端連接
        Socket s = ss.accept();

        // 封裝通道內(nèi)的流
        BufferedReader br = new BufferedReader(new InputStreamReader(
                s.getInputStream()));

        String line = null;
        while ((line = br.readLine()) != null) {
            System.out.println(line);
        }

        
        s.close();
    }
}


TCP傳輸容易出現(xiàn)的問題

  • 客戶端連接上服務(wù)端,兩端都在等待敛熬,沒有任何數(shù)據(jù)傳輸肺稀。
  • 通過例程分析:
    • 因?yàn)閞ead方法或者readLine方法是阻塞式。
  • 解決辦法:
    • 自定義結(jié)束標(biāo)記
    • 使用shutdownInput应民,shutdownOutput方法话原。
  • 客戶端文本文件,服務(wù)器輸出文本文件
/*
 * 問題:按照我們正常的思路加入反饋信息诲锹,結(jié)果卻沒反應(yīng)繁仁。為什么呢?
 * 讀取文本文件是可以以null作為結(jié)束信息的,但是呢归园,通道內(nèi)是不能這樣結(jié)束信息的黄虱。
 * 所以,服務(wù)器根本就不知道你結(jié)束了蔓倍。而你還想服務(wù)器給你反饋悬钳。所以,就相互等待了偶翅。
 * 
 * 如何解決呢?
 * A:再多寫一條數(shù)據(jù)默勾,告訴服務(wù)器,讀取到這條數(shù)據(jù)說明我就結(jié)束聚谁,你也結(jié)束吧母剥。
 *         這樣做可以解決問題,但是不好形导。
 * B:Socket對象提供了一種解決方案
 *         public void shutdownOutput()
 */

public class UploadClient {
    public static void main(String[] args) throws IOException {
        // 創(chuàng)建客戶端Socket對象
        Socket s = new Socket("192.168.12.92", 11111);

        // 封裝文本文件
        BufferedReader br = new BufferedReader(new FileReader(
                "InetAddressDemo.java"));
        // 封裝通道內(nèi)流
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(
                s.getOutputStream()));

        String line = null;
        while ((line = br.readLine()) != null) { // 阻塞
            bw.write(line);
            bw.newLine();
            bw.flush();
        }

        //Socket提供了一個(gè)終止环疼,它會(huì)通知服務(wù)器你別等了,我沒有數(shù)據(jù)過來了
        s.shutdownOutput();

        // 接收反饋
        BufferedReader brClient = new BufferedReader(new InputStreamReader(
                s.getInputStream()));
        String client = brClient.readLine(); // 阻塞
        System.out.println(client);

        // 釋放資源
        br.close();
        s.close();
    }
}




public class UploadServer {
    public static void main(String[] args) throws IOException {
        // 創(chuàng)建服務(wù)器端的Socket對象
        ServerSocket ss = new ServerSocket(11111);

        // 監(jiān)聽客戶端連接
        Socket s = ss.accept();// 阻塞

        // 封裝通道內(nèi)的流
        BufferedReader br = new BufferedReader(new InputStreamReader(
                s.getInputStream()));
        // 封裝文本文件
        BufferedWriter bw = new BufferedWriter(new FileWriter("Copy.java"));

        String line = null;
        while ((line = br.readLine()) != null) { // 阻塞
        // if("over".equals(line)){
        // break;
        // }
            bw.write(line);
            bw.newLine();
            bw.flush();
        }

        // 給出反饋
        BufferedWriter bwServer = new BufferedWriter(new OutputStreamWriter(
                s.getOutputStream()));
        bwServer.write("文件上傳成功");
        bwServer.newLine();
        bwServer.flush();

        // 釋放資源
        bw.close();
        s.close();
    }
}


  • 上傳圖片案例
public class UploadServer {
    public static void main(String[] args) throws IOException {
        // 創(chuàng)建服務(wù)器Socket對象
        ServerSocket ss = new ServerSocket(19191);

        // 監(jiān)聽客戶端連接
        Socket s = ss.accept();

        // 封裝通道內(nèi)流
        BufferedInputStream bis = new BufferedInputStream(s.getInputStream());
        // 封裝圖片文件
        BufferedOutputStream bos = new BufferedOutputStream(
                new FileOutputStream("mn.jpg"));

        byte[] bys = new byte[1024];
        int len = 0;
        while ((len = bis.read(bys)) != -1) {
            bos.write(bys, 0, len);
            bos.flush();
        }

        // 給一個(gè)反饋
        OutputStream os = s.getOutputStream();
        os.write("圖片上傳成功".getBytes());

        bos.close();
        s.close();
    }
}




    public static void main(String[] args) throws IOException {
        // 創(chuàng)建客戶端Socket對象
        Socket s = new Socket("192.168.12.92", 19191);

        // 封裝圖片文件
        BufferedInputStream bis = new BufferedInputStream(new FileInputStream(
                "林青霞.jpg"));
        // 封裝通道內(nèi)的流
        BufferedOutputStream bos = new BufferedOutputStream(s.getOutputStream());

        byte[] bys = new byte[1024];
        int len = 0;
        while ((len = bis.read(bys)) != -1) {
            bos.write(bys, 0, len);
            bos.flush();
        }
        
        s.shutdownOutput();

        // 讀取反饋
        InputStream is = s.getInputStream();
        byte[] bys2 = new byte[1024];
        int len2 = is.read(bys2);
        String client = new String(bys2, 0, len2);
        System.out.println(client);

        // 釋放資源
        bis.close();
        s.close();
    }
}


  • 服務(wù)器的代碼用線程進(jìn)行封裝朵耕,這樣可以模擬一個(gè)同時(shí)接收多人上傳文件的服務(wù)器炫隶。(用循環(huán)也可以但是效率低,是單線程的程序)
public class UploadClient {
    public static void main(String[] args) throws IOException {
        // 創(chuàng)建客戶端Socket對象
        Socket s = new Socket("192.168.12.92", 11111);

        // 封裝文本文件
        // BufferedReader br = new BufferedReader(new FileReader(
        // "InetAddressDemo.java"));
        BufferedReader br = new BufferedReader(new FileReader(
                "ReceiveDemo.java"));
        // 封裝通道內(nèi)流
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(
                s.getOutputStream()));

        String line = null;
        while ((line = br.readLine()) != null) { // 阻塞
            bw.write(line);
            bw.newLine();
            bw.flush();
        }

        // Socket提供了一個(gè)終止阎曹,它會(huì)通知服務(wù)器你別等了伪阶,我沒有數(shù)據(jù)過來了
        s.shutdownOutput();

        // 接收反饋
        BufferedReader brClient = new BufferedReader(new InputStreamReader(
                s.getInputStream()));
        String client = brClient.readLine(); // 阻塞
        System.out.println(client);

        // 釋放資源
        br.close();
        s.close();
    }
}





public class UserThread implements Runnable {
    private Socket s;

    public UserThread(Socket s) {
        this.s = s;
    }

    @Override
    public void run() {
        try {
            // 封裝通道內(nèi)的流
            BufferedReader br = new BufferedReader(new InputStreamReader(
                    s.getInputStream()));
            // 封裝文本文件
            // BufferedWriter bw = new BufferedWriter(new
            // FileWriter("Copy.java"));

            // 為了防止名稱沖突
            String newName = System.currentTimeMillis() + ".java";
            BufferedWriter bw = new BufferedWriter(new FileWriter(newName));

            String line = null;
            while ((line = br.readLine()) != null) { // 阻塞
                bw.write(line);
                bw.newLine();
                bw.flush();
            }

            // 給出反饋
            BufferedWriter bwServer = new BufferedWriter(
                    new OutputStreamWriter(s.getOutputStream()));
            bwServer.write("文件上傳成功");
            bwServer.newLine();
            bwServer.flush();

            // 釋放資源
            bw.close();
            s.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}



public class UploadServer {
    public static void main(String[] args) throws IOException {
        // 創(chuàng)建服務(wù)器Socket對象
        ServerSocket ss = new ServerSocket(11111);

        while (true) {
            Socket s = ss.accept();
            new Thread(new UserThread(s)).start();
        }
    }
}


最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市处嫌,隨后出現(xiàn)的幾起案子栅贴,更是在濱河造成了極大的恐慌,老刑警劉巖熏迹,帶你破解...
    沈念sama閱讀 221,635評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件檐薯,死亡現(xiàn)場離奇詭異,居然都是意外死亡注暗,警方通過查閱死者的電腦和手機(jī)坛缕,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,543評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門墓猎,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人祷膳,你說我怎么就攤上這事陶衅。” “怎么了直晨?”我有些...
    開封第一講書人閱讀 168,083評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵搀军,是天一觀的道長。 經(jīng)常有香客問我勇皇,道長罩句,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,640評(píng)論 1 296
  • 正文 為了忘掉前任敛摘,我火速辦了婚禮门烂,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘兄淫。我一直安慰自己屯远,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,640評(píng)論 6 397
  • 文/花漫 我一把揭開白布捕虽。 她就那樣靜靜地躺著慨丐,像睡著了一般。 火紅的嫁衣襯著肌膚如雪泄私。 梳的紋絲不亂的頭發(fā)上房揭,一...
    開封第一講書人閱讀 52,262評(píng)論 1 308
  • 那天,我揣著相機(jī)與錄音晌端,去河邊找鬼捅暴。 笑死,一個(gè)胖子當(dāng)著我的面吹牛咧纠,可吹牛的內(nèi)容都是我干的蓬痒。 我是一名探鬼主播,決...
    沈念sama閱讀 40,833評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼漆羔,長吁一口氣:“原來是場噩夢啊……” “哼乳幸!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起钧椰,我...
    開封第一講書人閱讀 39,736評(píng)論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎符欠,沒想到半個(gè)月后嫡霞,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,280評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡希柿,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,369評(píng)論 3 340
  • 正文 我和宋清朗相戀三年诊沪,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了养筒。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,503評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡端姚,死狀恐怖晕粪,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情渐裸,我是刑警寧澤巫湘,帶...
    沈念sama閱讀 36,185評(píng)論 5 350
  • 正文 年R本政府宣布,位于F島的核電站昏鹃,受9級(jí)特大地震影響尚氛,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜洞渤,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,870評(píng)論 3 333
  • 文/蒙蒙 一阅嘶、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧载迄,春花似錦讯柔、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,340評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至捏卓,卻和暖如春极祸,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背怠晴。 一陣腳步聲響...
    開封第一講書人閱讀 33,460評(píng)論 1 272
  • 我被黑心中介騙來泰國打工遥金, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人蒜田。 一個(gè)月前我還...
    沈念sama閱讀 48,909評(píng)論 3 376
  • 正文 我出身青樓稿械,卻偏偏與公主長得像,于是被迫代替她去往敵國和親冲粤。 傳聞我的和親對象是個(gè)殘疾皇子美莫,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,512評(píng)論 2 359

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

  • 1. 網(wǎng)絡(luò)編程概述 1.1 計(jì)算機(jī)網(wǎng)絡(luò) 是指將地理位置不同的具有獨(dú)立功能的多臺(tái)計(jì)算機(jī)及其外部設(shè)備,通過通信線路連接...
    JackChen1024閱讀 1,039評(píng)論 0 3
  • 網(wǎng)絡(luò)概念第一天 兩臺(tái)電腦怎么通過網(wǎng)絡(luò)傳輸數(shù)據(jù)梯捕?怎樣才能知道傳輸?shù)氖菙?shù)據(jù)厢呵?誰摸過網(wǎng)線? 看電影傀顾,怎么看的襟铭?通過電流,...
    小吖朱閱讀 1,564評(píng)論 0 1
  • 個(gè)人認(rèn)為,Goodboy1881先生的TCP /IP 協(xié)議詳解學(xué)習(xí)博客系列博客是一部非常精彩的學(xué)習(xí)筆記寒砖,這雖然只是...
    貳零壹柒_fc10閱讀 5,060評(píng)論 0 8
  • 1.這篇文章不是本人原創(chuàng)的赐劣,只是個(gè)人為了對這部分知識(shí)做一個(gè)整理和系統(tǒng)的輸出而編輯成的,在此鄭重地向本文所引用文章的...
    SOMCENT閱讀 13,075評(píng)論 6 174
  • 作者:Poll的筆記博客出處:http://www.cnblogs.com/maybe2030/本文版權(quán)歸作者和博...
    wshxj123閱讀 2,405評(píng)論 0 19