用Java代碼簡單的通信過程

       TCP提供了一種面向連接的、可靠的字節流服務。面向連接比較好理解,就是連接雙方在通信前需要預先建立一條連接,這猶如實際生活中的打電話。助於可靠性,TCP協議中涉及了諸多規則來保障通信鏈路的可靠性,總結起來,主要有以下幾點:

      (1)應用數據分割成TCP認爲最適合發送的數據塊。這部分是通過“MSS”(最大數據包長度)選項來控制的,通常這種機制也被稱爲一種協商機制,MSS規定了TCP傳往另一端的最大數據塊的長度。值得注意的是,MSS只能出現在SYN報文段中,若一方不接收來自另一方的MSS值,則MSS就定爲536字節。一般來講,在不出現分段的情況下,MSS值還是越大越好,這樣可以提高網絡的利用率。

      (2)重傳機制。設置定時器,等待確認包。

      (3)對首部和數據進行校驗。

      (4)TCP對收到的數據進行排序,然後交給應用層。

      (5)TCP的接收端丟棄重複的數據。

      (6)TCP還提供流量控制。

TCP報文:

(1)TCP封裝數據的格式:

(2)TCP首部的格式:

 TCP首部報文格式的說明:

     (1)每個TCP段都包括源端和目的端的端口號,用於尋找發送端和接收端的應用進程。這兩個值加上IP首部的源端IP地址和目的端IP地址唯一確定一個TCP連接。
     (2)序號用來標識從TCP發送端向接收端發送的數據字節流,它表示在這個報文段中的第一個數據字節。如果將字節流看作在兩個應用程序間的單向流動,則TCP用序號對每個字節進行計數。
     (3)當建立一個新連接時,SYN標誌變1。序號字段包含由這個主機選擇的該連接的初始序號ISN,該主機要發送數據的第一個字節的序號爲這個ISN1,因爲SYN標誌使用了一個序號。
     (4)既然每個被傳輸的字節都被計數,確認序號包含發送確認的一端所期望收到的下一個序號。因此,確認序號應當時上次已成功收到數據字節序號加1。只有ACK標誌爲1時確認序號字段纔有效。
     (5)發送ACK無需任何代價,因爲32位的確認序號字段和ACK標誌一樣,總是TCP首部的一部分。因此一旦一個連接建立起來,這個字段總是被設置,ACK標誌也總是被設置爲1
     (6TCP爲應用層提供全雙工的服務。因此,連接的每一端必須保持每個方向上的傳輸數據序號。
     (7TCP可以表述爲一個沒有選擇確認或否認的華東窗口協議。因此TCP首部中的確認序號表示發送方已成功收到字節,但還不包含確認序號所指的字節。當前還無法對數據流中選定的部分進行確認。
     (8)首部長度需要設置,因爲任選字段的長度是可變的。TCP首部最多60個字節。
     (96個標誌位中的多個可同時設置爲1
         URG
-緊急指針有效
         ACK-確認序號有效
         PSH-接收方應儘快將這個報文段交給應用層
         RST-重建連接
         SYN-同步序號用來發起一個連接
         FIN-發送端完成發送任務
     (10TCP的流量控制由連接的每一端通過聲明的窗口大小來提供。窗口大小爲字節數,起始於確認序號字段指明的值,這個值是接收端期望接收的字節數。窗口大小是一個16爲的字段,因而窗口大小最大爲65535字節。
     (11)檢驗和覆蓋整個TCP報文端:TCP首部和TCP數據。這是一個強制性的字段,一定是由發送端計算和存儲,並由接收端進行驗證。TCP檢驗和的計算和UDP首部檢驗和的計算一樣,也使用僞首部。
     (12)緊急指針是一個正的偏移量,黃蓉序號字段中的值相加表示緊急數據最後一個字節的序號。TCP的緊急方式是發送端向另一端發送緊急數據的一種方式。
     (13)最常見的可選字段是最長報文大小MMS,每個連接方通常都在通信的第一個報文段中指明這個選項。它指明本端所能接收的最大長度的報文段。

TCP三次握手與四次揮手全過程:

 

1、建立連接協議(三次握手)
    (1)客戶端發送一個帶SYN標誌的TCP報文到服務器。這是三次握手過程中的報文1。
    (2) 服務器端迴應客戶端的,這是三次握手中的第2個報文,這個報文同時帶ACK標誌和SYN標誌。因此它表示對剛纔客戶端SYN報文的迴應;同時又標誌SYN給客戶端,詢問客戶端是否準備好進行數據通訊。
    (3) 客戶必須再次迴應服務段一個ACK報文,這是報文段3。


TCP的三次握手可以通過報文來分析:

   (1)客戶端向服務器端發起同步請求,服務器側端口固定爲 102,客戶端端口由 socket 隨機產生


     (2)服務器端向客戶端響應,同時也向客戶端發起同步請求

     (3)客戶端予以確認

2 連接終止協議(四次揮手)

   由於TCP連接是全雙工的,因此每個方向都必須單獨進行關閉。這原則是當一方完成它的數據發送任務後就能發送一個FIN來終止這個方向的連接。收到一個 FIN只意味着這一方向上沒有數據流動,一個TCP連接在收到一個FIN後仍能發送數據。首先進行關閉的一方將執行主動關閉,而另一方執行被動關閉。
 (1) TCP客戶端發送一個FIN,用來關閉客戶到服務器的數據傳送(報文段4)。
 (2) 服務器收到這個FIN,它發回一個ACK,確認序號爲收到的序號加1(報文段5)。和SYN一樣,一個FIN將佔用一個序號。
 (3) 服務器關閉客戶端的連接,發送一個FIN給客戶端(報文段6)。
 (4) 客戶段發回ACK報文確認,並將確認序號設置爲收到序號加1(報文段7)。

通過Java代碼實現TCP連接三次握手和四次揮手的過程:


package com.zhaoming.test;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.net.UnknownHostException;
import java.text.SimpleDateFormat;
import java.util.Date;


public class ClientConnect extends Thread
{
    //定義一個Client的對象
    private Client client;
    
    private Socket socket;
    //定義IP和端口號是常量
    private static final String IP = "192.168.1.190";
    
    private static final int PORT = 20108;
  //聲明str是一個靜態變量,
    public static String str = null;
    //進行構造(持有另一個類對象的引用)    
    public ClientConnect(Client client)
    {
        this.client = client;
    }
    //啓用線程,處理連接
    public void run()
    {
        try
        {
            //初始化要連接的socket套接字
            socket = new Socket(IP,PORT);
            client.getjButton1().setEnabled(false);
        }
        catch (UnknownHostException e1)
        {
            e1.printStackTrace();
        }
        catch (IOException e1)
        {
            e1.printStackTrace();
        }
        //從服務器端通過socket讀取信息
        BufferedReader br = null;
        try
        {
            br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            //把讀到的信息顯示到TextArea中
            while (true)
            {
                str = br.readLine();
                client.getjTextArea1().append(str + "\n");
                //建一個新的文本文檔,用於存儲從服務器讀到的信息
                File file = new File("E:/temperatrue.txt");
                
                PrintWriter out = null;
                String date = null;
                //修改時間顯示的格式
                SimpleDateFormat sdf = null;
                
                try
                {
                    //把讀到的信息,寫到到文本文件中存儲起來
                    out = new PrintWriter(new FileOutputStream(file, true));
                    sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                    date = sdf.format(new Date());
                    out.println(date+"\t"+str);
                    out.flush();
                }
                catch (FileNotFoundException e)
                {
                    e.printStackTrace();
                }
                finally
                {
                    out.close();    
                }
                }
        } catch (IOException e)
        {
            e.printStackTrace();
        }
        //進行流關閉處理,先進行判斷,然後再關閉
        finally
        {
            try
            {
                if(br != null)
                {
                    br.close();
                    br =null;
                }
                if(socket != null)
                {
                    socket.close();
                    socket = null;
                }
            }
            catch (IOException e)
            {
                e.printStackTrace();
            }
        }
    }
}
      由於TCP連接是全雙工的,因此每個方向都必須單獨進行關閉。這原則是當一方完成它的數據發送任務後就能發送一個FIN來終止這個方向的連接。收到一個 FIN只意味着這一方向上沒有數據流動,一個TCP連接在收到一個FIN後仍能發送數據。首先進行關閉的一方將執行主動關閉,而另一方執行被動關閉。
      TCP協議的連接是全雙工連接,一個TCP連接存在雙向的讀寫通道。 
簡單說來是 “先關讀,後關寫”,一共需要四個階段。以客戶機發起關閉連接爲例:
     1.服務器讀通道關閉
     2.客戶機寫通道關閉
     3.客戶機讀通道關閉
     4.服務器寫通道關閉
      關閉行爲是在發起方數據發送完畢之後,給對方發出一個FIN(finish)數據段。直到接收到對方發送的FIN,且對方收到了接收確認ACK之後,雙方的數據通信完全結束,過程中每次接收都需要返回確認數據段ACK。

 

 

 

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