Java網(wǎng)絡(luò)編程基礎(chǔ)

轉(zhuǎn)載黑馬程序員筆記

主要內(nèi)容

  • 軟件架構(gòu)CS/BS
  • 網(wǎng)絡(luò)通信三要素
  • TCP通信
  • Socket套接字
  • ServerSocket

第一章 網(wǎng)絡(luò)編程入門

1.1軟件結(jié)構(gòu)

  • C/S結(jié)構(gòu) :全稱為Client/Server結(jié)構(gòu)太援,是指客戶端和服務(wù)器結(jié)構(gòu)一铅。常見程序有QQ、迅雷等軟件肌访。
C/S結(jié)構(gòu)
  • B/S結(jié)構(gòu) :全稱為Browser/Server結(jié)構(gòu)螟蒸,是指瀏覽器和服務(wù)器結(jié)構(gòu)盒使。常見瀏覽器有谷歌、火狐等七嫌。
B/S結(jié)構(gòu)

兩種架構(gòu)各有優(yōu)勢(shì)少办,但是無論哪種架構(gòu),都離不開網(wǎng)絡(luò)的支持诵原。網(wǎng)絡(luò)編程英妓,就是在一定的協(xié)議下,實(shí)現(xiàn)兩臺(tái)計(jì)算機(jī)的通信的程序绍赛。

1.2 網(wǎng)絡(luò)通信協(xié)議

  • 網(wǎng)絡(luò)通信協(xié)議:通過計(jì)算機(jī)網(wǎng)絡(luò)可以使多臺(tái)計(jì)算機(jī)實(shí)現(xiàn)連接蔓纠,位于同一個(gè)網(wǎng)絡(luò)中的計(jì)算機(jī)在進(jìn)行連接和通信時(shí)需要遵守一定的規(guī)則,這就好比在道路中行駛的汽車一定要遵守交通規(guī)則一樣惹资。在計(jì)算機(jī)網(wǎng)絡(luò)中贺纲,這些連接和通信的規(guī)則被稱為網(wǎng)絡(luò)通信協(xié)議,它對(duì)數(shù)據(jù)的傳輸格式褪测、傳輸速率猴誊、傳輸步驟等做了統(tǒng)一規(guī)定,通信雙方必須同時(shí)遵守才能完成數(shù)據(jù)交換侮措。
  • TCP/IP協(xié)議: 傳輸控制協(xié)議/因特網(wǎng)互聯(lián)協(xié)議( Transmission Control Protocol/Internet Protocol)懈叹,是Internet最基本、最廣泛的協(xié)議分扎。它定義了計(jì)算機(jī)如何連入因特網(wǎng)澄成,以及數(shù)據(jù)如何在它們之間傳輸?shù)臉?biāo)準(zhǔn)。它的內(nèi)部包含一系列的用于處理數(shù)據(jù)通信的協(xié)議畏吓,并采用了4層的分層模型墨状,每一層都呼叫它的下一層所提供的協(xié)議來完成自己的需求。
網(wǎng)絡(luò)通信協(xié)議

上圖中菲饼,TCP/IP協(xié)議中的四層分別是應(yīng)用層肾砂、傳輸層、網(wǎng)絡(luò)層和鏈路層宏悦,每層分別負(fù)責(zé)不同的通信功能镐确。
鏈路層:鏈路層是用于定義物理傳輸通道包吝,通常是對(duì)某些網(wǎng)絡(luò)連接設(shè)備的驅(qū)動(dòng)協(xié)議,例如針對(duì)光纖源葫、網(wǎng)線提供的驅(qū)動(dòng)诗越。
網(wǎng)絡(luò)層:網(wǎng)絡(luò)層是整個(gè)TCP/IP協(xié)議的核心,它主要用于將傳輸?shù)臄?shù)據(jù)進(jìn)行分組息堂,將分組數(shù)據(jù)發(fā)送到目標(biāo)計(jì)算機(jī)或者網(wǎng)絡(luò)嚷狞。
運(yùn)輸層:主要使網(wǎng)絡(luò)程序進(jìn)行通信,在進(jìn)行網(wǎng)絡(luò)通信時(shí)储矩,可以采用TCP協(xié)議感耙,也可以采用UDP協(xié)議。
應(yīng)用層:主要負(fù)責(zé)應(yīng)用程序的協(xié)議持隧,例如HTTP協(xié)議、FTP協(xié)議等逃片。

