1. 程式人生 > >TCP、UDP通信

TCP、UDP通信

得到 conn end tcp、udp tcp 字節 其中 個數 建立連接

開放系統互連參考模型 (Open System Interconnect 簡稱OSI)

OSI七層模型

1.應用層
2.表示層
3.會話層
4.傳輸層
5.網絡層
6.數據鏈路層
7.物理層

TCP/IP模型
1.應用層 上面3層:應用程序、協議:HTTP、FTP
2.傳輸層 TCP UDP
3.網絡層 IP(不可靠) ARP RARP ICMP
4.數據鏈路層 下面2層

UDP User Datagram Protocol
無連接的傳送層協議,提供不可靠的信息傳輸服務


1.提供無連接服務,客戶端向服務器發送數據時不必先建立連接,客戶端創建一個套接字並向服務器發送一個數據,然後客戶端可立即用這個套接字向另外一個服務器發送其他數據。

2.不能確保UDP數據報最終到達目的地,UDP對接收的數據不發送確認,發送端不知道數據是否被正確接收,也不會重發數據。
3.UDP傳輸數據較TCP快,占用資源少。
UDP信息包的標題很短,只有8個字節,相對於TCP的20個字節信息包的額外開銷很小。
UDP由於排除了信息可靠傳遞機制,將安全和排序等功能移交給上層應用來完成,極大降低了執行時間,使速度得到了保證。

TCP Transmission Control Protocol

面向連接的、可靠的、基於字節流的傳送層通信協議。

1.提供面向連接的服務。客戶端與服務端通信時,必須首先建立連接。
2.提供可靠的服務。當TCP向對方發送數據時,要求對方返回一個確認,如果沒有接收到對方的確認,則TCP自動重傳數據。

3.TCP對發送的數據進行排序,為每個發送字節關聯一個序列號。對方根據接收到的數據序列號,對接收數據排序,從而保證數據順序
3.TCP提供流量控制,TCP總是告知對方它能夠接收數據的字節數。
4.TCP連接是全雙工的,這意味著應用程序在任何時候,既可以發送數據也可以接收數據。


連接:
三次握手
1.服務器準備接收客戶端的連接。
2.客戶端向服務器發起請求,此時客戶端TCP發送一個SYN分節。
3.服務器確認客服端的SYN(同步),同時也發送一個SYN分節,服務器以單個分節向客戶端發送SYN和對客戶端SYN的ACK(確認)。
4.客戶端確認服務器的SYN。

第一次握手:Client什麽都不能確認;Server確認了對方發送正常

第二次握手:Client確認了:自己發送、接收正常,對方發送、接收正常;Server確認了:自己接收正常,對方發送正常
第三次握手:Client確認了:自己發送、接收正常,對方發送、接收正常;Server確認了:自己發送、接收正常,對方發送接收正常

斷開連接:
四次揮手

問題1: 為什麽要四次揮手?

答:根本原因是,一方發送FIN只表示自己發完了所有要發的數據,但還允許對方繼續把沒發完的數據發過來。

舉個例子:A和B打電話,通話即將結束後,A說“我沒啥要說的了”,B回答“我知道了”,但是B可能還會有要說的話,
A不能要求B跟著自己的節奏結束通話,於是B可能又巴拉巴拉說了一通,最後B說“我說完了”,A回答“知道了”,這樣通話才算結束。


問題2:為什麽雙方要發送這樣的數據包?

答:和握手的情況類似,只是為了讓對方知曉自己理解了對方的意圖。

由於主機IP地址與網絡服務是一對多的關系,所以主機使用不同的端口號區分不同的網絡服務
0-65535
通用端口號:0-1023 緊密綁定某些特殊服務,80是HTTP通信端口,21是FTP通信端口
已註冊端口號:1024-49151,提供一般應用程序使用
動態或私有端口:49162-65535

報文:
報文(message)是網絡中交換與傳輸的數據單元,即站點一次性要發送的數據塊。
報文包含了將要發送的完整的數據信息,其長短很不一致,長度不限且可變。

報文也是網絡傳輸的單位,傳輸過程中會不斷的封裝成分組、包、幀來傳輸,
封裝的方式就是添加一些信息段,那些就是報文頭以一定格式組織起來的數據。

IP數據包:20字節IP包頭+9字節UDP包頭+UDP數據

UDP報頭由4個域組成,其中每個域各占用2個字節,共8個字節

