中文亚洲精品无码_熟女乱子伦免费_人人超碰人人爱国产_亚洲熟妇女综合网

當前位置: 首頁 > news >正文

深圳網站制作鄭州怎么優(yōu)化網站排名靠前

深圳網站制作,鄭州怎么優(yōu)化網站排名靠前,網站地圖生成代碼,框架做網站指的是目錄: 再談“協議” HTTP協議 認識URL: urlnecode和urldecode HTTP協議格式: HTTP的方法: 簡易HTTP服務器: 傳輸層 再談端口號: 端口號范圍劃分: netstat: pidof&…

目錄:

再談“協議”

HTTP協議?

認識URL:

urlnecode和urldecode?

HTTP協議格式:

HTTP的方法:

簡易HTTP服務器:?

?傳輸層

再談端口號:?

端口號范圍劃分:

netstat:

pidof:

UDP協議

UDP協議端格式?:

檢驗和的解釋:

UDP的特點:

面向數據報:

UDP的緩沖區(qū):

UDP使用注意事項:

基于UDP的應用層協議:

TCP協議?

TCP協議段格式:?

??編輯

超時重傳機制:?

連接管理機制:

理解TIME_WAIT狀態(tài):

滑動窗口:?

流量控制:

擁塞控制:

延遲應答:?

捎帶應答:

?面向字節(jié)流:

?粘包問題:

?TCP異常情況

TCP小結:

基于TCP的應用層協議:

TCP/UDP對比?

用UDP實現可靠傳輸(經典面試題)

TCP的相關實驗?

理解listen的第二個參數??

Linux網絡編程套接字(上)icon-default.png?t=N6B9https://blog.csdn.net/Obto_/article/details/132189802?


再談“協議”

協議是一種 "約定". socket api的接口, 在讀寫數據時, 都是按 "字符串" 的方式來發(fā)送接收的. 如果我們要傳輸一些"結構化的數據" 怎么辦呢?

方案一:

  • 客戶端發(fā)送一個"1+1"的字符串
  • 這個字符串中會有兩個操作數且都是整形
  • 兩個數據之間會有一個字符是運算符
  • 數字和運算符之間沒有空格
  • ....?

方案二:

  • 定義結構體來表示我們需要交互的信息
  • 發(fā)送數據時將這個結構體轉化成字符串,接收到的數據的時候再用相同的規(guī)則把字符串轉成結構體
  • 這個過程叫做“序列化”和“反序列化”

無論我們采用方案一還是方案二,亦或者其他的,其目的都是保證一端發(fā)送時夠早的數據,在另一端能夠正確的進行解析,這種約定就是應用層協議?

HTTP協議?

雖然說應用層協議可以由我們程序員自己來定,但實際上,已經有大佬定義了現成的,又非常好用,HTTP(超文本傳輸協議)就是其中之一?

認識URL:

平時我們俗稱的“網址”,其實就是URL

?

urlnecode和urldecode?

像 / ? : 等這樣的字符, 已經被url當做特殊意義理解了. 因此這些字符不能隨意出現.
比如, 某個參數中需要帶有這些特殊字符, 就必須先對特殊字符進行轉義.
轉義的規(guī)則如下:
將需要轉碼的字符轉為16進制,然后從右到左,取4位(不足4位直接處理),每2位做一位,前面加上%,編碼成%XY格式

?

?比如我搜索:c++那么這兩個++就會被轉意成"%2B%2B"

?

HTTP協議格式:

//使用該指令可以在linux下查看url的請求
curl -I www.baidu.com

?

?

  • 首行:[方法] + [url] + [版本]
  • Header:請求的屬性,冒號分隔的禁止對,每組屬性之間用\n分隔,遇到空行表示Header結束?
  • Body:空行后面的內容都是Body(上圖沒有把Body截圖進去,太長了),Body允許為空,但如果Body存在,則在Header會有一個Content-Length屬性來表示Body的長度?

?

ps:Header中有的屬性不止圖上這些,這些只是較為常見的...?

HTTP的方法:

?

方法? ? ? ? ? ? ??說明支持的HTTP協議版本

GET? ? ? ? ? ? ? ?

獲取資源1.0、1.1
POST傳輸實體主體1.0、1.1
PUT傳輸文件1.0、1.1
HEAD獲得報文首部1.0、1.1
DELETE刪除文件1.0、1.1

OPTIONS

詢問支持的方法1.1
TRACE追蹤路徑1.1
CONNECT要求用隧道協議鏈接代理1.1
LINK建立和資源之間的聯系1.0
UNLINK斷開鏈接關系1.0

HTTP的狀態(tài)碼?:

類別原因
1XXInformational(信息狀態(tài)碼)接受的請求正在處理
2XXSUCCESS(成功狀態(tài)碼)請求正常處理完畢
3XXRedirection(重定向狀態(tài)碼)需要進行附加操作以完成請求
4XXClient Error(客戶端錯誤狀態(tài)碼)服務器無法處理請求
5XXServer Error(服務器錯誤狀態(tài)碼)服務器處理請求出錯

常見的狀態(tài)碼:200(OK),404(Not Found),403(Forbidden),302(Redirect,重定向)

504(Bad Gateway)?

HTTP常見的Header:

  • ?Content-Type: 數據類型(text/html等)
  • Content-Length: Body的長度
  • Host: 客戶端告知服務器, 所請求的資源是在哪個主機的哪個端口上
  • User-Agent: 聲明用戶的操作系統(tǒng)和瀏覽器版本信息
  • referer: 當前頁面是從哪個頁面跳轉過來的
  • location: 搭配3xx狀態(tài)碼使用, 告訴客戶端接下來要去哪里訪問
  • Cookie: 用于在客戶端存儲少量信息. 通常用于實現會話(session)的功能

簡易HTTP服務器:?

HttpServer.hpp

#pragma once#include <iostream>
#include <signal.h>
#include <functional>
#include "Sock.hpp"class HttpServer
{
public:using func_t = std::function<void(int)>;private:int listensock_;uint16_t port_;Sock sock;func_t func_;public:HttpServer(const uint16_t &port, func_t func) : port_(port), func_(func){listensock_ = sock.Socket();sock.Bind(listensock_, port_);sock.Listen(listensock_);}void Start(){signal(SIGCHLD, SIG_IGN);for (;;){std::string clientIp;uint16_t clientPort = 0;int sockfd = sock.Accept(listensock_, &clientIp, &clientPort);if (sockfd < 0)continue;if (fork() == 0){close(listensock_);func_(sockfd);close(sockfd);exit(0);}close(sockfd);}}~HttpServer(){if (listensock_ >= 0)close(listensock_);}
};

?HttpServer.cc

#include <iostream>
#include <memory>
#include <cassert>
#include <fstream>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>#include "HttpServer.hpp"
#include "Usage.hpp"
#include "Util.hpp"// 一般http都要有自己的web根目錄
#define ROOT "./wwwroot" // ./wwwroot/index.html
// 如果客戶端只請求了一個/,我們返回默認首頁
#define HOMEPAGE "index.html"void HandlerHttpRequest(int sockfd)
{// 1. 讀取請求 for testchar buffer[10240];ssize_t s = recv(sockfd, buffer, sizeof(buffer) - 1, 0);if (s > 0){buffer[s] = 0;// std::cout << buffer << "--------------------\n" << std::endl;}std::vector<std::string> vline;Util::cutString(buffer, "\n", &vline);std::vector<std::string> vblock;Util::cutString(vline[0], " ", &vblock);std::string file = vblock[1];std::string target = ROOT;if (file == "/")file = "/index.html";target += file;std::cout << target << std::endl;std::string content;std::ifstream in(target);if (in.is_open()){std::string line;while (std::getline(in, line)){content += line;}in.close();}std::string HttpResponse;if (content.empty())HttpResponse = "HTTP/1.1 404 NotFound\r\n";elseHttpResponse = "HTTP/1.1 200 OK\r\n";HttpResponse += "\r\n";HttpResponse += content;send(sockfd, HttpResponse.c_str(), HttpResponse.size(), 0);
}void TestHandlerHttpRequest(int sockfd)
{std::string content = "<h1>ok1111</h1>";std::string HttpResponse;if (content.empty())HttpResponse = "HTTP/1.1 404 NotFound\r\n";elseHttpResponse = "HTTP/1.1 200 OK\r\n";HttpResponse += "Content-Length: 11\r\n";HttpResponse += "\r\n";HttpResponse += content;std::cout << "####start################" << std::endl;std::cout << HttpResponse << std::endl;//send(sockfd, content.c_str(), content.size(), 0);char buf[1024] = {0};// const char *hello = "<html><head><title>Hello, World!</title></head><body><h1>Hello, World!</h1><p>Welcome to my website.</p></body></html>";const char *hello = content.c_str();sprintf(buf, "HTTP/1.0 200 OK\nContent-Length:%lu\n\n%s", strlen(hello), hello);//write(sockfd, buf, strlen(buf));write(sockfd, HttpResponse.c_str(), strlen(HttpResponse.c_str()));// send(sockfd,hello,sizeof(hello),0);std::cout << "#####end###############" << std::endl;
}int main(int argc, char *argv[])
{if (argc != 2){Usage(argv[0]);exit(0);}std::unique_ptr<HttpServer> httpserver(new HttpServer(atoi(argv[1]), TestHandlerHttpRequest));httpserver->Start();return 0;
}

?傳輸層

負責數據能夠從發(fā)送端傳輸接收端

再談端口號:?