1.3 協(xié)議分類

通信的協(xié)議還是比較復(fù)雜的屡拨,java.net 包中包含的類和接口,它們提供低層次的通信細(xì)節(jié)褥实。我們可以直接使用這些類和接口呀狼,來專注于網(wǎng)絡(luò)程序開發(fā),而不用考慮通信的細(xì)節(jié)损离。

java.net 包中提供了兩種常見的網(wǎng)絡(luò)協(xié)議的支持:

  • UDP:用戶數(shù)據(jù)報(bào)協(xié)議(User Datagram Protocol)哥艇。UDP是無連接通信協(xié)議,即在數(shù)據(jù)傳輸時(shí)僻澎,數(shù)據(jù)的發(fā)送端和接收端不建立邏輯連接貌踏。簡(jiǎn)單來說,當(dāng)一臺(tái)計(jì)算機(jī)向另外一臺(tái)計(jì)算機(jī)發(fā)送數(shù)據(jù)時(shí)窟勃,發(fā)送端不會(huì)確認(rèn)接收端是否存在祖乳,就會(huì)發(fā)出數(shù)據(jù),同樣接收端在收到數(shù)據(jù)時(shí)秉氧,也不會(huì)向發(fā)送端反饋是否收到數(shù)據(jù)眷昆。

    由于使用UDP協(xié)議消耗資源小,通信效率高汁咏,所以通常都會(huì)用于音頻亚斋、視頻和普通數(shù)據(jù)的傳輸例如視頻會(huì)議都使用UDP協(xié)議,因?yàn)檫@種情況即使偶爾丟失一兩個(gè)數(shù)據(jù)包攘滩,也不會(huì)對(duì)接收結(jié)果產(chǎn)生太大影響帅刊。

    但是在使用UDP協(xié)議傳送數(shù)據(jù)時(shí),由于UDP的面向無連接性轰驳,不能保證數(shù)據(jù)的完整性厚掷,因此在傳輸重要數(shù)據(jù)時(shí)不建議使用UDP協(xié)議弟灼。UDP的交換過程如下圖所示。

特點(diǎn):數(shù)據(jù)被限制在64kb以內(nèi)冒黑,超出這個(gè)范圍就不能發(fā)送了田绑。

數(shù)據(jù)報(bào)(Datagram):網(wǎng)絡(luò)傳輸?shù)幕締挝?/p>

  • TCP:傳輸控制協(xié)議 (Transmission Control Protocol)。TCP協(xié)議是面向連接的通信協(xié)議抡爹,即傳輸數(shù)據(jù)之前掩驱,在發(fā)送端和接收端建立邏輯連接,然后再傳輸數(shù)據(jù)冬竟,它提供了兩臺(tái)計(jì)算機(jī)之間可靠無差錯(cuò)的數(shù)據(jù)傳輸欧穴。

    在TCP連接中必須要明確客戶端與服務(wù)器端,由客戶端向服務(wù)端發(fā)出連接請(qǐng)求泵殴,每次連接的創(chuàng)建都需要經(jīng)過“三次握手”涮帘。

    • 三次握手:TCP協(xié)議中,在發(fā)送數(shù)據(jù)的準(zhǔn)備階段笑诅,客戶端與服務(wù)器之間的三次交互调缨,以保證連接的可靠。
      • 第一次握手吆你,客戶端向服務(wù)器端發(fā)出連接請(qǐng)求弦叶,等待服務(wù)器確認(rèn)。
      • 第二次握手妇多,服務(wù)器端向客戶端回送一個(gè)響應(yīng)伤哺,通知客戶端收到了連接請(qǐng)求。
      • 第三次握手者祖,客戶端再次向服務(wù)器端發(fā)送確認(rèn)信息立莉,確認(rèn)連接。整個(gè)交互過程如下圖所示咸包。
通信圖解

? 完成三次握手桃序,連接建立后,客戶端和服務(wù)器就可以開始進(jìn)行數(shù)據(jù)傳輸了烂瘫。由于這種面向連接的特性媒熊,TCP協(xié)議可以保證傳輸數(shù)據(jù)的安全,所以應(yīng)用十分廣泛坟比,例如下載文件芦鳍、瀏覽網(wǎng)頁等。

1.4 網(wǎng)絡(luò)編程三要素

協(xié)議

  • 協(xié)議:計(jì)算機(jī)網(wǎng)絡(luò)通信必須遵守的規(guī)則葛账,已經(jīng)介紹過了柠衅,不再贅述。

