
上圖中,TCP/IP協(xié)議中的四層分別是應(yīng)用層、傳輸層、網(wǎng)絡(luò)層和鏈路層,每層分別負(fù)責(zé)不同的通信功能,接下來(lái)針對(duì)這四層進(jìn)行詳細(xì)地講解。
鏈路層:鏈路層是用于定義物理傳輸通道,通常是對(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ò)。
傳輸層:主要使網(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.1IP地址和端口號(hào)
要想使網(wǎng)絡(luò)中的計(jì)算機(jī)能夠進(jìn)行通信,必須為每臺(tái)計(jì)算機(jī)指定一個(gè)標(biāo)識(shí)號(hào),通過(guò)這個(gè)標(biāo)識(shí)號(hào)來(lái)指定接受數(shù)據(jù)的計(jì)算機(jī)或者發(fā)送數(shù)據(jù)的計(jì)算機(jī)。
在TCP/IP協(xié)議中,這個(gè)標(biāo)識(shí)號(hào)就是IP地址,它可以唯一標(biāo)識(shí)一臺(tái)計(jì)算機(jī),目前,IP地址廣泛使用的版本是IPv4,它是由4個(gè)字節(jié)大小的二進(jìn)制數(shù)來(lái)表示,如:00001010000000000000000000000001。由于二進(jìn)制形式表示的IP地址非常不便記憶和處理,因此通常會(huì)將IP地址寫(xiě)成十進(jìn)制的形式,每個(gè)字節(jié)用一個(gè)十進(jìn)制數(shù)字(0-255)表示,數(shù)字間用符號(hào)“.”分開(kāi),如 “192.168.1.100”。
隨著計(jì)算機(jī)網(wǎng)絡(luò)規(guī)模的不斷擴(kuò)大,對(duì)IP地址的需求也越來(lái)越多,IPV4這種用4個(gè)字節(jié)表示的IP地址面臨枯竭,因此IPv6便應(yīng)運(yùn)而生了,IPv6使用16個(gè)字節(jié)表示IP地址,它所擁有的地址容量約是IPv4的8×1028倍,達(dá)到2128個(gè)(算上全零的),這樣就解決了網(wǎng)絡(luò)地址資源數(shù)量不夠的問(wèn)題。
通過(guò)IP地址可以連接到指定計(jì)算機(jī),但如果想訪問(wèn)目標(biāo)計(jì)算機(jī)中的某個(gè)應(yīng)用程序,還需要指定端口號(hào)。在計(jì)算機(jī)中,不同的應(yīng)用程序是通過(guò)端口號(hào)區(qū)分的。端口號(hào)是用兩個(gè)字節(jié)(16位的二進(jìn)制數(shù))表示的,它的取值范圍是0~65535,其中,0~1023之間的端口號(hào)用于一些知名的網(wǎng)絡(luò)服務(wù)和應(yīng)用,用戶的普通應(yīng)用程序需要使用1024以上的端口號(hào),從而避免端口號(hào)被另外一個(gè)應(yīng)用或服務(wù)所占用。
接下來(lái)通過(guò)一個(gè)圖例來(lái)描述IP地址和端口號(hào)的作用,如下圖所示。

1.1InetAddress
public class Example01{
? ?public static void main(String[] args) throws Exception {
? ? ? InetAddress local = InetAddress.getLocalHost();
? ? ?InetAddress remote = InetAddress.getByName("www.itcast.cn");
? ? ?System.out.println("本機(jī)的IP地址:" + local.getHostAddress());
? ? ?System.out.println("itcast的IP地址:" + remote.getHostAddress());
? ? ?System.out.println("itcast的主機(jī)名為:" + remote.getHostName());
? ? }
}
TCP/UDP
UDP:(適用于即時(shí)通信(QQ聊天 對(duì)數(shù)據(jù)準(zhǔn)確性和丟包要求比較低,但速度必須快),在線視頻(RTSP 速度一定要快,保證視頻連續(xù),但是偶爾花了一個(gè)圖像幀,人們還是能接受的),網(wǎng)絡(luò)語(yǔ)音電話(VoIP 語(yǔ)音數(shù)據(jù)包一般比較小,需要高速發(fā)送,偶爾斷音或串音也沒(méi)有問(wèn)題)等等。)
1,將數(shù)據(jù)和源及目的封裝在數(shù)據(jù)包中,不需要建立連接
2,每個(gè)數(shù)據(jù)包的大小限制在64k內(nèi)
3,因無(wú)連接,是不可靠協(xié)議
4,不需要建立連接,速度快
TCP:(對(duì)準(zhǔn)確率要求較高,適用于文件傳輸。郵件發(fā)送、上傳,下載等)
1.建立連接,行程傳輸數(shù)據(jù)的通道
2.在連接中形成大數(shù)據(jù)量傳輸
3.通過(guò)三次握手完成連接,是可靠協(xié)議
4.必須建立連接,是可靠協(xié)議
UDP:
要實(shí)現(xiàn)UDP通信需要?jiǎng)?chuàng)建一個(gè)發(fā)送端程序和一個(gè)接收端程序,很明顯,在通信時(shí)只有接收端程序先運(yùn)行,才能避免因發(fā)送端發(fā)送的數(shù)據(jù)無(wú)法接收,而造成數(shù)據(jù)丟失。因此,首先需要來(lái)完成接收端程序的編寫(xiě)。
UDP完成數(shù)據(jù)的發(fā)送
/*
*發(fā)送端
* 1,創(chuàng)建DatagramSocket對(duì)象
* 2,創(chuàng)建DatagramPacket對(duì)象,并封裝數(shù)據(jù)
* 3,發(fā)送數(shù)據(jù)
* 4,釋放流資源
*/
public?class?UDPSend {
public?static?void?main(String[] args)throwsIOException {
//1,創(chuàng)建DatagramSocket對(duì)象
DatagramSocket sendSocket =new?DatagramSocket();
//2,創(chuàng)建DatagramPacket對(duì)象,并封裝數(shù)據(jù)
//public DatagramPacket(byte[]buf,intlength, InetAddress address,intport)
//構(gòu)造數(shù)據(jù)報(bào)包,用來(lái)將長(zhǎng)度為length的包發(fā)送到指定主機(jī)上的指定端口號(hào)。
byte[ ] buffer ="hello,UDP".getBytes();
DatagramPacket dp =new?DatagramPacket(buffer, buffer.length, InetAddress.getByName("192.168.75.58"), 12306);
//3,發(fā)送數(shù)據(jù)
//public void send(DatagramPacket p)從此套接字發(fā)送數(shù)據(jù)報(bào)包
sendSocket.send(dp);
//4,釋放流資源
sendSocket.close();
?}
}
UDP完成數(shù)據(jù)的接收
/*
* UDP接收端
*
* 1,創(chuàng)建DatagramSocket對(duì)象
* 2,創(chuàng)建DatagramPacket對(duì)象
* 3,接收數(shù)據(jù)存儲(chǔ)到DatagramPacket對(duì)象中
* 4,獲取DatagramPacket對(duì)象的內(nèi)容
* 5,釋放流資源
*/
publicclassUDPReceive {
public?static?void?main (String[]args)?throws?IOException {
//1,創(chuàng)建DatagramSocket對(duì)象,并指定端口號(hào)
DatagramSocket ?receiveSocket =?new?DatagramSocket(12306);
//2,創(chuàng)建DatagramPacket對(duì)象,創(chuàng)建一個(gè)空的倉(cāng)庫(kù)
byte[ ] buffer=new?byte[1024];
DatagramPacket dp =?new?DatagramPacket (buffer, 1024);
//3,接收數(shù)據(jù)存儲(chǔ)到DatagramPacket對(duì)象中
receiveSocket.receive(dp);
//4,獲取DatagramPacket對(duì)象的內(nèi)容
//誰(shuí)發(fā)來(lái)的數(shù)據(jù)getAddress()
InetAddress ipAddress=dp.getAddress();
String ?ip = ipAddress.getHostAddress();//獲取到了IP地址
//發(fā)來(lái)了什么數(shù)據(jù)getData()
byte[ ] data=dp.getData();
//發(fā)來(lái)了多少數(shù)據(jù)getLenth()
int ?length ?= ?dp.getLength();
//顯示收到的數(shù)據(jù)
String ?dataStr ?= ?new ?String(data,0,length);
System.out.println("IP地址:"+ip+"數(shù)據(jù)是"+dataStr);
//5,釋放流資源
receiveSocket.close();
}
}
TCP通信
要實(shí)現(xiàn)TCP通信需要?jiǎng)?chuàng)建一個(gè)服務(wù)器端程序和一個(gè)客戶端程序,為了保證數(shù)據(jù)傳輸?shù)陌踩?,首先需要?shí)現(xiàn)服務(wù)器端程序。


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


文件上傳案例多線程版本:

實(shí)現(xiàn)服務(wù)器端可以同時(shí)接收多個(gè)客戶端上傳的文件。
l我們要修改服務(wù)器端代碼