端口號(Port)標識了一個主機上進行通信的不同應用程序?

?在TCP/IP協議中, 用 "源IP", "源端口號", "目的IP", "目的端口號", "協議號" 這樣一個五元組來標識一個通信(可以通過netstat -n查看);

?

端口號范圍劃分:

  • 0-1023:知名端口號,HTTP,FTP,SSH等這些廣為使用的應用層協議,他們的端口號都是固定的

  • 1024-65535:OS動態(tài)分配的端口號,客戶端程序的端口號,就是由OS從這個范圍分配的

有一些服務器是非常常用的,人們約定一些常用的服務器用的都是以下這些固定端口號

  • ssh服務器:用22端口
  • ftp服務器:用21端口
  • telnet服務器:用23端口
  • http服務器:用80端口
  • https服務器用:用443端口?

執(zhí)行下面的命令, 可以看到知名端口號

cat /etc/services

netstat:

netstat使用來查看網絡狀態(tài)的工具:

語法:netstat [選項]

功能:查看網絡狀態(tài)

常用選項

  • n 拒絕顯示別名,能顯示數字的全部轉化成數字
  • l 僅列出有在 Listen (監(jiān)聽) 的服務狀態(tài)
  • p 顯示建立相關鏈接的程序名
  • t (tcp)僅顯示tcp相關選項
  • u (udp)僅顯示udp相關選項
  • a (all)顯示所有選項,默認不顯示LISTEN相關

pidof:

查看服務器的進程id?

語法:pidof [進程名]

功能:通過進程名查找進程id?


UDP協議

UDP協議端格式?:

?

  • 16UDP長度,表示整個數據報(UDP首部+UDP數據)的最大長度
  • 如果檢驗和出錯,就會直接丟棄?

檢驗和的解釋:

UDP的檢驗和可以幫助接收方驗證接收到的UDP數據是否完整、正確,并且未被損壞或篡改。發(fā)送方在發(fā)送UDP數據包時會計算數據包的檢驗和,并將該檢驗和值包含在UDP頭部中。接收方在接收數據包時,也會重新計算數據包的檢驗和,并將計算結果與接收到的檢驗和進行比較。如果兩個值不相等,就說明數據包在傳輸過程中發(fā)生了錯誤或篡改。?

UDP的特點:

UDP?傳輸的過程類似寄信:

  • 無連接:直到對端IP和PORT直接進行傳輸
  • 不可靠:沒有確認機制,沒有重傳機制,如果網絡故障導致該數據段無法發(fā)送到對端,UDP協議也不會給應用層返回任何的錯誤信息
  • 面向數據包:不能夠靈活的控制讀寫數據的次數和數量(一次就發(fā)一個完整的報文)

面向數據報:

應用層交給UDP多長的報文,UDP原樣發(fā)送,不會 拆分也不會合并

用UDP傳輸100個字節(jié)的數據:

  • ?如果發(fā)送端調用一次sendto, 發(fā)送100個字節(jié), 那么接收端也必須調用對應的一次recvfrom, 接收100個字節(jié); 而不能循環(huán)調用10次recvfrom, 每次接收10個字節(jié)

UDP的緩沖區(qū):

  • UDP沒有真正意義上的發(fā)送緩沖區(qū),調用sendto會直接交給內核,由該內核數據傳給網絡協議進行后續(xù)的傳輸動作

  • UDP具有接收緩沖區(qū),但是這個接收緩沖區(qū)不能保證收到的UDP報的順序和發(fā)送UDP報的順序一致;如果緩沖區(qū)滿了,再到達的UDP數據會被丟棄

UDP使用注意事項:

我們注意到, UDP協議首部中有一個16位的最大長度. 也就是說一個UDP能傳輸的數據最大長度是64K(包含UDP首部).然而64K在當今的互聯網環(huán)境下, 是一個非常小的數字.如果我們需要傳輸的數據超過64K, 就需要在應用層手動的分包, 多次發(fā)送, 并在接收端手動拼裝

基于UDP的應用層協議:

  • NFS:網絡文件系統(tǒng)

  • TFTP:簡單文件傳輸協議

  • DHCP:動態(tài)主機配置協議

  • BOOTP:啟動協議

  • DNS:域名解析協議?


TCP協議?

TCP全稱為 "傳輸控制協議(Transmission Control Protocol"). 人如其名, 要對數據的傳輸進行一個詳細的控制

TCP協議段格式:?