IP地址

  • IP地址:指互聯(lián)網(wǎng)協(xié)議地址(Internet Protocol Address)籍琳,俗稱IP菲宴。IP地址用來給一個(gè)網(wǎng)絡(luò)中的計(jì)算機(jī)設(shè)備做唯一的編號(hào)贷祈。假如我們把“個(gè)人電腦”比作“一臺(tái)電話”的話,那么“IP地址”就相當(dāng)于“電話號(hào)碼”喝峦。

IP地址分類

  • IPv4:是一個(gè)32位的二進(jìn)制數(shù)势誊,通常被分為4個(gè)字節(jié),表示成a.b.c.d 的形式谣蠢,例如192.168.65.100 粟耻。其中a、b眉踱、c挤忙、d都是0~255之間的十進(jìn)制整數(shù),那么最多可以表示42億個(gè)谈喳。

  • IPv6:由于互聯(lián)網(wǎng)的蓬勃發(fā)展册烈,IP地址的需求量愈來愈大,但是網(wǎng)絡(luò)地址資源有限婿禽,使得IP的分配越發(fā)緊張茄厘。

    為了擴(kuò)大地址空間,擬通過IPv6重新定義地址空間谈宛,采用128位地址長(zhǎng)度,每16個(gè)字節(jié)一組胎署,分成8組十六進(jìn)制數(shù)吆录,表示成ABCD:EF01:2345:6789:ABCD:EF01:2345:6789,號(hào)稱可以為全世界的每一粒沙子編上一個(gè)網(wǎng)址琼牧,這樣就解決了網(wǎng)絡(luò)地址資源數(shù)量不夠的問題恢筝。

常用命令

  • 查看本機(jī)IP地址,在控制臺(tái)輸入:
ipconfig
  • 檢查網(wǎng)絡(luò)是否連通巨坊,在控制臺(tái)輸入:
ping 空格 IP地址
ping 220.181.57.216

特殊的IP地址

  • 本機(jī)IP地址:127.0.0.1撬槽、localhost

端口號(hào)

網(wǎng)絡(luò)的通信趾撵,本質(zhì)上是兩個(gè)進(jìn)程(應(yīng)用程序)的通信侄柔。每臺(tái)計(jì)算機(jī)都有很多的進(jìn)程,那么在網(wǎng)絡(luò)通信時(shí)占调,如何區(qū)分這些進(jìn)程呢暂题?

如果說IP地址可以唯一標(biāo)識(shí)網(wǎng)絡(luò)中的設(shè)備,那么端口號(hào)就可以唯一標(biāo)識(shí)設(shè)備中的進(jìn)程(應(yīng)用程序)了究珊。

  • 端口號(hào):用兩個(gè)字節(jié)表示的整數(shù)薪者,它的取值范圍是0~65535。其中剿涮,0~1023之間的端口號(hào)用于一些知名的網(wǎng)絡(luò)服務(wù)和應(yīng)用言津,普通的應(yīng)用程序需要使用1024以上的端口號(hào)攻人。如果端口號(hào)被另外一個(gè)服務(wù)或應(yīng)用所占用,會(huì)導(dǎo)致當(dāng)前程序啟動(dòng)失敗悬槽。

利用協(xié)議+IP地址+端口號(hào) 三元組合怀吻,就可以標(biāo)識(shí)網(wǎng)絡(luò)中的進(jìn)程了,那么進(jìn)程間的通信就可以利用這個(gè)標(biāo)識(shí)與其它進(jìn)程進(jìn)行交互陷谱。

第二章 TCP通信程序

2.1 概述

TCP通信能實(shí)現(xiàn)兩臺(tái)計(jì)算機(jī)之間的數(shù)據(jù)交互烙博,通信的兩端,要嚴(yán)格區(qū)分為客戶端(Client)與服務(wù)端(Server)烟逊。

兩端通信時(shí)步驟:

  1. 服務(wù)端程序渣窜,需要事先啟動(dòng),等待客戶端的連接宪躯。
  2. 客戶端主動(dòng)連接服務(wù)器端乔宿,連接成功才能通信。服務(wù)端不可以主動(dòng)連接客戶端访雪。

