從屌絲到架構(gòu)師的飛越(網(wǎng)絡編程篇)-TCP協(xié)議

一.介紹

TCP(Transmission Control Protocol 傳輸控制協(xié)議)是一種面向連接的并村、可靠的郭厌、基于字節(jié)流的傳輸層通信協(xié)議,由IETF的RFC 793定義拳锚。在簡化的計算機網(wǎng)絡OSI模型中氧秘,它完成第四層傳輸層所指定的功能,用戶數(shù)據(jù)報協(xié)議(UDP)是同一層內(nèi) [1]? 另一個重要的傳輸協(xié)議省撑。在因特網(wǎng)協(xié)議族(Internet protocol suite)中玉组,TCP層是位于IP層之上谎柄,應用層之下的中間層。不同主機的應用層之間經(jīng)常需要可靠的惯雳、像管道一樣的連接朝巫,但是IP層不提供這樣的流機制,而是提供不可靠的包交換石景。

二.知識點介紹

1劈猿、概述

2、TCP協(xié)議通信

3潮孽、TCP代碼實現(xiàn)

4揪荣、TCP網(wǎng)絡程序

5、文件上傳案例

6往史、文件上傳案例多線程版本

三.上課視頻對應說明文檔

1仗颈、概述

TCP協(xié)議是面向連接的通信協(xié)議,即在傳輸數(shù)據(jù)前先在發(fā)送端和接收端建立邏輯連接椎例,然后再傳輸數(shù)據(jù)挨决,它提供了兩臺計算機之間可靠無差錯的數(shù)據(jù)傳輸。在TCP連接中必須要明確客戶端與服務器端订歪,由客戶端向服務端發(fā)出連接請求脖祈,每次連接的創(chuàng)建都需要經(jīng)過“三次握手”。第一次握手刷晋,客戶端向服務器端發(fā)出連接請求盖高,等待服務器確認,第二次握手眼虱,服務器端向客戶端回送一個響應喻奥,通知客戶端收到了連接請求,第三次握手捏悬,客戶端再次向服務器端發(fā)送確認信息撞蚕,確認連接。整個交互過程如下圖所示邮破。

由于TCP協(xié)議的面向連接特性诈豌,它可以保證傳輸數(shù)據(jù)的安全性仆救,所以是一個? 被廣泛采用的協(xié)議抒和,例如在下載文件時,如果數(shù)據(jù)接收不完整彤蔽,將會導致文件數(shù)據(jù)丟失而不能被打開摧莽,因此,下載文件時必須采用TCP協(xié)議顿痪。

2镊辕、TCP協(xié)議通信

TCP通信同UDP通信一樣油够,都能實現(xiàn)兩臺計算機之間的通信,通信的兩端都需要創(chuàng)建socket對象征懈。

區(qū)別在于石咬,UDP中只有發(fā)送端和接收端,不區(qū)分客戶端與服務器端卖哎,計算機之間可以任意地發(fā)送數(shù)據(jù)鬼悠。

而TCP通信是嚴格區(qū)分客戶端與服務器端的,在通信時亏娜,必須先由客戶端去連接服務器端才能實現(xiàn)通信焕窝,服務器端不可以主動連接客戶端,并且服務器端程序需要事先啟動维贺,等待客戶端的連接它掂。

在JDK中提供了兩個類用于實現(xiàn)TCP程序,一個是ServerSocket類溯泣,用于表示服務器端虐秋,一個是Socket類,用于表示客戶端发乔。

通信時熟妓,首先創(chuàng)建代表服務器端的ServerSocket對象,該對象相當于開啟一個服務栏尚,并等待客戶端的連接起愈,然后創(chuàng)建代表客戶端的Socket對象向服務器端發(fā)出連接請求,服務器端響應請求译仗,兩者建立連接開始通信抬虽。

2.1、ServerSocket服務端

