新聞中心
TCP(Tranission Control Protocol)是一種網(wǎng)絡傳輸層協(xié)議,主要用于保證數(shù)據(jù)在網(wǎng)絡中可靠傳輸。在 Linux 操作系統(tǒng)中,TCP 協(xié)議是非常重要且廣泛使用的協(xié)議,因此深入了解 Linux TCP 將有助于我們更好地理解 Linux 網(wǎng)絡編程和網(wǎng)絡優(yōu)化。

我們提供的服務有:成都網(wǎng)站建設、網(wǎng)站制作、微信公眾號開發(fā)、網(wǎng)站優(yōu)化、網(wǎng)站認證、漢陽ssl等。為近1000家企事業(yè)單位解決了網(wǎng)站和推廣的問題。提供周到的售前咨詢和貼心的售后服務,是有科學管理、有技術的漢陽網(wǎng)站制作公司
本文將介紹一些常用的 Linux TCP 相關命令和工具,并通過一個 TCP 客戶端和服務器的例子,深入了解 TCP 的基本流程和一些常見的 TCP 問題。
1. 常用 Linux TCP 命令和工具
1.1 netstat
netstat 命令是一個非常常用的網(wǎng)絡命令,可以用于顯示各種網(wǎng)絡信息。在查看 TCP 相關信息時,我們可以使用以下命令來查看當前連接的狀態(tài)信息:
“`bash
netstat -ant | grep ESTABLISHED
“`
該命令將顯示當前所有已建立的連接信息,包括本地 IP 和端口號、遠程 IP 和端口號、TCP 的狀態(tài)等信息。
1.2 tcpdump
tcpdump 是一個用于捕獲網(wǎng)絡數(shù)據(jù)包的命令行工具。通過 tcpdump,我們可以詳細查看網(wǎng)絡通信中的各種細節(jié)信息,包括TCP包的頭部信息、數(shù)據(jù)長度、序列號、確認號等等。例如,我們可以使用以下命令來捕獲指定端口的數(shù)據(jù)包:
“`bash
sudo tcpdump -n -i eth0 port 8080
“`
該命令將捕獲所有進入或離開 eth0 接口上的 8080 端口的 TCP 數(shù)據(jù)包,并輸出詳細信息。
1.3 tcptrace
tcptrace 是一個用于分析和生成 TCP 連接數(shù)據(jù)包的工具。通過 tcptrace,我們可以詳細了解 TCP 連接的生命周期,了解各個階段的數(shù)據(jù)傳輸情況、時延、丟包等信息。例如,我們可以使用以下命令來生成 TCP 連接的日志文件:
“`bash
sudo tcpdump -n -s0 -w tcpdump.log port 8080
sudo tcptrace -xS tcpdump.log
“`
該命令將捕獲本地 8080 端口的所有 TCP 數(shù)據(jù)包,并將其轉儲到 tcpdump.log 文件中。然后我們可以使用 tcptrace 命令來分析生成數(shù)據(jù)文件。
2. TCP 客戶端和服務器的例子
在 Linux 中,我們可以使用各種編程語言來編寫 TCP 客戶端和服務器程序。下面,我們將使用 C 語言來編寫一個簡單的 TCP 客戶端和服務器程序,并通過這個例子來深入了解 TCP 的基本流程和一些常見的 TCP 問題。
2.1 TCP 服務器
下面是一個簡單的 TCP 服務器程序的示例代碼:
“`c
#include
#include
#include
#include
#include
#include
#include
#define PORT 8080
#define BACKLOG 10
int mn()
{
int sockfd, new_fd;
struct sockaddr_in my_addr;
struct sockaddr_in their_addr;
size_t sin_size;
char buf[1024];
if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
{
perror(“socket”);
exit(1);
}
my_addr.sin_family = AF_INET;
my_addr.sin_port = htons(PORT);
my_addr.sin_addr.s_addr = INADDR_ANY;
bzero(&(my_addr.sin_zero), 8);
if (bind(sockfd, (struct sockaddr *)&my_addr, sizeof(struct sockaddr)) == -1)
{
perror(“bind”);
exit(1);
}
if (listen(sockfd, BACKLOG) == -1)
{
perror(“l(fā)isten”);
exit(1);
}
printf(“server started listening on %d…\n”, PORT);
while (1)
{
if ((new_fd = accept(sockfd, (struct sockaddr *)&their_addr, (socklen_t *)&sin_size)) == -1)
{
perror(“accept”);
continue;
}
printf(“server got connection from %s\n”, inet_ntoa(their_addr.sin_addr));
send(new_fd, “Hello, world!”, 13, 0);
close(new_fd);
}
close(sockfd);
return 0;
}
“`
上述代碼中,我們首先通過 socket 函數(shù)創(chuàng)建了一個套接字,然后通過 bind 函數(shù)將該套接字與本地的 IP 地址和端口號綁定。接著,我們調(diào)用 listen 函數(shù)開啟服務,準備接受來自 TCP 客戶端的連接請求。在主循環(huán)中,我們通過 accept 函數(shù)等待客戶端的連接請求,一旦接收到連接請求,就使用 send 函數(shù)向客戶端發(fā)送歡迎信息,并使用 close 函數(shù)關閉連接。
2.2 TCP 客戶端
下面是一個簡單的 TCP 客戶端程序的示例代碼:
“`c
#include
#include
#include
#include
#include
#include
#include
#define PORT 8080
#define SERVER_ADDR “127.0.0.1”
int mn()
{
int sockfd;
struct sockaddr_in server_addr;
char buf[1024];
if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
{
perror(“socket”);
exit(1);
}
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(PORT);
server_addr.sin_addr.s_addr = inet_addr(SERVER_ADDR);
bzero(&(server_addr.sin_zero), 8);
if (connect(sockfd, (struct sockaddr *)&server_addr, sizeof(struct sockaddr)) == -1)
{
perror(“connect”);
exit(1);
}
recv(sockfd, buf, 1024, 0);
printf(“client received: %s\n”, buf);
close(sockfd);
return 0;
}
“`
上述代碼中,我們同樣首先通過 socket 函數(shù)創(chuàng)建了一個套接字,然后通過 connect 函數(shù)連接到遠程服務器的 IP 地址和端口號。在連接成功后,我們通過 recv 函數(shù)等待服務器的響應,并將其輸出到控制臺。我們通過 close 函數(shù)關閉連接。
3.
相關問題拓展閱讀:
- TCP那些事兒
- Linux TCP/IP協(xié)議棧數(shù)據(jù)包處理流程及代碼實現(xiàn)分析
TCP那些事兒
目錄:
以前畢盯洞我也認為TCP是相當?shù)讓拥臇|西,我永遠不需要去了解它。雖然差不多是這樣,但是實際生活中,你依然可能遇見和TCP算法相關的bug,這時候懂一些TCP的知識就至關重要了。(
本文也可以引申為,系統(tǒng)調(diào)用,操作系統(tǒng)這些都很重要,這個道理適用于很多東西
)
這里推薦一篇小短文, 人人都應該懂點TCP
使用TCP協(xié)議通信的雙方必須先建立TCP連接,并在內(nèi)核中為該連接維持一些必要的數(shù)據(jù)結構,比如連接的狀態(tài)、讀寫緩沖區(qū)、定時器等。當通信結束時,雙方必須關閉連接以釋放這些內(nèi)核數(shù)據(jù)。TCP服務基于流,源源不斷從一端流向另一端,發(fā)送端可以逐字節(jié)寫入,接收端可以逐字節(jié)讀出,無需分段。
需要注意的幾點:
TCP狀態(tài)(11種):
eg.
以上為TCP三次握手的狀態(tài)變遷
以下為TCP四次揮手的狀態(tài)變遷
服務器通過 listen 系統(tǒng)調(diào)用進入
LISTEN
狀態(tài),被動等待客戶端連接,也就是所謂的被動打開。一旦監(jiān)聽到SYN(同步報文段)請求,就將該連接放入內(nèi)核的等待隊列,并向客戶端發(fā)送帶SYN的ACK(確認報文段),此時該連接處于
SYN_RECVD
狀態(tài)。如果服務器收到客戶端返回的ACK,則轉到
ESTABLISHED
狀態(tài)。這個狀態(tài)就是連接雙方能進行全雙工數(shù)據(jù)傳輸?shù)臓顟B(tài)。
而當客戶端主動關閉連接時,服務器收到FIN報文,通過返回ACK使連接進入
CLOSE_WAIT
狀態(tài)。此狀態(tài)表示——等待服務器應用程序關閉連接。通常,服務器檢測到客戶端關閉連接之后,也會立即給客戶端發(fā)送一個FIN來關閉連接,使連接轉移到
LAST_ACK
狀態(tài),等待客戶端對最后一個FIN結束報文段的最后一次確認,一旦確認完成,連接就徹底關閉了。
客戶端通過 connect 系統(tǒng)調(diào)用主動與服務器建立連接。此系統(tǒng)調(diào)用會首先給服務器發(fā)一個SYN,使連接進入
SYN_SENT
狀態(tài)。
connect 調(diào)用可能因為兩種原因失敗:1. 目標端口不存在(未被任何進程監(jiān)聽)護著該端口被
TIME_WAIT
狀態(tài)的連接占用( 詳見后文 )。2. 連接超時,在超時時間內(nèi)未收到服務器的ACK。
如果 connect 調(diào)用失敗,則連接返回初始的
CLOSED
狀態(tài),如果調(diào)用成功,則轉到
ESTABLISHED
狀態(tài)。
客戶端執(zhí)行主動關閉時,它會向服務器發(fā)送一個FIN,連接進入
TIME_WAIT_1
狀態(tài),如果收到服務器的ACK,進入
TIME_WAIT_2
狀態(tài)。此時服務器處于
CLOSE_WAIT
狀態(tài),這一對狀態(tài)是可能發(fā)生辦關閉的狀態(tài)(詳見后文)。此時如果服務器發(fā)送FIN關閉連接,則客戶端會發(fā)送ACK進行確認并進入
TIME_WAIT
狀態(tài)。
流量控制是為了控制發(fā)送方發(fā)送速率,保證接收方來得及接收。
接收方發(fā)送的確認報文中的窗口字段可以用來控制發(fā)送方窗口大小,從而影響發(fā)送方的發(fā)送速率。將窗口字段設置為 0,則發(fā)送方不能發(fā)送數(shù)據(jù)。
如果網(wǎng)絡出現(xiàn)擁塞,分組將會丟失,此時發(fā)送方會繼續(xù)重傳手枯,從而導致網(wǎng)絡擁塞程度更高。因此當出現(xiàn)擁塞時,應當控制發(fā)送方的速率。這一點和流量控制很像,但是出發(fā)點不同。
流量控制是為了讓接收方能來得及接收,而擁塞控制是為了降低整個網(wǎng)絡的擁塞程度。
TCP 主要通過四種算法來進行擁塞控制:
慢開始、擁塞避免、快重傳、快恢復。
在Linux下有多種則埋實現(xiàn),比如reno算法,vegas算法和cubic算法等。
發(fā)送方需要維護一個叫做擁塞窗口(cwnd)的狀態(tài)變量,注意擁塞窗口與發(fā)送方窗口的區(qū)別:擁塞窗口只是一個狀態(tài)變量,實際決定發(fā)送方能發(fā)送多少數(shù)據(jù)的是發(fā)送方窗口。
為了便于討論,做如下假設:
發(fā)送的最初執(zhí)行慢開始,令 cwnd=1,發(fā)送方只能發(fā)送 1 個報文段;當收到確認后,將 cwnd 加倍,因此之后發(fā)送方能夠發(fā)送的報文段數(shù)量為:2、4、8 …
注意到慢開始每個輪次都將 cwnd 加倍,這樣會讓 cwnd 增長速度非???,從而使得發(fā)送方發(fā)送的速度增長速度過快,網(wǎng)絡擁塞的可能也就更高。設置一個慢開始門限 ssthresh,當 cwnd >= ssthresh 時,進入擁塞避免,每個輪次只將 cwnd 加 1。
如果出現(xiàn)了超時,則令 ssthresh = cwnd/2,然后重新執(zhí)行慢開始。
在接收方,要求每次接收到報文段都應該對最后一個已收到的有序報文段進行確認。例如已經(jīng)接收到 M1 和 M2,此時收到 M4,應當發(fā)送對 M2 的確認。
在發(fā)送方,如果收到三個重復確認,那么可以知道下一個報文段丟失,此時執(zhí)行快重傳,立即重傳下一個報文段。例如收到三個 M2,則 M3 丟失,立即重傳 M3。
在這種情況下,只是丟失個別報文段,而不是網(wǎng)絡擁塞。因此執(zhí)行快恢復,令 ssthresh = cwnd/2 ,cwnd = ssthresh,注意到此時直接進入擁塞避免。
慢開始和快恢復的快慢指的是 cwnd 的設定值,而不是 cwnd 的增長速率。慢開始 cwnd 設定為 1,而快恢復 cwnd 設定為 ssthresh。
??發(fā)送端的每個TCP報文都必須得到接收方的應答,才算傳輸成功。
??TCP為每個TCP報文段都維護一個重傳定時器。
??發(fā)送端在發(fā)出一個TCP報文段之后就啟動定時器,如果在定時時間類未收到應答,它就將重發(fā)該報文段并重置定時器。
??因為TCP報文段最終在網(wǎng)絡層是以IP數(shù)據(jù)報的形式發(fā)送,而IP數(shù)據(jù)報到達接收端可能是亂序或者重復的。TCP協(xié)議會對收到的TCP報文進行重排、整理,確保順序正確。
TCP報文段所攜帶的應用程序數(shù)據(jù)按照長度分為兩種:
交互數(shù)據(jù)
和
成塊數(shù)據(jù)
對于什么是粘包、拆包問題,我想先舉兩個簡單的應用場景:
對于之一種情況,服務端的處理流程可以是這樣的:當客戶端與服務端的連接建立成功之后,服務端不斷讀取客戶端發(fā)送過來的數(shù)據(jù),當客戶端與服務端連接斷開之后,服務端知道已經(jīng)讀完了一條消息,然后進行解碼和后續(xù)處理…。對于第二種情況,如果按照上面相同的處理邏輯來處理,那就有問題了,我們來看看
第二種情況
下客戶端發(fā)送的兩條消息遞交到服務端有可能出現(xiàn)的情況:
之一種情況:
服務端一共讀到兩個數(shù)據(jù)包,之一個包包含客戶端發(fā)出的之一條消息的完整信息,第二個包包含客戶端發(fā)出的第二條消息,那這種情況比較好處理,服務器只需要簡單的從網(wǎng)絡緩沖區(qū)去讀就好了,之一次讀到之一條消息的完整信息,消費完再從網(wǎng)絡緩沖區(qū)將第二條完整消息讀出來消費。
第二種情況:
服務端一共就讀到一個數(shù)據(jù)包,這個數(shù)據(jù)包包含客戶端發(fā)出的兩條消息的完整信息,這個時候基于之前邏輯實現(xiàn)的服務端就蒙了,因為服務端不知道之一條消息從哪兒結束和第二條消息從哪兒開始,這種情況其實是發(fā)生了TCP粘包。
第三種情況:
服務端一共收到了兩個數(shù)據(jù)包,之一個數(shù)據(jù)包只包含了之一條消息的一部分,之一條消息的后半部分和第二條消息都在第二個數(shù)據(jù)包中,或者是之一個數(shù)據(jù)包包含了之一條消息的完整信息和第二條消息的一部分信息,第二個數(shù)據(jù)包包含了第二條消息的剩下部分,這種情況其實是發(fā)送了TCP拆,因為發(fā)生了一條消息被拆分在兩個包里面發(fā)送了,同樣上面的服務器邏輯對于這種情況是不好處理的。
我們知道tcp是以流動的方式傳輸數(shù)據(jù),傳輸?shù)淖钚挝粸橐粋€報文段(segment)。tcp Header中有個Options標識位,常見的標識為mss(Maximum Segment Size)指的是,連接層每次傳輸?shù)臄?shù)據(jù)有個更大限制MTU(Maximum Tranission Unit),一般是1500比特,超過這個量要分成多個報文段,mss則是這個更大限制減去TCP的header,光是要傳輸?shù)臄?shù)據(jù)的大小,一般為1460比特。換算成字節(jié),也就是180多字節(jié)。
tcp為提高性能,發(fā)送端會將需要發(fā)送的數(shù)據(jù)發(fā)送到緩沖區(qū),等待緩沖區(qū)滿了之后,再將緩沖中的數(shù)據(jù)發(fā)送到接收方。同理,接收方也有緩沖區(qū)這樣的機制,來接收數(shù)據(jù)。
發(fā)生TCP粘包、拆包主要是由于下面一些原因:
既然知道了tcp是無界的數(shù)據(jù)流,且協(xié)議本身無法避免粘包,拆包的發(fā)生,那我們只能在應用層數(shù)據(jù)協(xié)議上,加以控制。通常在制定傳輸數(shù)據(jù)時,可以使用如下方法:
寫了一個簡單的 golang 版的tcp服務器實例,僅供參考:
例子
參考和推薦閱讀書目:
注釋:
eg.
Linux TCP/IP協(xié)議棧數(shù)據(jù)包處理流程及代碼實現(xiàn)分析
好吧,我來回答吧,首先是網(wǎng)卡驅(qū)動程序捕銀衡獲到數(shù)據(jù)包,做檢驗無誤后,和DMA以及CPU交互,然后由DMA和驅(qū)動程序創(chuàng)建BD表,然后分配skbuf(LINUX下)數(shù)據(jù)結構保存獲得的數(shù)據(jù)幀,內(nèi)核通過協(xié)議棧處理這個skbuf,通常是層層剝離每個層的首部,然后傳到上一層,細節(jié)就是一個變量做偏移量,每次做一個首部偏移讀取租搏鍵首部數(shù)據(jù),識別本層協(xié)議類型以及下一層協(xié)議類型,具體過程就是這個網(wǎng)絡原理的過程,請參考《TCP/IP詳解卷一》《linux設備驅(qū)動程序》《understanding linux network internals》弊巧《Unix網(wǎng)絡編程卷一》等。
太高聲了
】
關于linux tcp例子的介紹到此就結束了,不知道你從中找到你需要的信息了嗎 ?如果你還想了解更多這方面的信息,記得收藏關注本站。
創(chuàng)新互聯(lián)服務器托管擁有成都T3+級標準機房資源,具備完善的安防設施、三線及BGP網(wǎng)絡接入帶寬達10T,機柜接入千兆交換機,能夠有效保證服務器托管業(yè)務安全、可靠、穩(wěn)定、高效運行;創(chuàng)新互聯(lián)專注于成都服務器托管租用十余年,得到成都等地區(qū)行業(yè)客戶的一致認可。
當前文章:深入了解LinuxTCP的例子(linuxtcp例子)
網(wǎng)站URL:http://www.dlmjj.cn/article/djioojh.html


咨詢
建站咨詢