在Java中详瑞,提供了兩個(gè)類用于實(shí)現(xiàn)TCP通信程序:

  1. 客戶端:java.net.Socket 類表示。創(chuàng)建Socket對(duì)象臣缀,向服務(wù)端發(fā)出連接請(qǐng)求坝橡,服務(wù)端響應(yīng)請(qǐng)求,兩者建立連接開始通信精置。
  2. 服務(wù)端:java.net.ServerSocket 類表示计寇。創(chuàng)建ServerSocket對(duì)象,相當(dāng)于開啟一個(gè)服務(wù)脂倦,并等待客戶端的連接番宁。

2.2 Socket類

Socket 類:該類實(shí)現(xiàn)客戶端套接字,套接字指的是兩臺(tái)設(shè)備之間通訊的端點(diǎn)赖阻。

構(gòu)造方法

  • public Socket(String host, int port) :創(chuàng)建套接字對(duì)象并將其連接到指定主機(jī)上的指定端口號(hào)蝶押。如果指定的host是null ,則相當(dāng)于指定地址為回送地址火欧。

    小貼士:回送地址(127.x.x.x) 是本機(jī)回送地址(Loopback Address)棋电,主要用于網(wǎng)絡(luò)軟件測(cè)試以及本地機(jī)進(jìn)程間通信,無論什么程序布隔,一旦使用回送地址發(fā)送數(shù)據(jù)离陶,立即返回,不進(jìn)行任何網(wǎng)絡(luò)傳輸衅檀。

構(gòu)造舉例招刨,代碼如下:

Socket client = new Socket("127.0.0.1", 6666);

成員方法

  • public InputStream getInputStream() : 返回此套接字的輸入流。

    • 如果此Scoket具有相關(guān)聯(lián)的通道哀军,則生成的InputStream 的所有操作也關(guān)聯(lián)該通道沉眶。
    • 關(guān)閉生成的InputStream也將關(guān)閉相關(guān)的Socket打却。
  • public OutputStream getOutputStream() : 返回此套接字的輸出流。

    • 如果此Scoket具有相關(guān)聯(lián)的通道谎倔,則生成的OutputStream 的所有操作也關(guān)聯(lián)該通道柳击。
    • 關(guān)閉生成的OutputStream也將關(guān)閉相關(guān)的Socket。
  • public void close() :關(guān)閉此套接字片习。

    • 一旦一個(gè)socket被關(guān)閉捌肴,它不可再使用。
    • 關(guān)閉此socket也將關(guān)閉相關(guān)的InputStream和OutputStream 藕咏。
  • public void shutdownOutput() : 禁用此套接字的輸出流状知。

    • 任何先前寫出的數(shù)據(jù)將被發(fā)送,隨后終止輸出流孽查。

    2.3 ServerSocket類

ServerSocket類:這個(gè)類實(shí)現(xiàn)了服務(wù)器套接字饥悴,該對(duì)象等待通過網(wǎng)絡(luò)的請(qǐng)求。

構(gòu)造方法

  • public ServerSocket(int port) :使用該構(gòu)造方法在創(chuàng)建ServerSocket對(duì)象時(shí)盲再,就可以將其綁定到一個(gè)指定的端口號(hào)上西设,參數(shù)port就是端口號(hào)。

構(gòu)造舉例答朋,代碼如下:

ServerSocket server = new ServerSocket(6666);

成員方法

  • public Socket accept() :偵聽并接受連接贷揽,返回一個(gè)新的Socket對(duì)象,用于和客戶端實(shí)現(xiàn)通信梦碗。該方法會(huì)一直阻塞直到建立連接擒滑。

2.4 簡(jiǎn)單的TCP網(wǎng)絡(luò)程序

TCP通信分析圖解

  1. 【服務(wù)端】啟動(dòng),創(chuàng)建ServerSocket對(duì)象,等待連接叉弦。
  2. 【客戶端】啟動(dòng),創(chuàng)建Socket對(duì)象,請(qǐng)求連接藻糖。
  3. 【服務(wù)端】接收連接,調(diào)用accept方法淹冰,并返回一個(gè)Socket對(duì)象。
  4. 【客戶端】Socket對(duì)象巨柒,獲取OutputStream樱拴,向服務(wù)端寫出數(shù)據(jù)。
  5. 【服務(wù)端】Scoket對(duì)象洋满,獲取InputStream晶乔,讀取客戶端發(fā)送的數(shù)據(jù)。

到此牺勾,客戶端向服務(wù)端發(fā)送數(shù)據(jù)成功正罢。