?

  • 源/目的端口號:表示數據是從哪個進程來,到哪個進程去
  • 32位序號/32位確認號會在下文詳細講述
  • 4位TCP報頭長度:表示該TCP頭部有多少個32位bit(有多少字節(jié));所以TCP頭部最大長度是15*4
  • 6位標志位:
    • URG:緊急指針是否有效
    • ACK:確認號是否有效
    • PSH:提示接收端應用立刻從TCP緩沖區(qū)把數據讀走
    • RST:對方要求重新建立連接;我們把攜帶RST表示的稱為復位報文段
    • SYN:請求建立連接;我們把攜帶SYN標識的稱為同步報文段
    • FIN:通知對方,本端要關閉了,我們稱攜帶FIN標識的為結束報文段
  • 16位窗口大小:后面詳細講
  • 16位校驗和:發(fā)送端填充,CRC校驗。接收端校驗不通過,則認為數據有問題。
  • 16位緊急指針:表示那部分數據是緊急數據

?CRC校驗:

CRC校驗的原理如下:

  1. 首先,定義一個生成多項式(通常是二進制數),表示為G(X)(如0x8005)。
  2. 發(fā)送方計算數據的校驗碼,使用生成多項式G(X)進行計算。具體計算過程是將數據按照二進制形式做除法運算,除數為生成多項式G(X)。
  3. 將計算得到的校驗碼添加到數據后面,形成帶有校驗碼的數據包,然后發(fā)送給接收方。
  4. 接收方使用相同的生成多項式G(X)進行計算,將接收到的數據進行除法運算,得到一個余數。
  5. 如果接收方計算得到的余數為0,則說明數據在傳輸過程中沒有發(fā)生錯誤;如果余數不為0,則說明數據發(fā)生了錯誤或者被篡改。

?

?

TCP將每個字節(jié)的數據都進行編號,即序列號

?

?每一個ACK都帶有對應的確認序列號,意思就是告訴發(fā)送者,我已經收到了這個序列號之前的所有數據,下一次你從這個序列號+1的后面開始發(fā)送


超時重傳機制:?

?

  • 主機A發(fā)送數據給B之后,可能因為網絡擁堵等問題,數據無法到達B
  • 如果主機A在一個特定時間間隔內沒有收到B發(fā)來的確認應答,就會重發(fā)?

當然還有下面這種情況:

?

因此主機B會收到很多重復數據. 那么TCP協議需要能夠識別出那些包是重復的包, 并且把重復的丟棄掉.這時候我們可以利用前面提到的序列號, 就可以很容易做到去重的效果
?

那么超時的時長如何定義:

  • 最理想的情況下, 找到一個最小的時間, 保證 "確認應答一定能在這個時間內返回
  • 但是這個時間的長短, 隨著網絡環(huán)境的不同, 是有差異的
  • 如果超時時間設的太長, 會影響整體的重傳效率
  • 如果超時時間設的太短, 有可能會頻繁發(fā)送重復的包

?TCP為了保證無論在任何環(huán)境下都能比較高性能的通信,因此會動態(tài)計算最大超時時間.

  • ?Linux中(BSD Unix和Windows也是如此), 超時以500ms為一個單位進行控制, 每次判定超時重發(fā)的超時時間都是500ms的整數倍
  • 如果重發(fā)一次之后, 仍然得不到應答, 等待 2*500ms 后再進行重傳
  • 如果仍然得不到應答, 等待 4*500ms 進行重傳. 依次類推, 以指數形式遞增
  • 累計到一定的重傳次數, TCP認為網絡或者對端主機出現異常, 強制關閉連接
    ?

連接管理機制:

?

服務端的狀態(tài)轉換:?

  • [CLOSED -> LISTEN]服務器端調用listen后進入LISTEN狀態(tài),等待客戶端連接
  • [LISTEN->SYN_RCVD]一旦監(jiān)聽到連接請求(同步報文段),就將該連接放入內核等待隊列中,并向客戶端發(fā)送SYN確認報文
  • [SYN_RCVD -> ESTABLISHED]服務端一旦收到客戶端的確認報文,就進入ESTABLISHED狀態(tài),可以進行讀寫數據了
  • [ESTABLISHED -> CLOSE_WAIT]當客戶端主動關閉(close()),服務器會收到結束報文段,服務器返回確認報文段并進入CLOSE_WAIT
  • [CLOSE_WAIT -> LAST_ACK]進入CLOSE_WAIT后說明服務器準備關閉連接(需要處理完當前的數據);當服務器真正調用close關閉連接時,會響客戶端發(fā)送FIN,此時服務器進入LAST_ACK狀態(tài),等待最后一個ACK到來(這個ACK是客戶端確認收到了FIN)
  • [LAST_ACK -> CLOSED]服務端收到了對FIN的ACK,徹底關閉連接