通過前面的學習知道纵菌,在開發(fā)TCP程序時阐污,首先需要創(chuàng)建服務器端程序。JDK的java.net包中提供了一個ServerSocket類咱圆,該類的實例對象可以實現(xiàn)一個服務器段的程序笛辟。通過查閱API文檔可知,ServerSocket類提供了多種構(gòu)造方法序苏,接下來就對ServerSocket的構(gòu)造和方法進行逐一地講解手幢。

ServerSocket(int port):創(chuàng)建綁定到特定端口的服務器套接字。

使用該構(gòu)造方法在創(chuàng)建ServerSocket對象時忱详,就可以將其綁定到一個指定的端口號上(參數(shù)port就是端口號)围来。

接下來學習一下ServerSocket的常用方法:

(1) Socket accept():偵聽并接受到此套接字的連接。

(2) InetAddress getInetAddress:返回此服務器的套接字的本地地址。

ServerSocket對象負責監(jiān)聽某臺計算機的某個端口號监透,在創(chuàng)建ServerSocket對象后桶错,需要繼續(xù)調(diào)用該對象的accept()方法,接收來自客戶端的請求胀蛮。當執(zhí)行了accept()方法之后院刁,服務器端程序會發(fā)生阻塞,直到客戶端發(fā)出連接請求粪狼,accept()方法才會返回一個Scoket對象用于和客戶端實現(xiàn)通信黎比,程序才能繼續(xù)向下執(zhí)行。

2.2鸳玩、Socket客戶端

講解了ServerSocket對象可以實現(xiàn)服務端程序阅虫,但只實現(xiàn)服務器端程序還不能完成通信,此時還需要一個客戶端程序與之交互不跟,為此JDK提供了一個Socket類颓帝,用于實現(xiàn)TCP客戶端程序。

通過查閱API文檔可知Socket類同樣提供了多種構(gòu)造方法窝革,接下來就對Socket的常用構(gòu)造方法進行詳細講解购城。

(1)Socket(String host,int port):創(chuàng)建一個流套接字并將其連接到指定主機上的指定端口號。

使用該構(gòu)造方法在創(chuàng)建Socket對象時虐译,會根據(jù)參數(shù)去連接在指定地址和端口上運行的服務器程序瘪板,其中參數(shù)host接收的是一個字符串類型的IP地址。

(2)Socket(InetAddress address,int port):創(chuàng)建一個流套接字并將其連接到指定IP地址的指定端口號漆诽。

該方法在使用上與第二個構(gòu)造方法類似侮攀,參數(shù)address用于接收一個InetAddress類型的對象,該對象用于封裝一個IP地址厢拭。

在以上Socket的構(gòu)造方法中兰英,最常用的是第一個構(gòu)造方法。

接下來學習一下Socket的常用方法供鸠,如表所示畦贸。

在Socket類的常用方法中,getInputStream()和getOutStream()方法分別用于獲取輸入流和輸出流楞捂。當客戶端和服務端建立連接后薄坏,數(shù)據(jù)是以IO流的形式進行交互的,從而實現(xiàn)通信寨闹。

接下來通過一張圖來描述服務器端和客戶端的數(shù)據(jù)傳輸胶坠,如下圖所示。

3鼻忠、TCP代碼實現(xiàn)

Socket客戶端和ServerSocket服務器端

完成步驟:

(1)建立客戶端和服務器端涵但。

(2)建立連接后,通過Socket中的IO流(Socket流)進行數(shù)據(jù)的傳輸帖蔓。

(3)(如果是服務器端矮瘟,則需要添加一步操作:通過Socket服務獲取Sokect再獲取其當中的IO流)

(4)關(guān)閉socket。

(5)同樣塑娇,客戶端與服務器端是兩個獨立的應用程序澈侠。

注意:

(1)服務器端開啟后等待客戶端訪問,可以不關(guān)閉埋酬。

(2)一個服務器端對應多個客戶端哨啃。

(3)不同客戶端間通信可以通過服務器端中轉(zhuǎn)信息。

4写妥、TCP網(wǎng)絡程序

了解了ServerSocket拳球、Socket類的基本用法,為了讓大家更好地掌握這兩個類的使用珍特,接下來通過一個TCP通信的案例來進一步學習祝峻。如下圖所示。