簡(jiǎn)單通信

自此,服務(wù)端向客戶端回寫數(shù)據(jù)驻民。

  1. 【服務(wù)端】Socket對(duì)象翻具,獲取OutputStream履怯,向客戶端回寫數(shù)據(jù)。
  2. 【客戶端】Scoket對(duì)象裆泳,獲取InputStream叹洲,解析回寫數(shù)據(jù)。
  3. 【客戶端】釋放資源工禾,斷開連接运提。

客戶端向服務(wù)器發(fā)送數(shù)據(jù)

服務(wù)端實(shí)現(xiàn):

public class ServerTCP {
    public static void main(String[] args) throws IOException {
        System.out.println("服務(wù)端啟動(dòng) , 等待連接 .... ");
        // 1.創(chuàng)建 ServerSocket對(duì)象,綁定端口闻葵,開始等待連接
        ServerSocket ss = new ServerSocket(6666);
        // 2.接收連接 accept 方法, 返回 socket 對(duì)象.
        Socket server = ss.accept();
        // 3.通過socket 獲取輸入流
        InputStream is = server.getInputStream();
        // 4.一次性讀取數(shù)據(jù)
        // 4.1 創(chuàng)建字節(jié)數(shù)組
        byte[] b = new byte[1024];
        // 4.2 據(jù)讀取到字節(jié)數(shù)組中.
        int len = is.read(b)民泵;
        // 4.3 解析數(shù)組,打印字符串信息
        String msg = new String(b, 0, len);
        System.out.println(msg);
        //5.關(guān)閉資源.
        is.close();
        server.close();
    }
}

客戶端實(shí)現(xiàn):

public class ClientTCP {
    public static void main(String[] args) throws Exception {
        System.out.println("客戶端 發(fā)送數(shù)據(jù)");
        // 1.創(chuàng)建 Socket ( ip , port ) , 確定連接到哪里.
        Socket client = new Socket("localhost", 6666);
        // 2.獲取流對(duì)象 . 輸出流
        OutputStream os = client.getOutputStream();
        // 3.寫出數(shù)據(jù).
        os.write("你好么? tcp ,我來了".getBytes());
        // 4. 關(guān)閉資源 .
        os.close();
        client.close();
    }
}

服務(wù)器向客戶端回寫數(shù)據(jù)

服務(wù)端實(shí)現(xiàn):

public class ServerTCP {
    public static void main(String[] args) throws IOException {
        System.out.println("服務(wù)端啟動(dòng) , 等待連接 .... ");
        // 1.創(chuàng)建 ServerSocket對(duì)象,綁定端口笙隙,開始等待連接
        ServerSocket ss = new ServerSocket(6666);
        // 2.接收連接 accept 方法, 返回 socket 對(duì)象.
        Socket server = ss.accept();
        // 3.通過socket 獲取輸入流
        InputStream is = server.getInputStream();
        // 4.一次性讀取數(shù)據(jù)
        // 4.1 創(chuàng)建字節(jié)數(shù)組
        byte[] b = new byte[1024];
        // 4.2 據(jù)讀取到字節(jié)數(shù)組中.
        int len = is.read(b)洪灯;
        // 4.3 解析數(shù)組,打印字符串信息
        String msg = new String(b, 0, len);
        System.out.println(msg);
        // =================回寫數(shù)據(jù)=======================
        // 5. 通過 socket 獲取輸出流
         OutputStream out = server.getOutputStream();
        // 6. 回寫數(shù)據(jù)
         out.write("我很好,謝謝你".getBytes());
        // 7.關(guān)閉資源.
        out.close();
        is.close();
        server.close();
    }
}

客戶端實(shí)現(xiàn):

public class ClientTCP {
    public static void main(String[] args) throws Exception {
        System.out.println("客戶端 發(fā)送數(shù)據(jù)");
        // 1.創(chuàng)建 Socket ( ip , port ) , 確定連接到哪里.
        Socket client = new Socket("localhost", 6666);
        // 2.通過Scoket,獲取輸出流對(duì)象 
        OutputStream os = client.getOutputStream();
        // 3.寫出數(shù)據(jù).
        os.write("你好么? tcp ,我來了".getBytes());
        // ==============解析回寫=========================
        // 4. 通過Scoket,獲取 輸入流對(duì)象
        InputStream in = client.getInputStream();
        // 5. 讀取數(shù)據(jù)數(shù)據(jù)
        byte[] b = new byte[100];
        int len = in.read(b);
        System.out.println(new String(b, 0, len));
        // 6. 關(guān)閉資源 .
        in.close();
        os.close();
        client.close();
    }
}