16位源端口號 16位目的端口號
16位UDP長度 16位UDP校驗和
數據(如果有)
......

數據包的長度是指包括報頭和數據部分在內的總字節數,報頭的長度是固定的
包含報頭在內的數據報的最大長度為65535字節。不過,一些實際應用往往會限制數據報的大小,有時會降低到8192字節。

UDP協議使用報頭中的校驗值來保證數據的安全。
校驗值首先在數據發送方通過特殊的算法計算得出,在傳遞到接收方之後,還需要再重新計算。
檢測到錯誤時,UDP不做錯誤校正,只是簡單地把損壞的消息段扔掉,或者給應用程序提供警告信息。


UDP不排序,到達順序也可能跟發送時的順序不同。

UDP:
    WSADATA wsaData;
    int ret = WSAStartup(MAKEWORD(2, 2), &wsaData);
    if (ret != 0)
    {
        WSACleanup();
        return 0;
    }
    MFC為
    if (!AfxSocketInit())
    {
        AfxMessageBox(IDP_SOCKETS_INIT_FAILED);
        return FALSE;
    }

服務端:
    1.創建數據報套接字
      SOCKET severSocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
    2.綁定端口
      SOCKADDR_IN recvAddr;
      recvAddr.sin_family = AF_INET;
      recvAddr.sin_port = htons(9001);  //本地(服務器端口),客戶端發送到這個端口
      recvAddr.sin_addr.S_un.S_addr = htonl(INADDR_ANY);  //接收任何IP的消息
      if (bind(severSocket, (SOCKADDR*)&recvAddr, sizeof(recvAddr)) == SOCKET_ERROR)
      {
          closesocket(severSocket);
          WSACleanup();
          return 0;
      }
      
      3.收發數據
      
      SOCKADDR_IN sendAddr;
      int nSendAddrSize = sizeof(sendAddr);
      char recvBuf[1024] = { 0 };
      //會接收到對方的IP和端口,發送的時候使用這個IP和端口
      recvfrom(severSocket, recvBuf, sizeof(recvBuf), 0, (SOCKADDR*)&sendAddr, &nSendAddrSize)  
      sendto(severSocket, recvBuf, sizeof(recvBuf), 0, (SOCKADDR*)&sendAddr, nSendAddrSize);
      
      4.關閉套接字
      closesocket(severSocket);
      WSACleanup();
      
客戶端://未綁定端口,系統自動分配端口,這樣對方在沒有收到你的消息之前不知道你的端口號,不能向你發數據
        //可以綁定一個端口
    1.創建數據報套接字
      SOCKET clientSocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
    
    2.收發數據
      SOCKADDR_IN sendAddr;
      sendAddr.sin_family = AF_INET;
      sendAddr.sin_port = htons(9001);  //對方端口
      sendAddr.sin_addr.S_un.S_addr = inet_addr("192.168.0.126"); // //對方IP 
      
      char sendBuf[1024] = { 0 };
      sendto(clientSocket, sendBuf, sizeof(sendBuf), 0, (SOCKADDR*)&sendAddr, sizeof(sendAddr));
      
      char recvBuf[1024] = { 0 };
      SOCKADDR_IN recvAddr;
       int nRecvAddrSize = sizeof(SOCKADDR_IN);
      recvfrom(clientSocket, recvBuf, 1024, 0, (SOCKADDR*)&recvAddr, &nRecvAddrSize);

    3.關閉套接字
      closesocket(clientSocket);
      WSACleanup();
      
      
    MFC: CAsyncSocket或者CSocket
    //nLocalPort為0,則系統自動分配端口,這樣對方在沒有收到你的消息之前不知道你的端口號,不能向你發數據
    Create(nLocalPort, SOCK_DGRAM);    //創建及綁定端口(封裝了bind),不要再調用Bind了
    
    SendTo(str, str.GetLength(), unPort, strIP);//對方端口、IP

    OnReceive:
    char recvBuf[1024] = { 0 };
    CString strIP = _T("");
    UINT nPort = 0;
    ReceiveFrom(recvBuf, 1024, strIP, nPort);//收了發送方的數據、端口及IP
    
    Close();

TCP:

服務端:

創建套接字--綁定--監聽--accept接受客戶端連接--收發數據--關閉套接字

客戶端:

創建套接字--connect--收發數據--關閉套接字

TCP、UDP通信