要實現(xiàn)TCP通信需要創(chuàng)建一個服務器端程序和一個客戶端程序扎筒,為了保證數(shù)據(jù)傳輸?shù)陌踩岳痴遥紫刃枰獙崿F(xiàn)服務器端程序。

4.1嗜桌、服務端

代碼示例:

/*

* TCP 服務器端

*

* 1,創(chuàng)建服務器ServerSocket對象(指定服務器端口號)

* 2奥溺,開啟服務器了,等待客戶端的連接骨宠,當客戶端連接后浮定,可以獲取到連接服務器的客戶端Socket對象

* 3,給客戶端反饋信息

* 4,關(guān)閉流資源

*/

public class TCPServer {

public static void main(String[] args) throws IOException {

//1,創(chuàng)建服務器ServerSocket對象(指定服務器端口號)

ServerSocket ss = new ServerSocket(8888);

//2,開啟服務器了层亿,等待客戶端的連接壶唤,當客戶端連接后,可以獲取到連接服務器的客戶端Socket對象

Socket s = ss.accept();

//3,給客戶端反饋信息

/*

* a,獲取客戶端的輸出流

* b,在服務端端棕所,通過客戶端的輸出流寫數(shù)據(jù)給客戶端

*/

//a,獲取客戶端的輸出流

OutputStream out = s.getOutputStream();

//b,在服務端端鸠补,通過客戶端的輸出流寫數(shù)據(jù)給客戶端

out.write("你已經(jīng)連接上了服務器".getBytes());

//4,關(guān)閉流資源

out.close();

s.close();

//ss.close();? 服務器流 通常都是不關(guān)閉的

}

}

4.2、客戶端

代碼示例:

/*

* TCP 客戶端

*

* 1帆阳,創(chuàng)建客戶端Socket對象,(指定要連接的服務器地址與端口號)

* 2,獲取服務器端的反饋回來的信息

* 3,關(guān)閉流資源

*/

public class TCPClient {

public static void main(String[] args) throws IOException {

//1嗤形,創(chuàng)建客戶端Socket對象,(指定要連接的服務器地址與端口號)

Socket s = new Socket("192.168.74.58", 8888);

//2,獲取服務器端的反饋回來的信息

InputStream in = s.getInputStream();

//獲取獲取流中的數(shù)據(jù)

byte[] buffer = new byte[1024];

//把流中的數(shù)據(jù)存儲到數(shù)組中,并記錄讀取字節(jié)的個數(shù)

int length = in.read(buffer);

//顯示數(shù)據(jù)

System.out.println( new String(buffer, 0 , length) );

//3,關(guān)閉流資源

in.close();

s.close();

}

}

5针贬、文件上傳案例

5.1击费、案例分析

目前大多數(shù)服務器都會提供文件上傳的功能,由于文件上傳需要數(shù)據(jù)的安全性和完整性桦他,很明顯需要使用TCP協(xié)議來實現(xiàn)蔫巩。接下來通過一個案例來實現(xiàn)圖片上傳的功能。如下圖所示。原圖:文件上傳.bmp

5.2圆仔、服務端程序

首先編寫服務器端程序垃瞧,用來接收圖片。

代碼示例:

/*

* 文件上傳? 服務器端

*

*/