第三章 綜合案例

3.1 文件上傳案例

文件上傳分析圖解

  1. 【客戶端】輸入流,從硬盤讀取文件數(shù)據(jù)到程序中竟痰。
  2. 【客戶端】輸出流签钩,寫出文件數(shù)據(jù)到服務(wù)端。
  3. 【服務(wù)端】輸入流坏快,讀取文件數(shù)據(jù)到服務(wù)端程序铅檩。
  4. 【服務(wù)端】輸出流,寫出文件數(shù)據(jù)到服務(wù)器硬盤中莽鸿。
upload

基本實(shí)現(xiàn)

服務(wù)端實(shí)現(xiàn):

public class FileUpload_Server {
    public static void main(String[] args) throws IOException {
        System.out.println("服務(wù)器 啟動(dòng).....  ");
        // 1. 創(chuàng)建服務(wù)端ServerSocket
        ServerSocket serverSocket = new ServerSocket(6666);
        // 2. 建立連接 
        Socket accept = serverSocket.accept();
        // 3. 創(chuàng)建流對(duì)象
        // 3.1 獲取輸入流,讀取文件數(shù)據(jù)
        BufferedInputStream bis = new BufferedInputStream(accept.getInputStream());
        // 3.2 創(chuàng)建輸出流,保存到本地 .
        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("copy.jpg"));
        // 4. 讀寫數(shù)據(jù)
        byte[] b = new byte[1024 * 8];
        int len;
        while ((len = bis.read(b)) != -1) {
            bos.write(b, 0, len);
        }
        //5. 關(guān)閉 資源
        bos.close();
        bis.close();
        accept.close();
        System.out.println("文件上傳已保存");
    }
}

客戶端實(shí)現(xiàn):

public class FileUPload_Client {
    public static void main(String[] args) throws IOException {
        // 1.創(chuàng)建流對(duì)象
        // 1.1 創(chuàng)建輸入流,讀取本地文件  
        BufferedInputStream bis  = new BufferedInputStream(new FileInputStream("test.jpg"));
        // 1.2 創(chuàng)建輸出流,寫到服務(wù)端 
        Socket socket = new Socket("localhost", 6666);
        BufferedOutputStream   bos   = new BufferedOutputStream(socket.getOutputStream());

        //2.寫出數(shù)據(jù). 
        byte[] b  = new byte[1024 * 8 ];
        int len ; 
        while (( len  = bis.read(b))!=-1) {
            bos.write(b, 0, len);
            bos.flush();
        }
        System.out.println("文件發(fā)送完畢");
        // 3.釋放資源

        bos.close(); 
        socket.close();
        bis.close(); 
        System.out.println("文件上傳完畢 ");
    }
}

文件上傳優(yōu)化分析

  1. 文件名稱寫死的問題

    服務(wù)端昧旨,保存文件的名稱如果寫死,那么最終導(dǎo)致服務(wù)器硬盤祥得,只會(huì)保留一個(gè)文件兔沃,建議使用系統(tǒng)時(shí)間優(yōu)化,保證文件名稱唯一级及,代碼如下:

FileOutputStream fis = new FileOutputStream(System.currentTimeMillis()+".jpg") // 文件名稱
BufferedOutputStream bos = new BufferedOutputStream(fis);
  1. 循環(huán)接收的問題

    服務(wù)端乒疏,指保存一個(gè)文件就關(guān)閉了,之后的用戶無法再上傳饮焦,這是不符合實(shí)際的怕吴,使用循環(huán)改進(jìn),可以不斷的接收不同用戶的文件县踢,代碼如下:

// 每次接收新的連接,創(chuàng)建一個(gè)Socket
while(true){
    Socket accept = serverSocket.accept();
    ......
}
  1. 效率問題

    服務(wù)端转绷,在接收大文件時(shí),可能耗費(fèi)幾秒鐘的時(shí)間硼啤,此時(shí)不能接收其他用戶上傳议经,所以,使用多線程技術(shù)優(yōu)化,代碼如下:

while(true){
    Socket accept = serverSocket.accept();
    // accept 交給子線程處理.
    new Thread(() -> {
        ......
        InputStream bis = accept.getInputStream();
        ......
    }).start();
}

