java學習筆記:網絡編程

java學習筆記:網絡編程

 

第二部分 網絡編程
一、基本概念介紹
1.網絡三要素 :IP地址,端口,協議
TCP:Transmission Control Protocol    傳輸控制協議
IP:Internet Protocol  因特網互聯協議

2.網絡模型簡介
TCP/IP協議組的層次結構 :
OSI(Open System Interconnection開放系統互連)參考模型 :這個模型將網絡的不同功能劃分成爲7層。
OSI參考模型:物理層、數據鏈路層、網絡層、傳輸層、會話層、表示層、應用層。
TCP/IP模型:網絡接口、網絡層、傳輸層、應用層。

 

應用層:如HTTP, FTP, DNS。作用:主要負責應用程序的協議,如HTTP協議,FTP協議等。主要是一些終端的應用。
傳輸層:如TCP, UDP。作用:主要使網絡程序進行通信,在進行網絡通信時,可以採用 TCP協議,也可以採用UDP協議。
網絡層:如IP, ICMP, IGMP。作用:網絡層是整個TCP/IP協議的核心,它主要用於將傳輸的數據進行分組,將分組的數據發送到指定的計算機或者網絡。
鏈路層:如驅動程序,接口。作用:鏈路層用於定義物理傳輸通道,通常是對某些網絡連接設備的驅動協議,例如針對光纖、雙絞線提供的驅動。

說明 :我們所講的網絡編程,主要涉及的是傳輸層的 TCP,UDP協議和網絡層的 IP協議.

3.IP地址和端口號
IP地址可以唯一標識一臺計算機,目前,IP地址廣泛使用的版本是IPv4,他是由 4 個字節代表的二進制數來表示.比如:   11000000 10101000 00000000 00000001   192.168.0.1
由於二進制形式表示的IP地址非常不便於記憶和喫力,因此通常會將IP地址寫成十進制的形式,每個字節用一個十進制數字表示,數字間用符號 .分開.比如: 192.168.0.1
IPv6使用 16 個字節表示 IP地址,它所擁有的地址容量約是IPv4的 8 * 10的28倍

在計算機中,不同的應用程序是通過端口號區分的.端口號是用兩個字節 (16位的二進制數)表示的,它的取值是 0~65535,其中, 0~1023 之間的端口號用於一些知名的網絡服務和應用,(如:telnet佔用端口23,HTTP佔用端口80等)用戶的普通應用程序需要使用 1024 以上的端口號,從而避免端口號衝突.

網絡通信使用 IP地址標識網絡中的一臺計算機,使用端口號標識計算中的進程(程序).
進程號 :系統分配的.
端口號 :程序分配的.
一個進程中可以分配多個端口.

位於網絡中的一臺計算機可以通過IP地址去訪問另一臺計算機,並通過端口號訪問目標計算機中的某一個應用程序.

查看ip地址的命令:ipconfig,然後找到IPv4地址這一欄,就是本機的ip地址了。
查看所有進程的ip地址和對應的端口:netstat -na

4.IP對象介紹
InetAddress類用來封裝我們前面講解的數字式IP地址和該地址的域名.你通過一個IP主機名與這個類發生作用,IP主機名比它的IP地址用起來更簡單更容易理解.
    InetAddress類內部隱藏了地址數字.
InetAddress類的工廠方法: (靜態方法)
    InetAddress類沒有明顯的構造方法,爲生成一個InetAddress對象,必須運用一個可用的工廠方法.工廠方法僅是一個類中靜態方法返回一個該類實例的約定,對於 InetAddress,三個方法 getLocalHost(), getByName() 以及 getAllByName() 可以用來創建 InetAddress的實例

java.lang.Object
|_ java.net.InetAddress

public class InetAddress extends Object implements Serializable//此類表示互聯網協議(IP)地址。
static InetAddress getLocalHost()//返回本地主機
static InetAddress getByName(String host)//在給定主機名的情況下確定主機的IP地址。
static InetAddress getAllByName(String host)//在給定主機名的情況下,根據系統上配置的名稱服務返回其IP地址所組成的數組。
在Internet上.用一個名稱來代表多個機器是常用的事,getAllByName()方法返回代表由一個特殊名稱分解的所有地址的InetAddress類數組.在不能把名稱分解成至少一個地址時,它將引發一個UnknownHostException異常.

代碼實現 :
import java.io.IOException;
import java.net.InetAddress;

public class Demo01 {
    public static void main(String[] args) throws IOException{
        //InetAddress類的基本演示
        //1.getLocalHost
        InetAddress localHost = InetAddress.getLocalHost();
        System.out.println("本地的IP地址:" + localHost.getHostAddress());
        System.out.println("本地主機的名稱:" + localHost.getHostName());

        //2.getByName()
        InetAddress itcast = InetAddress.getByName("www.itcast.cn");
        System.out.println(itcast);

        //3.getAllByName()
        InetAddress[] baidus = InetAddress.getAllByName("www.baidu.com");
        for(InetAddress baidu : baidus){
            System.out.println(baidu);
        }
    }
}