客戶端的狀態(tài)轉換:??

  • [CLOSED -> SYN_SENT] 客戶端調用connect, 發(fā)送同步報文段
  • [SYN_SENT -> ESTABLISHED] connect調用成功, 則進入ESTABLISHED狀態(tài), 開始讀寫數據
  • [ESTABLISHED -> FIN_WAIT_1] 客戶端主動調用close時, 向服務器發(fā)送結束報文段, 同時進入FIN_WAIT_1
  • [FIN_WAIT_1 -> FIN_WAIT_2] 客戶端收到服務器對結束報文段的確認, 則進入FIN_WAIT_2, 開始等待服務器的結束報文段
  • [FIN_WAIT_2 -> TIME_WAIT] 客戶端收到服務器發(fā)來的結束報文段, 進入TIME_WAIT, 并發(fā)出LAST_ACK
  • [TIME_WAIT -> CLOSED] 客戶端要等待一個2MSL(Max Segment Life, 報文最大生存時間)的時間, 才會進入CLOSED狀態(tài)
    ?

理解TIME_WAIT狀態(tài):

可以做一個測試,首先啟動server,然后啟動client,再將Ctrl-C是server終止后再次運行server

就會綁定失敗:

$ ./server

bind error : Address already in use?

這是因為雖然server的應用程序終止了,但是TCP協議層的連接并沒有完全斷開,因此不能再監(jiān)聽同樣的server端口?

  • ?TCP協議規(guī)定,主動關閉連接的一方要處于TIME_ WAIT狀態(tài),等待兩個MSL(maximum segment lifetime)的時間后才能回到CLOSED狀態(tài)
  • 我們使用Ctrl-C終止了server, 所以server是主動關閉連接的一方, 在TIME_WAIT期間仍然不能再次監(jiān)聽同樣的server端口
  • MSL在RFC1122中規(guī)定為兩分鐘,但是各操作系統(tǒng)的實現不同, 在Centos7上默認配置的值是60s
  • 可以通過 cat /proc/sys/net/ipv4/tcp_fin_timeout 查看msl的值

但是為什么是2MSL?

  • ?MSL是TCP報文的最大生存時間, 因此TIME_WAIT持續(xù)存在2MSL的話
  • 就能保證在兩個傳輸方向上的尚未被接收或遲到的報文段都已經消失
  • 同時也是在理論上保證最后一個報文可靠到達(假設最后一個ACK丟失, 那么服務器會再重發(fā)一個FIN. 這時雖然客戶端的進程不在了, 但是TCP連接還在, 仍然可以重發(fā)LAST_ACK);
    ?

在server的TCP連接沒有完全斷開之前不允許監(jiān)聽,某些情況不太合理,下面是解決方法?

  • ?使用setsockopt()設置socket描述符的 選項SO_REUSEADDR為1, 表示允許創(chuàng)建端口號相同但IP地址不同的多個socket描述符
int opt = 1;
setsockopt(listenfd , SOL_SOCKET , SO_REUSERADDR, &opt ,sizeof(opt));

滑動窗口:?

剛才我們討論了確認應答策略, 對每一個發(fā)送的數據段, 都要給一個ACK確認應答. 收到ACK后再發(fā)送下一個數據段.這樣做有一個比較大的缺點, 就是性能較差. 尤其是數據往返的時間較長的時候
?

但是這樣一收一發(fā)的效率很慢,(就像你去超市買菜,跑一趟就買一根,來回跑1000趟一樣)

但是如果我們一次性發(fā)送多條數據就可以大大提升效率(指的是你一次多帶點菜回來)?

?

  • 窗口大小指的是無需等待確認應答而可以繼續(xù)發(fā)送數據的最大值. 上圖的窗口大小就是4000個字節(jié)(四個段)
  • 發(fā)送前四個段的時候, 不需要等待任何ACK, 直接發(fā)送;
  • 收到第一個ACK后, 滑動窗口向后移動, 繼續(xù)發(fā)送第五個段的數據; 依次類推
  • 操作系統(tǒng)內核為了維護這個滑動窗口, 需要開辟 發(fā)送緩沖區(qū) 來記錄當前還有哪些數據沒有應答; 只有確認應答過的數據, 才能從緩沖區(qū)刪掉
  • 窗口越大, 則網絡的吞吐率就越高
    ?

?

那么再這種情況出現丟包,該如何重傳?

?情況一:數據包已經到達,ACK丟失了

?

這種情況問題不大,因為可以通過后續(xù)的ACK來確認?

情況二:數據包直接就丟失了?

?

  • 當某一段報文段丟失之后, 發(fā)送端會一直收到 1001 這樣的ACK, 就像是在提醒發(fā)送端 "我想要的是 1001"一樣
  • 如果發(fā)送端主機連續(xù)三次收到了同樣一個 "1001" 這樣的應答, 就會將對應的數據 1001 - 2000 重新發(fā)送
  • 這個時候接收端收到了 1001 之后, 再次返回的ACK就是7001了(因為2001 - 7000)接收端其實之前就已經收到了, 被放到了接收端操作系統(tǒng)內核的接收緩沖區(qū)中
  • 當出現三次重復的確認應答就會進行重發(fā)
    ?