優(yōu)化實(shí)現(xiàn)

public class FileUpload_Server {
    public static void main(String[] args) throws IOException {
        System.out.println("服務(wù)器 啟動(dòng).....  ");
        // 1. 創(chuàng)建服務(wù)端ServerSocket
        ServerSocket serverSocket = new ServerSocket(6666);
        // 2. 循環(huán)接收,建立連接
        while (true) {
            Socket accept = serverSocket.accept();
            /* 
            3. socket對(duì)象交給子線程處理,進(jìn)行讀寫操作
               Runnable接口中,只有一個(gè)run方法,使用lambda表達(dá)式簡(jiǎn)化格式
            */
            new Thread(() -> {
                try (
                    //3.1 獲取輸入流對(duì)象
                    BufferedInputStream bis = new BufferedInputStream(accept.getInputStream());
                    //3.2 創(chuàng)建輸出流對(duì)象, 保存到本地 .
                    FileOutputStream fis = new FileOutputStream(System.currentTimeMillis() + ".jpg");
                    BufferedOutputStream bos = new BufferedOutputStream(fis);) {
                    // 3.3 讀寫數(shù)據(jù)
                    byte[] b = new byte[1024 * 8];
                    int len;
                    while ((len = bis.read(b)) != -1) {
                      bos.write(b, 0, len);
                    }
                    //4. 關(guān)閉 資源
                    bos.close();
                    bis.close();
                    accept.close();
                    System.out.println("文件上傳已保存");
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }).start();
        }
    }
}

信息回寫分析圖解

前四步與基本文件上傳一致.

  1. 【服務(wù)端】獲取輸出流爸业,回寫數(shù)據(jù)其骄。
  2. 【客戶端】獲取輸入流,解析回寫數(shù)據(jù)扯旷。
upload2

回寫實(shí)現(xiàn)

public class FileUpload_Server {
    public static void main(String[] args) throws IOException {
        System.out.println("服務(wù)器 啟動(dòng).....  ");
        // 1. 創(chuàng)建服務(wù)端ServerSocket
        ServerSocket serverSocket = new ServerSocket(6666);
        // 2. 循環(huán)接收,建立連接
        while (true) {
            Socket accept = serverSocket.accept();
            /*
            3. socket對(duì)象交給子線程處理,進(jìn)行讀寫操作
               Runnable接口中,只有一個(gè)run方法,使用lambda表達(dá)式簡(jiǎn)化格式
            */
            new Thread(() -> {
                try (
                    //3.1 獲取輸入流對(duì)象
                    BufferedInputStream bis = new BufferedInputStream(accept.getInputStream());
                    //3.2 創(chuàng)建輸出流對(duì)象, 保存到本地 .
                    FileOutputStream fis = new FileOutputStream(System.currentTimeMillis() + ".jpg");
                    BufferedOutputStream bos = new BufferedOutputStream(fis);
                ) {
                    // 3.3 讀寫數(shù)據(jù)
                    byte[] b = new byte[1024 * 8];
                    int len;
                    while ((len = bis.read(b)) != -1) {
                        bos.write(b, 0, len);
                    }

                    // 4.=======信息回寫===========================
                    System.out.println("back ........");
                    OutputStream out = accept.getOutputStream();
                    out.write("上傳成功".getBytes());
                    out.close();
                    //================================

                    //5. 關(guān)閉 資源
                    bos.close();
                    bis.close();
                    accept.close();
                    System.out.println("文件上傳已保存");
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }).start();
        }
    }
}

客戶端實(shí)現(xiàn):

public class FileUpload_Client {
    public static void main(String[] args) throws IOException {
        // 1.創(chuàng)建流對(duì)象
        // 1.1 創(chuàng)建輸入流,讀取本地文件
        BufferedInputStream bis = new BufferedInputStream(new FileInputStream("test.jpg"));
        // 1.2 創(chuàng)建輸出流,寫到服務(wù)端
        Socket socket = new Socket("localhost", 6666);
        BufferedOutputStream bos = new BufferedOutputStream(socket.getOutputStream());

        //2.寫出數(shù)據(jù).
        byte[] b  = new byte[1024 * 8 ];
        int len ;
        while (( len  = bis.read(b))!=-1) {
            bos.write(b, 0, len);
        }
        // 關(guān)閉輸出流,通知服務(wù)端,寫出數(shù)據(jù)完畢
        socket.shutdownOutput();
        System.out.println("文件發(fā)送完畢");
        // 3. =====解析回寫============
        InputStream in = socket.getInputStream();
        byte[] back = new byte[20];
        in.read(back);
        System.out.println(new String(back));
        in.close();
        // ============================

        // 4.釋放資源
        socket.close();
        bis.close();
    }
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末拯爽,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子钧忽,更是在濱河造成了極大的恐慌毯炮,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,919評(píng)論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件耸黑,死亡現(xiàn)場(chǎng)離奇詭異桃煎,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)大刊,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,567評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門为迈,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人缺菌,你說我怎么就攤上這事葫辐。” “怎么了伴郁?”我有些...
    開封第一講書人閱讀 163,316評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵耿战,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我焊傅,道長(zhǎng)剂陡,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,294評(píng)論 1 292
  • 正文 為了忘掉前任狐胎,我火速辦了婚禮鸭栖,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘握巢。我一直安慰自己纤泵,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,318評(píng)論 6 390
  • 文/花漫 我一把揭開白布镜粤。 她就那樣靜靜地躺著,像睡著了一般玻褪。 火紅的嫁衣襯著肌膚如雪肉渴。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,245評(píng)論 1 299
  • 那天带射,我揣著相機(jī)與錄音同规,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛券勺,可吹牛的內(nèi)容都是我干的绪钥。 我是一名探鬼主播,決...
    沈念sama閱讀 40,120評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼关炼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼程腹!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起儒拂,我...
    開封第一講書人閱讀 38,964評(píng)論 0 275
  • 序言:老撾萬榮一對(duì)情侶失蹤寸潦,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后社痛,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體见转,經(jīng)...
    沈念sama閱讀 45,376評(píng)論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,592評(píng)論 2 333
  • 正文 我和宋清朗相戀三年蒜哀,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了斩箫。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,764評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡撵儿,死狀恐怖乘客,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情统倒,我是刑警寧澤寨典,帶...
    沈念sama閱讀 35,460評(píng)論 5 344
  • 正文 年R本政府宣布,位于F島的核電站房匆,受9級(jí)特大地震影響耸成,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜浴鸿,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,070評(píng)論 3 327
  • 文/蒙蒙 一井氢、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧岳链,春花似錦花竞、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,697評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至苗分,卻和暖如春厌蔽,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背摔癣。 一陣腳步聲響...
    開封第一講書人閱讀 32,846評(píng)論 1 269
  • 我被黑心中介騙來泰國(guó)打工奴饮, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留纬向,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,819評(píng)論 2 370
  • 正文 我出身青樓戴卜,卻偏偏與公主長(zhǎng)得像逾条,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子投剥,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,665評(píng)論 2 354

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