5.協議介紹
在所示的 TCP/IP結構時,提到傳輸層的兩個重要的高級協議,分別是 UDP和 TCP協議,其中 UDP是 User Datagram Protocol 的簡稱,稱爲用戶數據報協議,TCP是 Transmission Control Protocol 的簡稱,稱爲傳輸控制協議.
(1)UDP 用戶數據報協議
UDP是無連接通信協議,即在數據傳輸時,發送端和接收端不建立邏輯連接.簡單來說,當一臺計算機向另外一臺計算機發送數據時,發送端不會確認接收端是否存在,就會發出數據.同樣接收端在接收數據時,也不會向發送端反饋是否收到數據.由於使用 UDP協議消耗資源小,通信效率高,所以通常都會用於音頻,視頻和普通數據的傳輸,例如視頻會議都使用UDP協議,因爲這種情況即使偶爾丟失一兩個數據包,也不會對接收結果產生太大影響.但是在使用 UDP協議傳送數據時,由於UDP面向無連接,不能保證數據的完整性,因此在傳輸重要數據時不建議使用 UDP協議.
特點 :兩端都相同,可以發送數據,同時也可以接收數據.無明顯的差異.

(2)TCP傳輸控制協議
TCP協議是面向連接的通信協議,即在傳輸數據前先在發送端和接收端建立邏輯連接,然後再傳輸數據,它提供了兩臺計算機之間可靠無差錯的數據傳輸.在TCP連接中必須要明確客戶端和服務器端, 每次連接的創建都需要經過 `三次握手`,第一次握手,客戶端向服務端發出連接請求,等待服務器確認;第二次握手,服務器向客戶端回送一個響應,通知客戶端收到了連接請求;第三次握手,客戶端再次向服務器端發送確認信息,確認連接.
由於TCP協議的面向連接性,它可以保證傳輸數據的安全性,所以是一個被廣泛採用的協議,例如在下載文件時,如果數據接收不完整,將會導致文件數據丟失而不能打開,因此,下載文件時必須採用TCP協議.

(3)兩種傳輸協議的簡單對比
1_使用UDP時,每個數據包中都給出了完整的地址信息,因此不需要建立發送方和接收方法的連接.
2_對於 TCP協議,由於它是一個面向連接的協議,在Socket之間進行數據傳輸之前必然要建立連接.所以在TCP中多了一個連接的建立時間.
3_使用UDP傳輸數據時是有大小限制的,每個被傳輸的數據報必須限定在 64KB之內.
4_TCP沒有這方面的限制,一旦連接建立起來,雙方的socket就可以按統一的格式傳輸大量的數據.
5_UDP是一個不可靠的協議,發送方所發送的數據報不一定以相同的次序到達接收方.
6_TCP是一個可靠的協議,它確保接收方完全正確地獲取發送方所發送的全部數據.
UDP 無連接,效率高.
TCP有連接,安全高.

6.套接字 (Socket)
Socket是連接運行在網絡上的兩個程序間的雙向通訊的端點. 可以理解爲套接字就是由端口號與IP地址的組合得出一個網絡套接字.
TCP通信是嚴格區分客戶端和服務器端的,在通信時,必須先由客戶端去連接服務器端才能實現通信,服務器端不可以主動連接客戶端,並且服務器端程序需要事先啓動,等待客戶端的連接.
在JDK中提供了兩個類用於實現TCP程序,一個是 ServerSocket類.用於表示服務器端,一個是Socket類,用於表示客戶端.通信時,首先創建代表服務器端的ServerSocket對象,該對象相當於開啓一個服務,並等待客戶端的連接,然後創建代表客戶端的Socket對象,向服務器端發送連接請求,服務器端響應請求.兩者建立連接開始通信.

(1)ServerSocket 服務器套接字
在開發TCP程序時,首先需要創建服務器端程序,JDK的java.net包中提供了一個ServerSocket類,該類的實例對象可以實現一個服務器端的程序.
java.lang.Object
|_java.net.ServerSocket
直接已知子類:SSLServerSocket

public class ServerSocket extends Object//此類實現服務器套接字。服務器套接字等待請求通過網絡傳入。它基於該請求執行某些操作,然後可能向請求者返回結果。
構造方法:ServerSocket(int port)//創建綁定到特定端口的服務器套接字