這種機制也被稱作"高速重發(fā)控制"(也稱“快重傳”)

流量控制:

?接收端處理數據的速度是有限的. 如果發(fā)送端發(fā)的太快, 導致接收端的緩沖區(qū)被打滿, 這個時候如果發(fā)送端繼續(xù)發(fā)送,就會造成丟包, 繼而引起丟包重傳等等一系列連鎖反應.因此TCP支持根據接收端的處理能力, 來決定發(fā)送端的發(fā)送速度. 這個機制就叫做流量控制(Flow Control)

  • ?接收端將自己可以接收的緩沖區(qū)大小放入 TCP 首部中的 "窗口大小" 字段, 通過ACK端通知發(fā)送端
  • 窗口大小字段越大, 說明網絡的吞吐量越高
  • 接收端一旦發(fā)現自己的緩沖區(qū)快滿了, 就會將窗口大小設置成一個更小的值通知給發(fā)送端
  • 發(fā)送端接受到這個窗口之后, 就會減慢自己的發(fā)送速度
  • 如果接收端緩沖區(qū)滿了, 就會將窗口置為0; 這時發(fā)送方不再發(fā)送數據, 但是需要定期發(fā)送一個窗口探測數據段, 使接收端把窗口大小告訴發(fā)送端
    ?

?

接收端如何把窗口大小告訴發(fā)送端呢? 回憶我們的TCP首部中, 有一個16位窗口字段, 就是存放了窗口大小信息;那么問題來了, 16位數字最大表示65535, 那么TCP窗口最大就是65535字節(jié)么?

實際上, TCP首部40字節(jié)選項中還包含了一個窗口擴大因子M, 實際窗口大小是 窗口字段的值左移 M 位
?

擁塞控制:

?雖然TCP有了滑動窗口這個大殺器, 能夠高效可靠的發(fā)送大量的數據. 但是如果在剛開始階段就發(fā)送大量的數據, 仍然可能引發(fā)問題.因為網絡上有很多的計算機, 可能當前的網絡狀態(tài)就已經比較擁堵. 在不清楚當前網絡狀態(tài)下, 貿然發(fā)送大量的數據,是很有可能引起雪上加霜的

TCP引入慢啟動機制, 先發(fā)少量的數據, 探探路, 摸清當前的網絡擁堵狀態(tài), 再決定按照多大的速度傳輸數據

  • 此處引入一個概念程為擁塞窗口
  • 發(fā)送開始的時候, 定義擁塞窗口大小為1
  • 每次收到一個ACK應答, 擁塞窗口加1
  • 每次發(fā)送數據包的時候, 將擁塞窗口和接收端主機反饋的窗口大小做比較, 取較小的值作為實際發(fā)送的窗口

像這樣的擁塞窗口增長速度是指數級別的

  • 為了不增長的那么快, 因此不能使擁塞窗口單純的加倍
  • 此處引入一個叫做慢啟動的閾值
  • 當擁塞窗口超過這個閾值的時候, 不再按照指數方式增長, 而是按照線性方式增長
    • 當TCP開始啟動的時候, 慢啟動閾值等于窗口最大值
    • 在每次超時重發(fā)的時候, 慢啟動閾值會變成原來的一半, 同時擁塞窗口置回1

少量的丟包, 我們僅僅是觸發(fā)超時重傳; 大量的丟包, 我們就認為網絡擁塞;
當TCP通信開始后, 網絡吞吐量會逐漸上升; 隨著網絡發(fā)生擁堵, 吞吐量會立刻下降;
擁塞控制, 歸根結底是TCP協議想盡可能快的把數據傳輸給對方, 但是又要避免給網絡造成太大壓力的折中方案
?

延遲應答:?

?如果接收數據的主機立刻返回ACK應答, 這時候返回的窗口可能比較小

  • ?假設接收端緩沖區(qū)為1M. 一次收到了500K的數據; 如果立刻應答, 返回的窗口就是500K
  • 但實際上可能處理端處理的速度很快, 10ms之內就把500K數據從緩沖區(qū)消費掉了
  • 在這種情況下, 接收端處理還遠沒有達到自己的極限, 即使窗口再放大一些, 也能處理過來
  • 如果接收端稍微等一會再應答, 比如等待200ms再應答, 那么這個時候返回的窗口大小就是1M
    ?

窗口越大,網絡吞吐量就越大,傳輸效率就越高,我們的目標是保證網絡不擁塞的情況下,盡量提高傳輸效率

那么所有的包都可以延遲應答么? 肯定也不是

  • 數量限制: 每隔N個包就應答一次(一般N=2,超時時間取200ms)
  • 時間限制: 超過最大延遲時間就應答一次
    ?

捎帶應答:

在延遲應答的基礎上, 我們發(fā)現, 很多情況下, 客戶端服務器在應用層也是 "一發(fā)一收" 的. 意味著客戶端給服務器說了"How are you", 服務器也會給客戶端回一個 "Fine, thank you";那么這個時候ACK就可以搭順風車, 和服務器回應的 "Fine, thank you" 一起回給客戶端

?

?面向字節(jié)流:

?創(chuàng)建一個TCP的socket, 同時在內核中創(chuàng)建一個 發(fā)送緩沖區(qū) 和一個 接收緩沖區(qū);

  • ?調用write時, 數據會先寫入發(fā)送緩沖區(qū)中
  • 如果發(fā)送的字節(jié)數太長, 會被拆分成多個TCP的數據包發(fā)出
  • 如果發(fā)送的字節(jié)數太短, 就會先在緩沖區(qū)里等待, 等到緩沖區(qū)長度差不多了, 或者其他合適的時機發(fā)送出去
  • 接收數據的時候, 數據也是從網卡驅動程序到達內核的接收緩沖區(qū)
  • 然后應用程序可以調用read從接收緩沖區(qū)拿數據
  • 另一方面, TCP的一個連接, 既有發(fā)送緩沖區(qū), 也有接收緩沖區(qū), 那么對于這一個連接, 既可以讀數據, 也可以寫數據. 這個概念叫做 全雙工

?由于緩沖區(qū)的存在, TCP程序的讀和寫不需要一一匹配(不同于UDP), 例如:

  • ?寫100個字節(jié)數據時, 可以調用一次write寫100個字節(jié), 也可以調用100次write, 每次寫一個字節(jié)
  • 讀100個字節(jié)數據時, 也完全不需要考慮寫的時候是怎么寫的, 既可以一次read 100個字節(jié), 也可以一次read一個字節(jié), 重復100次;

?粘包問題:

  • 首先要明確, 粘包問題中的 "包" , 是指的應用層的數據包
  • 在TCP的協議頭中, 沒有如同UDP一樣的 "報文長度" 這樣的字段, 但是有一個序號這樣的字段
  • 站在傳輸層的角度, TCP是一個一個報文過來的. 按照序號排好序放在緩沖區(qū)中
  • 站在應用層的角度, 看到的只是一串連續(xù)的字節(jié)數據
  • 那么應用程序看到了這么一連串的字節(jié)數據, 就不知道從哪個部分開始到哪個部分, 是一個完整的應用層數據包

那么如何避免粘包問題呢? 歸根結底就是一句話, 明確兩個包之間的邊界

  • 對于定長的包, 保證每次都按固定大小讀取即可; 例如上面的Request結構, 是固定大小的, 那么就從緩沖區(qū)從頭開始按sizeof(Request)依次讀取即可
  • 對于變長的包, 可以在包頭的位置, 約定一個包總長度的字段, 從而就知道了包的結束位置
  • 對于變長的包, 還可以在包和包之間使用明確的分隔符(應用層協議, 是程序猿自己來定的, 只要保證分隔符不和正文沖突即可)
    ?

?對于UDP來說,不存在粘包問題

  • 對于UDP, 如果還沒有上層交付數據, UDP的報文長度仍然在. 同時, UDP是一個一個把數據交付給應用層. 就有很明確的數據邊界
  • 站在應用層的站在應用層的角度, 使用UDP的時候, 要么收到完整的UDP報文, 要么不收. 不會出現"半個"的情況
    ?

?TCP異常情況

進程終止: 進程終止會釋放文件描述符, 仍然可以發(fā)送FIN. 和正常關閉沒有什么區(qū)別

機器重啟: 和進程終止的情況相同

機器掉電/網線斷開: 接收端認為連接還在, 一旦接收端有寫入操作, 接收端發(fā)現連接已經不在了, 就會進行reset. 即使沒有寫入操作, TCP自己也內置了一個?;疃〞r器, 會定期詢問對方是否還在. 如果對方不在, 也會把連接釋放


另外, 應用層的某些協議, 也有一些這樣的檢測機制. 例如HTTP長連接中, 也會定期檢測對方的狀態(tài). 例如QQ, 在QQ
斷線之后, 也會定期嘗試重新連接
?

TCP小結:

?為什么TCP這么復雜? 因為要保證可靠性, 同時又盡可能的提高性能

可靠性:

  • 檢驗和
  • 序列號
  • 確認應答
  • 超時重發(fā)
  • 連接管理
  • 流量控制
  • 擁塞控制

提高性能:

  • 滑動窗口
  • 快速重傳
  • 延遲應答
  • 捎帶應答

其他:?

  • 定時器(超時重傳定時器,?;疃〞r器,TIME_WAIT定時器等)

基于TCP的應用層協議:

  • HTTP
  • HTTPS
  • SSH
  • Telnet
  • FTP
  • SMTP
  • 還有你自己寫TCP程序自定義的應用層協議?