  • 學(xué)習(xí)過程是看畢向東老師的視頻师脂。 1.網(wǎng)絡(luò)模型 面試的時(shí)候,技術(shù)總監(jiān)最后和我說到這里時(shí)薇缅,我表示一臉蒙蔽危彩,說不出這7層...
    英勇青銅5閱讀 791評(píng)論 0 13
  • [TOC] TCP/IP協(xié)議分層模型 TCP/IP分層模型 下層提供接口供上層使用,為上層服務(wù)泳桦。 Socket簡(jiǎn)述...
    0x70e8閱讀 558評(píng)論 0 1
  • 網(wǎng)絡(luò)編程的概述 網(wǎng)絡(luò)編程的實(shí)質(zhì)就是用來實(shí)現(xiàn)網(wǎng)絡(luò)互連的不同計(jì)算機(jī)上運(yùn)行的程序間可以進(jìn)行數(shù)據(jù)交換汤徽。 一.OSI網(wǎng)絡(luò)模型...
    思念揮霍閱讀 377評(píng)論 0 0
  • 1. 網(wǎng)絡(luò)編程概述 1.1 計(jì)算機(jī)網(wǎng)絡(luò) 是指將地理位置不同的具有獨(dú)立功能的多臺(tái)計(jì)算機(jī)及其外部設(shè)備,通過通信線路連接...
    JackChen1024閱讀 1,035評(píng)論 0 3
  • 網(wǎng)絡(luò)編程 一.楔子 你現(xiàn)在已經(jīng)學(xué)會(huì)了寫python代碼,假如你寫了兩個(gè)python文件a.py和b.py浮毯,分別去運(yùn)...
    go以恒閱讀 2,016評(píng)論 0 6