ServerSocket使用該構造方法在創建對象時,就可以將其綁定到一個指定的端口號上.端口號可以指定爲0,此時系統就會分配一個還沒有被其它網絡程序所使用的端口號,由於客戶端需要指定的端口號來訪問服務器端程序,因此端口號隨機分配的情況並不常用,通常都會讓服務器端程序監聽一個指定的端口號.
Socket accept()//真挺並接受到此套接字的連接。
InetAddress getInetAddress()//返回此服務器套接字的本地地址
void close()// 關閉此套接字
ServerSocket對象負責監聽某臺計算機的某個端口號,在創建ServerSocket對象後,需要繼續調用該對象的accept()方法,接收來自客戶端的請求,當執行了accept()方法之後,服務器端程序會發生阻塞,直到客戶端發出連接請求,accept()方法纔會返回一個Socket對象用於和客戶端實現通信,程序才能繼續向下執行.

(2)Socket 客戶端套接字
ServerSocket對象可以實現服務器端程序,但只實現服務器端程序還不能完成通信,此時還需要一個客戶端程序與之交互,爲此,JDK提供了一個Socket類,用於實現TCP客戶端程序.
java.lang.Object
|_java.net.Socket
直接已知子類:SSLSocket

public class Socket extends Object//此類實現客戶端套接字(也可以就叫“套接字”)。套接字是兩臺機器間通信的斷點。
構造方法:
Socket(String host, int port)//創建一個流套接字,並將其連接到指定主機上的指定端口號。
Socket(String host, int port);使用該構造方法在創建Socket對象時,會根據參數去連接在指定地址和端口上運行的服務器程序,其中參數host接收的是一個字符串類型的IP地址.
Socket(InetAddress address, int port)//創建一個流套接字,並將其連接到指定IP地址的指定端口號。
Socket(InetAddressaddress, int port);該方法和上面這個構造方法類似,參數address用於接收一個InetAddress類型的對象,該對象用於封裝一個IP地址.

void close()//關閉此套接字
InetAddress getInetAddress()//返回套接字連接的地址
int getPort()//返回此套接字連接到的遠程端口
InputStream getInputStream()//返回此套接字的輸入流
OutputStream getOutputStream()//返回此套接字的輸出流
在方法摘要中,getInputStrean() 和 getOutputStream() 方法分別用於獲取輸入流和輸出流,當客戶端和服務端建立連接後,數據是以 IO流的形式進行交互的,從而實現通信.
void shutdownInput()//此套接字的輸入流置於“流的末尾”
void shutdownOutput()//禁用此套接字的輸出流


(3)簡單的TCP網絡程序
見文件:客戶端和服務器端的通信圖.png

 

TCP服務端代碼 :
//file name: TCPServerDemo.java
//TCP服務端代碼
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Date;

public class TCPServerDemo {
    public static void main(String[] args) throws Exception{
        //1.創建服務端
        ServerSocket serverSocket = new ServerSocket(8888);
        //2.等待客戶端的連接
        Socket socket = serverSocket.accept();
        //3.解析客戶端的內容
        //3.1 獲取IP地址
        String ip = socket.getInetAddress().getHostAddress();
        //3.2 獲取客戶端輸入的內容
        InputStream in = socket.getInputStream();
        int len;
        byte[] buf = new byte[1024];
        while((len = in.read(buf)) != -1){
            System.out.println(ip + "(" + new Date().toLocaleString() + ")");
            String str = new String(buf, 0, len);
            System.out.println(str);
        }

        //5. 給客戶端返回內容
        OutputStream out = socket.getOutputStream();
        out.write("身體還硬朗,就是想出去走走!".getBytes());
        socket.shutdownOutput();

        //4. 關閉服務端
        serverSocket.close();
    }
}


//file name: TCPClientDemo.java
//TCP客戶端代碼

import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;

public class TCPClientDemo {
    public static void main(String[] args) throws Exception{
        //1.建立客戶端
        Socket socket = new Socket("localhost", 8888);
        //2.給服務端輸出內容
        OutputStream out = socket.getOutputStream();
        out.write("我是客戶端,您最近還好嗎?".getBytes());
        socket.shutdownOutput();
        //4.獲取服務端返回的內容
        InputStream in = socket.getInputStream();
        int len;
        byte[] buf = new byte[1024];
        while((len = in.read(buf)) != -1){
            String str = new String(buf, 0, len);
            System.out.println("服務端返回內容:" + str);
        }

        //3.關閉客戶端
        socket.close();
    }
}

重點說明 :向socket通道中發送完畢數據後需要調用Socket的shutdownOutput() 方法關閉客戶端的輸出流.需要注意的是,shutdownOutput() 方法非常重要,因爲服務器端程序在while循環中讀取客戶端發送的數據,當讀取到 -1 時纔會結束循環,如果在客戶端不調用 shutdownOutput() 方法關閉輸出流,服務器端就不會讀到 -1,而會一直執行while循環,同時客戶端讀取服務器端數據的read(byte[] buf); 方法也是一個阻塞方法,這樣服務器端和客戶端程序就進入了一個 `死鎖`狀態,兩個程序都不能結束.

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章