網(wǎng)絡(luò)編程
內(nèi)容介紹
網(wǎng)絡(luò)通信協(xié)議UDP通信TCP通信內(nèi)容學習目標
能夠獨立完成文件上傳案例的源代碼編寫、編譯、執(zhí)行任務(wù)。
網(wǎng)絡(luò)通信協(xié)議
可以通過計算機網(wǎng)絡(luò)連接多臺計算機。同一網(wǎng)絡(luò)上的計算機在連接和通信時必須遵守特定規(guī)則。這相當于在路上行駛的汽車必須遵守交通規(guī)則。
在計算機網(wǎng)絡(luò)中,這些連接和通信的規(guī)則被稱為網(wǎng)絡(luò)通信協(xié)議,它對數(shù)據(jù)的傳輸格式、傳輸速率、傳輸步驟等做了統(tǒng)一規(guī)定,通信雙方必須同時遵守才能完成數(shù)據(jù)交換。網(wǎng)絡(luò)通信協(xié)議有很多種,目前應用最廣泛的是TCP/IP協(xié)議(Transmission Control Protocal/Internet Protoal傳輸控制協(xié)議/英特網(wǎng)互聯(lián)協(xié)議),它是一個包括TCP協(xié)議和IP協(xié)議,UDP(User Datagram Protocol)協(xié)議和其它一些協(xié)議的協(xié)議組,在學習具體協(xié)議之前首先了解一下TCP/IP協(xié)議組的層次結(jié)構(gòu)。在進行數(shù)據(jù)傳輸時,要求發(fā)送的數(shù)據(jù)與收到的數(shù)據(jù)完全一樣,這時,就需要在原有的數(shù)據(jù)上添加很多信息,以保證數(shù)據(jù)在傳輸過程中數(shù)據(jù)格式完全一致。TCP/IP協(xié)議的層次結(jié)構(gòu)比較簡單,共分為四層,如圖所示。
上圖中,TCP/IP協(xié)議中的四層分別是應用層、傳輸層、網(wǎng)絡(luò)層和鏈路層,每層分別負責不同的通信功能,接下來針對這四層進行詳細地講解。
鏈路層:鏈路層是用于定義物理傳輸通道,通常是對某些網(wǎng)絡(luò)連接設(shè)備的驅(qū)動協(xié)議,例如針對光纖、網(wǎng)線提供的驅(qū)動。
網(wǎng)絡(luò)層:網(wǎng)絡(luò)層是整個TCP/IP協(xié)議的核心,它主要用于將傳輸?shù)臄?shù)據(jù)進行分組,將分組數(shù)據(jù)發(fā)送到目標計算機或者網(wǎng)絡(luò)。
運輸層:主要使網(wǎng)絡(luò)程序進行通信,在進行網(wǎng)絡(luò)通信時,可以采用TCP協(xié)議,也可以采用UDP協(xié)議。
應用層:主要負責應用程序的協(xié)議,例如HTTP協(xié)議、FTP協(xié)議等。
IP地址和端口號
要想使網(wǎng)絡(luò)中的計算機能夠進行通信,必須為每臺計算機指定一個標識號,通過這個標識號來指定接受數(shù)據(jù)的計算機或者發(fā)送數(shù)據(jù)的計算機。在TCP/IP協(xié)議中,這個標識號就是IP地址,它可以唯一標識一臺計算機,目前,IP地址廣泛使用的版本是IPV4,它是由4個字節(jié)大小的二進制數(shù)來表示,如:00001010000000000000000000000001。由于二進制形式表示的IP地址非常不便記憶和處理,因此通常會將IP地址寫成十進制的形式,每個字節(jié)用一個十進制數(shù)字(0-255)表示,數(shù)字間用符號“.”分開,如 “192.168.1.100”。隨著計算機網(wǎng)絡(luò)規(guī)模的不斷擴大,對IP地址的需求也越來越多,IPV4這種用4個字節(jié)表示的IP地址面臨枯竭,因此IPv6 便應運而生了,IPv6使用16個字節(jié)表示IP地址,它所擁有的地址容量約是IPv4的8×1028倍,達到2128個(算上全零的),這樣就解決了網(wǎng)絡(luò)地址資源數(shù)量不夠的問題。
通過IP地址可以連接到指定計算機,但如果想訪問目標計算機中的某個應用程序,還需要指定端口號。在計算機中,不同的應用程序是通過端口號區(qū)分的。端口號是用兩個字節(jié)(16位的二進制數(shù))表示的,它的取值范圍是0~65535,其中,0~1023之間的端口號用于一些知名的網(wǎng)絡(luò)服務(wù)和應用,用戶的普通應用程序需要使用1024以上的端口號,從而避免端口號被另外一個應用或服務(wù)所占用。
接下來通過一個圖例來描述IP地址和端口號的作用,如下圖所示。
從上圖中可以清楚地看到,位于網(wǎng)絡(luò)中一臺計算機可以通過IP地址去訪問另一臺計算機,并通過端口號訪問目標計算機中的某個應用程序。
InetAddress
了解了IP地址的作用,我們看學習下JDK中提供了一個InetAdderss類,該類用于封裝一個IP地址,并提供了一系列與IP地址相關(guān)的方法,下表中列出了InetAddress類的一些常用方法。
方法聲明 | 功能描述 | |
InetAddress getByName(String host) | 參數(shù)host表示指定的主機,該方法用于在給定主機名的情況下確定主機的 IP 地址 | |
InetAddress getLocalHost() | 創(chuàng)建一個表示本地主機的InetAddress對象 | |
String getHostName() | 得到IP地址的主機名,如果是本機則是計算機名,不是本機則是主機名,如果沒有域名則是IP地址 | |
String getHostAddress() | 得到字符串格式的原始 IP 地址 |
上表中,列舉了InetAddress的四個常用方法。其中,前兩個方法用于獲得該類的實例對象,第一個方法用于獲得表示指定主機的InetAddress對象,第二個方法用于獲得表示本地的InetAddress對象。通過InetAddress對象便可獲取指定主機名,IP地址等,接下來通過一個案例來演示InetAddress的常用方法,如下所示。
import java.net.InetAddress;
public class Example01 {
public static void main(String[] args) throws Exception {
InetAddress localAddress = Ine();
InetAddress remoteAddress = Ine("www.a;);
Sy("本機的IP地址:" + localAddre());
Sy("atguigu的IP地址:" + remoteAddre());
Sy("atguigu的主機名為:" + remo());
}
UDP與TCP協(xié)議
在介紹TCP/IP結(jié)構(gòu)時,提到傳輸層的兩個重要的高級協(xié)議,分別是UDP和TCP,其中UDP是User Datagram Protocol的簡稱,稱為用戶數(shù)據(jù)報協(xié)議,TCP是Transmission Control Protocol的簡稱,稱為傳輸控制協(xié)議。
UDP協(xié)議介紹
UDP是無連接通信協(xié)議,即在數(shù)據(jù)傳輸時,數(shù)據(jù)的發(fā)送端和接收端不建立邏輯連接。簡單來說,當一臺計算機向另外一臺計算機發(fā)送數(shù)據(jù)時,發(fā)送端不會確認接收端是否存在,就會發(fā)出數(shù)據(jù),同樣接收端在收到數(shù)據(jù)時,也不會向發(fā)送端反饋是否收到數(shù)據(jù)。由于使用UDP協(xié)議消耗資源小,通信效率高,所以通常都會用于音頻、視頻和普通數(shù)據(jù)的傳輸例如視頻會議都使用UDP協(xié)議,因為這種情況即使偶爾丟失一兩個數(shù)據(jù)包,也不會對接收結(jié)果產(chǎn)生太大影響。但是在使用UDP協(xié)議傳送數(shù)據(jù)時,由于UDP的面向無連接性,不能保證數(shù)據(jù)的完整性,因此在傳輸重要數(shù)據(jù)時不建議使用UDP協(xié)議。UDP的交換過程如下圖所示。
TCP協(xié)議介紹
TCP協(xié)議是面向連接的通信協(xié)議,即在傳輸數(shù)據(jù)前先在發(fā)送端和接收端建立邏輯連接,然后再傳輸數(shù)據(jù),它提供了兩臺計算機之間可靠無差錯的數(shù)據(jù)傳輸。在TCP連接中必須要明確客戶端與服務(wù)器端,由客戶端向服務(wù)端發(fā)出連接請求,每次連接的創(chuàng)建都需要經(jīng)過“三次握手”。第一次握手,客戶端向服務(wù)器端發(fā)出連接請求,等待服務(wù)器確認,第二次握手,服務(wù)器端向客戶端回送一個響應,通知客戶端收到了連接請求,第三次握手,客戶端再次向服務(wù)器端發(fā)送確認信息,確認連接。整個交互過程如下圖所示。
由于TCP協(xié)議的面向連接特性,它可以保證傳輸數(shù)據(jù)的安全性,所以是一個被廣泛采用的協(xié)議,例如在下載文件時,如果數(shù)據(jù)接收不完整,將會導致文件數(shù)據(jù)丟失而不能被打開,因此,下載文件時必須采用TCP協(xié)議。
UDP通信
DatagramPacket
前面介紹了UDP是一種面向無連接的協(xié)議,因此,在通信時發(fā)送端和接收端不用建立連接。UDP通信的過程就像是貨運公司在兩個碼頭間發(fā)送貨物一樣。在碼頭發(fā)送和接收貨物時都需要使用集裝箱來裝載貨物,UDP通信也是一樣,發(fā)送和接收的數(shù)據(jù)也需要使用“集裝箱”進行打包,為此JDK中提供了一個DatagramPacket類,該類的實例對象就相當于一個集裝箱,用于封裝UDP通信中發(fā)送或者接收的數(shù)據(jù)。
想要創(chuàng)建一個DatagramPacket對象,首先需要了解一下它的構(gòu)造方法。在創(chuàng)建發(fā)送端和接收端的DatagramPacket對象時,使用的構(gòu)造方法有所不同,接收端的構(gòu)造方法只需要接收一個字節(jié)數(shù)組來存放接收到的數(shù)據(jù),而發(fā)送端的構(gòu)造方法不但要接收存放了發(fā)送數(shù)據(jù)的字節(jié)數(shù)組,還需要指定發(fā)端口號送端IP地址和。接下來根據(jù)API文檔的內(nèi)容,對DatagramPacket的構(gòu)造方法進行逐一詳細地講解。
- DatagramPacket(byte[] buf,int length):
使用該構(gòu)造方法在創(chuàng)建DatagramPacket對象時,指定了封裝數(shù)據(jù)的字節(jié)數(shù)組和數(shù)據(jù)的大小,沒有指定IP地址和端口號。很明顯,這樣的對象只能用于接收端,不能用于發(fā)送端。因為發(fā)送端一定要明確指出數(shù)據(jù)的目的地(ip地址和端口號),而接收端不需要明確知道數(shù)據(jù)的來源,只需要接收到數(shù)據(jù)即可。
- DatagramPacket(byte[] buf,int length,InetAddress addr,int port):
使用該構(gòu)造方法在創(chuàng)建DatagramPacket對象時,不僅指定了封裝數(shù)據(jù)的字節(jié)數(shù)組和數(shù)據(jù)的大小,還指定了數(shù)據(jù)包的目標IP地址(addr)和端口號(port)。該對象通常用于發(fā)送端,因為在發(fā)送數(shù)據(jù)時必須指定接收端的IP地址和端口號,就好像發(fā)送貨物的集裝箱上面必須標明接收人的地址一樣。
上面我們講解了DatagramPacket的構(gòu)造方法,接下來對DatagramPacket類中的常用方法進行詳細地講解,如下表所示。
方法聲明 | 功能描述 |
InetAddress getAddress() | 該方法用于返回發(fā)送端或者接收端的IP地址,如果是發(fā)送端的DatagramPacket對象,就返回接收端的IP地址,反之,就返回發(fā)送端的IP地址 |
int getPort() | 該方法用于返回發(fā)送端或者接收端的端口號,如果是發(fā)送端的DatagramPacket對象,就返回接收端的端口號,反之,就返回發(fā)送端的端口號 |
byte[] getData() | 該方法用于返回將要接收或者將要發(fā)送的數(shù)據(jù),如果是發(fā)送端的DatagramPacket對象,就返回將要發(fā)送的數(shù)據(jù),反之,就返回接收到的數(shù)據(jù) |
int getLength() | 該方法用于返回接收或者將要發(fā)送數(shù)據(jù)的長度,如果是發(fā)送端的DatagramPacket對象,就返回將要發(fā)送的數(shù)據(jù)長度,反之,就返回接收到數(shù)據(jù)的長度 |
Datagramsocket
DatagramPacket數(shù)據(jù)包的作用就如同是“集裝箱”,可以將發(fā)送端或者接收端的數(shù)據(jù)封裝起來。然而運輸貨物只有“集裝箱”是不夠的,還需要有碼頭。在程序中需要實現(xiàn)通信只有DatagramPacket數(shù)據(jù)包也同樣不行,為此JDK中提供的一個DatagramSocket類。DatagramSocket類的作用就類似于碼頭,使用這個類的實例對象就可以發(fā)送和接收DatagramPacket數(shù)據(jù)包,發(fā)送數(shù)據(jù)的過程如下圖所示。
在創(chuàng)建發(fā)送端和接收端的DatagramSocket對象時,使用的構(gòu)造方法也有所不同,下面對DatagramSocket類中常用的構(gòu)造方法進行講解。
- DatagramSocket():
該構(gòu)造方法用于創(chuàng)建發(fā)送端的DatagramSocket對象,在創(chuàng)建DatagramSocket對象時,并沒有指定端口號,此時,系統(tǒng)會分配一個沒有被其它網(wǎng)絡(luò)程序所使用的端口號。
- DatagramSocket(int port):
該構(gòu)造方法既可用于創(chuàng)建接收端的DatagramSocket對象,又可以創(chuàng)建發(fā)送端的DatagramSocket對象,在創(chuàng)建接收端的DatagramSocket對象時,必須要指定一個端口號,這樣就可以監(jiān)聽指定的端口。
上面我們講解了DatagramSocket的構(gòu)造方法,接下來對DatagramSocket類中的常用方法進行詳細地講解。
方法聲明 | 功能描述 |
void receive(DatagramPacket p) | 該方法用于將接收到的數(shù)據(jù)填充到DatagramPacket數(shù)據(jù)包中,在接收到數(shù)據(jù)之前會一直處于阻塞狀態(tài),只有當接收到數(shù)據(jù)包時,該方法才會返回 |
void send(DatagramPacket p) | 該方法用于發(fā)送DatagramPacket數(shù)據(jù)包,發(fā)送的數(shù)據(jù)包中包含將要發(fā)送的數(shù)據(jù)、數(shù)據(jù)的長度、遠程主機的IP地址和端口號 |
void close() | 關(guān)閉當前的Socket,通知驅(qū)動程序釋放為這個Socket保留的資源 |
UDP網(wǎng)絡(luò)程序
講解了DatagramPacket和DatagramSocket的作用,接下來通過一個案例來學習一下它們在程序中的具體用法。
下圖為UDP發(fā)送端與接收端交互圖解,原圖為UDP圖解.bmp
要實現(xiàn)UDP通信需要創(chuàng)建一個發(fā)送端程序和一個接收端程序,很明顯,在通信時只有接收端程序先運行,才能避免因發(fā)送端發(fā)送的數(shù)據(jù)無法接收,而造成數(shù)據(jù)丟失。因此,首先需要來完成接收端程序的編寫。
/* * 使用UDP完成數(shù)據(jù)的發(fā)送 * 發(fā)送端 * 1,創(chuàng)建DatagramSocket對象 * 2,創(chuàng)建DatagramPacket對象,并封裝數(shù)據(jù) * 3,發(fā)送數(shù)據(jù) * 4,釋放流資源 */ public class UDPSend { public static void main(String[] args) throws IOException { //1,創(chuàng)建DatagramSocket對象 DatagramSocket sendSocket = new DatagramSocket(); //2,創(chuàng)建DatagramPacket對象,并封裝數(shù)據(jù) //public DatagramPacket(byte[] buf, int length, InetAddress address, int port) //構(gòu)造數(shù)據(jù)報包,用來將長度為 length 的包發(fā)送到指定主機上的指定端口號。 byte[] buffer = "hello,UDP".getBytes(); DatagramPacket dp = new DatagramPacket(buffer, bu, Ine("192.168.74.58"), 12306); //3,發(fā)送數(shù)據(jù) //public void send(DatagramPacket p) 從此套接字發(fā)送數(shù)據(jù)報包 (dp); //4,釋放流資源 (); } } /* * 使用UDP完成數(shù)據(jù)的發(fā)送 * 發(fā)送端 * 1,創(chuàng)建DatagramSocket對象 * 2,創(chuàng)建DatagramPacket對象,并封裝數(shù)據(jù) * 3,發(fā)送數(shù)據(jù) * 4,釋放流資源 */ public class UDPSend { public static void main(String[] args) throws IOException { //1,創(chuàng)建DatagramSocket對象 DatagramSocket sendSocket = new DatagramSocket(); //2,創(chuàng)建DatagramPacket對象,并封裝數(shù)據(jù) //public DatagramPacket(byte[] buf, int length, InetAddress address, int port) //構(gòu)造數(shù)據(jù)報包,用來將長度為 length 的包發(fā)送到指定主機上的指定端口號。 byte[] buffer = "hello,UDP".getBytes(); DatagramPacket dp = new DatagramPacket(buffer, bu, Ine("192.168.74.58"), 12306); //3,發(fā)送數(shù)據(jù) //public void send(DatagramPacket p) 從此套接字發(fā)送數(shù)據(jù)報包 (dp); //4,釋放流資源 (); } }
TCP通信
學習了如何實現(xiàn)UDP通信,再來學習下實現(xiàn)TCP通信。TCP通信同UDP通信一樣,都能實現(xiàn)兩臺計算機之間的通信,通信的兩端都需要創(chuàng)建socket對象。區(qū)別在于,UDP中只有發(fā)送端和接收端,不區(qū)分客戶端與服務(wù)器端,計算機之間可以任意地發(fā)送數(shù)據(jù)。而TCP通信是嚴格區(qū)分客戶端與服務(wù)器端的,在通信時,必須先由客戶端去連接服務(wù)器端才能實現(xiàn)通信,服務(wù)器端不可以主動連接客戶端,并且服務(wù)器端程序需要事先啟動,等待客戶端的連接。
在JDK中提供了兩個類用于實現(xiàn)TCP程序,一個是ServerSocket類,用于表示服務(wù)器端,一個是Socket類,用于表示客戶端。通信時,首先創(chuàng)建代表服務(wù)器端的ServerSocket對象,該對象相當于開啟一個服務(wù),并等待客戶端的連接,然后創(chuàng)建代表客戶端的Socket對象向服務(wù)器端發(fā)出連接請求,服務(wù)器端響應請求,兩者建立連接開始通信。整個通信過程如圖所示。原圖TCP圖解1.bmp
ServerSocket
通過前面的學習知道,在開發(fā)TCP程序時,首先需要創(chuàng)建服務(wù)器端程序。JDK的java.net包中提供了一個ServerSocket類,該類的實例對象可以實現(xiàn)一個服務(wù)器段的程序。通過查閱API文檔可知,ServerSocket類提供了多種構(gòu)造方法,接下來就對ServerSocket的構(gòu)造方法進行逐一地講解。
- ServerSocket(int port):
使用該構(gòu)造方法在創(chuàng)建ServerSocket對象時,就可以將其綁定到一個指定的端口號上(參數(shù)port就是端口號)。端口號可以指定為0,此時系統(tǒng)就會為我們分配一個還沒有被其它網(wǎng)絡(luò)程序所使用的端口號。由于客戶端需要根據(jù)指定的端口號來訪問服務(wù)器端程序,因此端口號隨機分配的情況并不常用,通常都會讓服務(wù)器端程序監(jiān)聽一個指定的端口號。
在以上介紹的構(gòu)造方法中,第二個構(gòu)造方法是最常使用的。了解了如何通過ServerSocket的構(gòu)造方法創(chuàng)建對象,接下來學習一下ServerSocket的常用方法,如表所示。
方法聲明 | 功能描述 |
Socket accept() | 該方法用于等待客戶端的連接,在客戶端連接之前一直處于阻塞狀態(tài),如果有客戶端連接就會返回一個與之對應的Socket對象 |
InetAddress getInetAddress() | 該方法用于返回一個InetAddress對象,該對象中封裝了ServerSocket綁定的IP地址 |
ServerSocket對象負責監(jiān)聽某臺計算機的某個端口號,在創(chuàng)建ServerSocket對象后,需要繼續(xù)調(diào)用該對象的accept()方法,接收來自客戶端的請求。當執(zhí)行了accept()方法之后,服務(wù)器端程序會發(fā)生阻塞,直到客戶端發(fā)出連接請求,accept()方法才會返回一個Scoket對象用于和客戶端實現(xiàn)通信,程序才能繼續(xù)向下執(zhí)行。
Socket
講解了ServerSocket對象可以實現(xiàn)服務(wù)端程序,但只實現(xiàn)服務(wù)器端程序還不能完成通信,此時還需要一個客戶端程序與之交互,為此JDK提供了一個Socket類,用于實現(xiàn)TCP客戶端程序。通過查閱API文檔可知Socket類同樣提供了多種構(gòu)造方法,接下來就對Socket的常用構(gòu)造方法進行詳細講解。
1、Socket()
使用該構(gòu)造方法在創(chuàng)建Socket對象時,并沒有指定IP地址和端口號,也就意味著只創(chuàng)建了客戶端對象,并沒有去連接任何服務(wù)器。通過該構(gòu)造方法創(chuàng)建對象后還需調(diào)用connect(SocketAddress endpoint)方法,才能完成與指定服務(wù)器端的連接,其中參數(shù)endpoint用于封裝IP地址和端口號。
2、Socket(String host, int port)
使用該構(gòu)造方法在創(chuàng)建Socket對象時,會根據(jù)參數(shù)去連接在指定地址和端口上運行的服務(wù)器程序,其中參數(shù)host接收的是一個字符串類型的IP地址。
3、Socket(InetAddress address, int port)
該方法在使用上與第二個構(gòu)造方法類似,參數(shù)address用于接收一個InetAddress類型的對象,該對象用于封裝一個IP地址。
在以上Socket的構(gòu)造方法中,最常用的是第一個構(gòu)造方法。了解了Socket構(gòu)造方法的用法,接下來學習一下Socket的常用方法,如表所示。
方法聲明 | 功能描述 |
int getPort() | 該方法返回一個int類型對象,該對象是Socket對象與服務(wù)器端連接的端口號 |
InetAddress getLocalAddress() | 該方法用于獲取Socket對象綁定的本地IP地址,并將IP地址封裝成InetAddress類型的對象返回 |
void close() | 該方法用于關(guān)閉Socket連接,結(jié)束本次通信。在關(guān)閉socket之前,應將與socket相關(guān)的所有的輸入/輸出流全部關(guān)閉,這是因為一個良好的程序應該在執(zhí)行完畢時釋放所有的資源 |
InputStream getInputStream() | 該方法返回一個InputStream類型的輸入流對象,如果該對象是由服務(wù)器端的Socket返回,就用于讀取客戶端發(fā)送的數(shù)據(jù),反之,用于讀取服務(wù)器端發(fā)送的數(shù)據(jù) |
OutputStream getOutputStream() | 該方法返回一個OutputStream類型的輸出流對象,如果該對象是由服務(wù)器端的Socket返回,就用于向客戶端發(fā)送數(shù)據(jù),反之,用于向服務(wù)器端發(fā)送數(shù)據(jù) |
在Socket類的常用方法中,getInputStream()和getOutStream()方法分別用于獲取輸入流和輸出流。當客戶端和服務(wù)端建立連接后,數(shù)據(jù)是以IO流的形式進行交互的,從而實現(xiàn)通信。接下來通過一張圖來描述服務(wù)器端和客戶端的數(shù)據(jù)傳輸,如下圖所示。
簡單的TCP網(wǎng)絡(luò)程序
了解了ServerSocket、Socket類的基本用法,為了讓大家更好地掌握這兩個類的使用,接下來通過一個TCP通信的案例來進一步學習。如圖所示。原圖TCP圖解2.bmp
要實現(xiàn)TCP通信需要創(chuàng)建一個服務(wù)器端程序和一個客戶端程序,為了保證數(shù)據(jù)傳輸?shù)陌踩?,首先需要實現(xiàn)服務(wù)器端程序。
/* * TCP 服務(wù)器端 * * 1,創(chuàng)建服務(wù)器ServerSocket對象(指定服務(wù)器端口號) * 2,開啟服務(wù)器了,等待客戶端的連接,當客戶端連接后,可以獲取到連接服務(wù)器的客戶端Socket對象 * 3,給客戶端反饋信息 * 4,關(guān)閉流資源 */ public class TCPServer { public static void main(String[] args) throws IOException { //1,創(chuàng)建服務(wù)器ServerSocket對象(指定服務(wù)器端口號) ServerSocket ss = new ServerSocket(8888); //2,開啟服務(wù)器了,等待客戶端的連接,當客戶端連接后,可以獲取到連接服務(wù)器的客戶端Socket對象 Socket s = (); //3,給客戶端反饋信息 /* * a,獲取客戶端的輸出流 * b,在服務(wù)端端,通過客戶端的輸出流寫數(shù)據(jù)給客戶端 */ //a,獲取客戶端的輸出流 OutputStream out = s.getOutputStream(); //b,在服務(wù)端端,通過客戶端的輸出流寫數(shù)據(jù)給客戶端 out.write("你已經(jīng)連接上了服務(wù)器".getBytes()); //4,關(guān)閉流資源 out.close(); s.close(); (); 服務(wù)器流 通常都是不關(guān)閉的 } } 完成了服務(wù)器端程序的編寫,接下來編寫客戶端程序。 /* * TCP 客戶端 * * 1,創(chuàng)建客戶端Socket對象,(指定要連接的服務(wù)器地址與端口號) * 2,獲取服務(wù)器端的反饋回來的信息 * 3,關(guān)閉流資源 */ public class TCPClient { public static void main(String[] args) throws IOException { //1,創(chuàng)建客戶端Socket對象,(指定要連接的服務(wù)器地址與端口號) Socket s = new Socket("192.168.74.58", 8888); //2,獲取服務(wù)器端的反饋回來的信息 InputStream in = s.getInputStream(); //獲取獲取流中的數(shù)據(jù) byte[] buffer = new byte[1024]; //把流中的數(shù)據(jù)存儲到數(shù)組中,并記錄讀取字節(jié)的個數(shù) int length = in.read(buffer); //顯示數(shù)據(jù) Sy( new String(buffer, 0 , length) ); //3,關(guān)閉流資源 in.close(); s.close(); } }
TCP案例——文件上傳
目前大多數(shù)服務(wù)器都會提供文件上傳的功能,由于文件上傳需要數(shù)據(jù)的安全性和完整性,很明顯需要使用TCP協(xié)議來實現(xiàn)。接下來通過一個案例來實現(xiàn)圖片上傳的功能。如下圖所示。原圖:文件上傳.bmp
- 首先編寫服務(wù)器端程序,用來接收圖片。
/* * 文件上傳 服務(wù)器端 * */ public class TCPServer { public static void main(String[] args) throws IOException { //1,創(chuàng)建服務(wù)器,等待客戶端連接 ServerSocket serverSocket = new ServerSocket(8888); Socket clientSocket = (); //顯示哪個客戶端Socket連接上了服務(wù)器 InetAddress ipObject = clien();//得到IP地址對象 String ip = i(); //得到IP地址字符串 Sy("小樣,抓到你了,連接我??!" + "IP:" + ip); //7,獲取Socket的輸入流 InputStream in = clien(); //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é)輸出流中 (buffer, 0, len); } //-----------------反饋信息--------------------- //10,獲取Socket的輸出流, 作用:寫反饋信息給客戶端 OutputStream out = clien(); //11,寫反饋信息給客戶端 out.write("圖片上傳成功".getBytes()); out.close(); (); in.close(); clien(); (); } } 編寫客戶端,完成上傳圖片 /* * 文件上傳 客戶端 * * public void shutdownOutput() 禁用此Socket的輸出流,間接的相當于告知了服務(wù)器數(shù)據(jù)寫入完畢 */ public class TCPClient { public static void main(String[] args) throws IOException { //2,創(chuàng)建客戶端Socket,連接服務(wù)器 Socket socket = new Socket("192.168.74.58", 8888); //3,獲取Socket流中的輸出流,功能:用來把數(shù)據(jù)寫到服務(wù)器 OutputStream out = (); //4,創(chuàng)建字節(jié)輸入流,功能:用來讀取數(shù)據(jù)源(圖片)的字節(jié) BufferedInputStream fileIn = new BufferedInputStream(new FileInputStream("D:\\NoDir\\;)); //5,把圖片數(shù)據(jù)寫到Socket的輸出流中(把數(shù)據(jù)傳給服務(wù)器) byte[] buffer = new byte[1024]; int len = -1; while ((len = (buffer)) != -1){ //把數(shù)據(jù)寫到Socket的輸出流中 out.write(buffer, 0, len); } //6,客戶端發(fā)送數(shù)據(jù)完畢,結(jié)束Socket輸出流的寫入操作,告知服務(wù)器端 (); //-----------------反饋信息--------------------- //12,獲取Socket的輸入流 作用: 讀反饋信息 InputStream in = (); //13,讀反饋信息 byte[] info = new byte[1024]; //把反饋信息存儲到info數(shù)組中,并記錄字節(jié)個數(shù) int length = in.read(info); //顯示反饋結(jié)果 Sy( new String(info, 0, length) ); //關(guān)閉流 in.close(); (); out.close(); (); } }
總結(jié)
知識點總結(jié)
- IP地址:用來唯一表示我們自己的電腦的,是一個網(wǎng)絡(luò)標示
- 端口號: 用來區(qū)別當前電腦中的應用程序的
- UDP: 傳送速度快,但是容易丟數(shù)據(jù),如視頻聊天,語音聊天
- TCP: 傳送穩(wěn)定,不會丟失數(shù)據(jù),如文件的上傳、下載
- UDP程序交互的流程發(fā)送端
1,創(chuàng)建DatagramSocket對象
2,創(chuàng)建DatagramPacket對象,并封裝數(shù)據(jù)
3,發(fā)送數(shù)據(jù)
4,釋放流資源
- 接收端
1,創(chuàng)建DatagramSocket對象
2,創(chuàng)建DatagramPacket對象
3,接收數(shù)據(jù)存儲到DatagramPacket對象中
4,獲取DatagramPacket對象的內(nèi)容
5,釋放流資源
- TCP程序交互的流程客戶端
1,創(chuàng)建客戶端的Socket對象
2,獲取Socket的輸出流對象
3,寫數(shù)據(jù)給服務(wù)器
4,獲取Socket的輸入流對象
5,使用輸入流,讀反饋信息
6,關(guān)閉流資源
- 服務(wù)器端
1,創(chuàng)建服務(wù)器端ServerSocket對象,指定服務(wù)器端端口號
2,開啟服務(wù)器,等待著客戶端Socket對象的連接,如有客戶端連接,返回客戶端的Socket對象
3,通過客戶端的Socket對象,獲取客戶端的輸入流,為了實現(xiàn)獲取客戶端發(fā)來的數(shù)據(jù)
4,通過客戶端的輸入流,獲取流中的數(shù)據(jù)
5,通過客戶端的Socket對象,獲取客戶端的輸出流,為了實現(xiàn)給客戶端反饋信息
6,通過客戶端的輸出流,寫數(shù)據(jù)到流中
7,關(guān)閉流資源
1.《關(guān)于怎么查看應用程序的端口號,你需要知道這些網(wǎng)絡(luò)編程、Socket套接字編程》援引自互聯(lián)網(wǎng),旨在傳遞更多網(wǎng)絡(luò)信息知識,僅代表作者本人觀點,與本網(wǎng)站無關(guān),侵刪請聯(lián)系頁腳下方聯(lián)系方式。
2.《關(guān)于怎么查看應用程序的端口號,你需要知道這些網(wǎng)絡(luò)編程、Socket套接字編程》僅供讀者參考,本網(wǎng)站未對該內(nèi)容進行證實,對其原創(chuàng)性、真實性、完整性、及時性不作任何保證。
3.文章轉(zhuǎn)載時請保留本站內(nèi)容來源地址,http://f99ss.com/gl/3162522.html