public class TCPServer {

public static void main(String[] args) throws IOException {

//1,創(chuàng)建服務器坪郭,等待客戶端連接

ServerSocket serverSocket = new ServerSocket(8888);

Socket clientSocket = serverSocket.accept();

//顯示哪個客戶端Socket連接上了服務器

InetAddress ipObject = clientSocket.getInetAddress();//得到IP地址對象

String ip = ipObject.getHostAddress(); //得到IP地址字符串

System.out.println("小樣个从,抓到你了,連接我M嵛帧嗦锐!" + "IP:" + ip);

//7,獲取Socket的輸入流

InputStream in = clientSocket.getInputStream();

//8,創(chuàng)建目的地的字節(jié)輸出流? D:\\upload\\192.168.74.58(1).jpg

BufferedOutputStream fileOut =new BufferedOutputStream(new FileOutputStream("D:\\upload\\192.168.74.58(1).jpg"));

//9,把Socket輸入流中的數(shù)據(jù),寫入目的地的字節(jié)輸出流中

byte[] buffer = new byte[1024];

int len = -1;

while((len = in.read(buffer)) != -1){

//寫入目的地的字節(jié)輸出流中

fileOut.write(buffer, 0, len);

}

//-----------------反饋信息---------------------

//10,獲取Socket的輸出流, 作用:寫反饋信息給客戶端

OutputStream out = clientSocket.getOutputStream();

//11,寫反饋信息給客戶端

out.write("圖片上傳成功".getBytes());

out.close();

fileOut.close();

in.close();

clientSocket.close();

//serverSocket.close();

}

}

5.3沪曙、客戶端

編寫客戶端奕污,完成上傳圖片

代碼示例:

/*

* 文件上傳 客戶端

*

* public void shutdownOutput()? 禁用此Socket的輸出流,間接的相當于告知了服務器數(shù)據(jù)寫入完畢

*/

public class TCPClient {

public static void main(String[] args) throws IOException {

//2,創(chuàng)建客戶端Socket,連接服務器

Socket socket = new Socket("192.168.74.58", 8888);

//3,獲取Socket流中的輸出流液走,功能:用來把數(shù)據(jù)寫到服務器

OutputStream out = socket.getOutputStream();

//4,創(chuàng)建字節(jié)輸入流菊值,功能:用來讀取數(shù)據(jù)源(圖片)的字節(jié)

BufferedInputStream fileIn = new BufferedInputStream(new FileInputStream("D:\\NoDir\\test.jpg"));

//5,把圖片數(shù)據(jù)寫到Socket的輸出流中(把數(shù)據(jù)傳給服務器)

byte[] buffer = new byte[1024];

int len = -1;

while ((len = fileIn.read(buffer)) != -1){

//把數(shù)據(jù)寫到Socket的輸出流中

out.write(buffer, 0, len);

}

//6,客戶端發(fā)送數(shù)據(jù)完畢,結(jié)束Socket輸出流的寫入操作育灸,告知服務器端

socket.shutdownOutput();

//-----------------反饋信息---------------------

//12,獲取Socket的輸入流? 作用: 讀反饋信息

InputStream in = socket.getInputStream();

//13,讀反饋信息

byte[] info = new byte[1024];

//把反饋信息存儲到info數(shù)組中腻窒,并記錄字節(jié)個數(shù)

int length = in.read(info);

//顯示反饋結(jié)果

System.out.println( new String(info, 0, length) );

//關(guān)閉流

in.close();

fileIn.close();

out.close();

socket.close();

}

}

6、文件上傳案例多線程版本

6.1磅崭、案例圖解

實現(xiàn)服務器端可以同時接收多個客戶端上傳的文件儿子。

6.2、修改服務器端代碼

代碼示例:

/*

* 文件上傳多線程版本, 服務器端

*/