TCP/UDP對比?

  • TCP用于可靠傳輸的情況, 應用于文件傳輸, 重要狀態(tài)更新等場景
  • UDP用于對高速傳輸和實時性要求較高的通信領域, 例如, 早期的QQ, 視頻傳輸等. 另外UDP可以用于廣播

用UDP實現可靠傳輸(經典面試題)

參考tcp的可靠性機制

  • 引入序列號,保證數據順序
  • 引入確認應答,確保對端收到了數據
  • 引出超時重傳,如果隔一段時間沒有應答,就重發(fā)數據

TCP的相關實驗?

理解listen的第二個參數?

這里將listen的第二個參數改成2,并且不調用accept

test.server.cc

#include "tcp_socket.hpp"
int main(int argc, char *argv[])
{if (argc != 3){printf("Usage ./test_server [ip] [port]\n");return 1;}TcpSocket sock;bool ret = sock.Bind(argv[1], atoi(argv[2]));if (!ret){return 1;}ret = sock.Listen(2);if (!ret){return 1;}// 客戶端不進行 acceptwhile (1){sleep(1);}return 0;
}

test.client.cc

#include "tcp_socket.hpp"
int main(int argc, char *argv[])
{if (argc != 3){printf("Usage ./test_client [ip] [port]\n");return 1;}TcpSocket sock;bool ret = sock.Connect(argv[1], atoi(argv[2]));if (ret){printf("connect ok\n");}else{printf("connect failed\n");}while (1){sleep(1);}return 0;
}

此時啟動 3 個客戶端同時連接服務器, 用 netstat 查看服務器狀態(tài), 一切正常.
但是啟動第四個客戶端時, 發(fā)現服務器對于第四個連接的狀態(tài)存在問題了

客戶端狀態(tài)正常, 但是服務器端出現了 SYN_RECV 狀態(tài), 而不是 ESTABLISHED 狀態(tài)

這是因為, Linux內核協議棧為一個tcp連接管理使用兩個隊列:

  1. 半鏈接隊列(用來保存處于SYN_SENT和SYN_RECV狀態(tài)的請求)
  2. 全連接隊列(accpetd隊列)(用來保存處于established狀態(tài),但是應用層沒有調用accept取走的請求)

而全連接隊列的長度會受到 listen 第二個參數的影響,全連接隊列滿了的時候, 就無法繼續(xù)讓當前連接的狀態(tài)進入 established 狀態(tài),這個隊列的長度通過上述實驗可知, 是 listen 的第二個參數 + 1




?

?

?

?

?

?

?

?

?

?

?

?

?

?

?

?

?

?

?

?

?

?

?

?

?

?

?

?

?

?

??

?

?

?

?


?

?

?

?

?

?

?

?

http://www.risenshineclean.com/news/31468.html

相關文章:

  • 南通seo公司網站免費推廣產品平臺有哪些
  • 上海奉賢 網站建設百度指數查詢移動版
  • 廉江網站建設公眾號推廣合作平臺
  • html網頁設計基礎seo優(yōu)化主要做什么
  • 網站開發(fā)者模式企業(yè)官網建站
  • 學網站建設需要什么軟件百度外包公司有哪些
  • 企業(yè)網站建設參考資料競價推廣賬戶競價托管
  • 珠海服務好的網站建設武漢seo
  • 江陰做網站百度秒收錄軟件工具
  • 微商網站開發(fā)寧波網站推廣營銷
  • 恒華大廈做網站公司山東做網站
  • 專門教做甜品的網站愛站網關鍵詞
  • wordpress用qq注冊谷歌seo什么意思
  • 如何做目錄wordpress北京網站優(yōu)化外包
  • 上海 高端 網站建設我的百度賬號登錄
  • 創(chuàng)新的鹽城網站建設百度收錄查詢代碼
  • 一步一步教你做網站哪家公司建設網站好
  • 做網站續(xù)費seo概念
  • 租電信服務器開網站磁力鏈bt磁力天堂
  • wordpress主題 dux1.2原版整站優(yōu)化排名
  • dk域名網站全搜網
  • 白菜網站建設如何優(yōu)化網站首頁
  • 公司網站建app互聯網營銷師怎么報名
  • 建設部一建注冊公示網站培訓心得體會范文大全1000字
  • 怎么建設好一個外貿購物網站哪有惡意點擊軟件買的
  • 寧夏住房和城鄉(xiāng)建設廳網站執(zhí)業(yè)資格工具大全
  • 邢臺網站優(yōu)化建設站長查詢域名
  • 二級域名網站怎么投廣告百度seo優(yōu)化規(guī)則
  • 手機端網站建設公司經典網絡營銷案例
  • 網站如何防止被復制百度打車客服電話