public class TCPServer {

public static void main(String[] args) throws IOException {

//1,創(chuàng)建服務器砸喻,等待客戶端連接

ServerSocket serverSocket = new ServerSocket(6666);

//實現(xiàn)多個客戶端連接服務器的操作

while(true){

final Socket clientSocket = serverSocket.accept();

//啟動線程柔逼,完成與當前客戶端的數(shù)據(jù)交互過程

new Thread(){

public void run() {

try{

//顯示哪個客戶端Socket連接上了服務器

InetAddress ipObject = clientSocket.getInetAddress();//得到IP地址對象

String ip = ipObject.getHostAddress(); //得到IP地址字符串

System.out.println("小樣,抓到你了割岛,連接我S涫省!" + "IP:" + ip);

//7,獲取Socket的輸入流

InputStream in = clientSocket.getInputStream();

//8,創(chuàng)建目的地的字節(jié)輸出流? D:\\upload\\192.168.74.58(1).jpg

BufferedOutputStream fileOut =new BufferedOutputStream(new FileOutputStream("D:\\upload\\"+ip+"("+System.currentTimeMillis()+").jpg"));

//9,把Socket輸入流中的數(shù)據(jù)癣漆,寫入目的地的字節(jié)輸出流中

byte[] buffer = new byte[1024];

int len = -1;

while((len = in.read(buffer)) != -1){

//寫入目的地的字節(jié)輸出流中

fileOut.write(buffer, 0, len);

}

//-----------------反饋信息---------------------

//10,獲取Socket的輸出流, 作用:寫反饋信息給客戶端

OutputStream out = clientSocket.getOutputStream();

//11,寫反饋信息給客戶端

out.write("圖片上傳成功".getBytes());

out.close();

fileOut.close();

in.close();

clientSocket.close();

} catch(IOException e){

e.printStackTrace();

}

};

}.start();

}

//serverSocket.close();

}

}

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末维咸,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子惠爽,更是在濱河造成了極大的恐慌癌蓖,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,036評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件婚肆,死亡現(xiàn)場離奇詭異租副,居然都是意外死亡,警方通過查閱死者的電腦和手機较性,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,046評論 3 395
  • 文/潘曉璐 我一進店門用僧,熙熙樓的掌柜王于貴愁眉苦臉地迎上來结胀,“玉大人,你說我怎么就攤上這事责循≡愀郏” “怎么了?”我有些...
    開封第一講書人閱讀 164,411評論 0 354
  • 文/不壞的土叔 我叫張陵沼死,是天一觀的道長。 經(jīng)常有香客問我崔赌,道長意蛀,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,622評論 1 293
  • 正文 為了忘掉前任健芭,我火速辦了婚禮县钥,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘慈迈。我一直安慰自己若贮,他們只是感情好,可當我...
    茶點故事閱讀 67,661評論 6 392
  • 文/花漫 我一把揭開白布痒留。 她就那樣靜靜地躺著谴麦,像睡著了一般。 火紅的嫁衣襯著肌膚如雪伸头。 梳的紋絲不亂的頭發(fā)上匾效,一...
    開封第一講書人閱讀 51,521評論 1 304
  • 那天,我揣著相機與錄音恤磷,去河邊找鬼面哼。 笑死,一個胖子當著我的面吹牛扫步,可吹牛的內(nèi)容都是我干的魔策。 我是一名探鬼主播,決...
    沈念sama閱讀 40,288評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼河胎,長吁一口氣:“原來是場噩夢啊……” “哼闯袒!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起游岳,我...
    開封第一講書人閱讀 39,200評論 0 276
  • 序言:老撾萬榮一對情侶失蹤搁吓,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后吭历,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體堕仔,經(jīng)...
    沈念sama閱讀 45,644評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,837評論 3 336
  • 正文 我和宋清朗相戀三年晌区,在試婚紗的時候發(fā)現(xiàn)自己被綠了摩骨。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片通贞。...
    茶點故事閱讀 39,953評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖恼五,靈堂內(nèi)的尸體忽然破棺而出昌罩,到底是詐尸還是另有隱情,我是刑警寧澤灾馒,帶...
    沈念sama閱讀 35,673評論 5 346
  • 正文 年R本政府宣布茎用,位于F島的核電站,受9級特大地震影響睬罗,放射性物質(zhì)發(fā)生泄漏轨功。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,281評論 3 329
  • 文/蒙蒙 一容达、第九天 我趴在偏房一處隱蔽的房頂上張望古涧。 院中可真熱鬧,春花似錦花盐、人聲如沸羡滑。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,889評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽柒昏。三九已至,卻和暖如春熙揍,著一層夾襖步出監(jiān)牢的瞬間昙楚,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,011評論 1 269
  • 我被黑心中介騙來泰國打工诈嘿, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留堪旧,地道東北人。 一個月前我還...
    沈念sama閱讀 48,119評論 3 370
  • 正文 我出身青樓奖亚,卻偏偏與公主長得像淳梦,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子昔字,可洞房花燭夜當晚...
    茶點故事閱讀 44,901評論 2 355

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