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

當(dāng)前位置: 首頁 > news >正文

做網(wǎng)站頁面該建多大的畫布以圖搜圖百度識(shí)圖

做網(wǎng)站頁面該建多大的畫布,以圖搜圖百度識(shí)圖,怎樣做o2o網(wǎng)站,寶安疫情最新情況上一節(jié),我們學(xué)習(xí)了TCP協(xié)議的服務(wù)器-客戶端的編程流程以及對(duì)中間的過程進(jìn)行了詳細(xì)的討論,那么,這一節(jié),我們對(duì)于TCP協(xié)議的特點(diǎn)進(jìn)行進(jìn)一步的分析,這也是面試的重點(diǎn)和難點(diǎn)。 目錄 一、TCP 協(xié)議特點(diǎn) 1.1 連接的建立與斷…

? ? ? ? 上一節(jié),我們學(xué)習(xí)了TCP協(xié)議的服務(wù)器-客戶端的編程流程以及對(duì)中間的過程進(jìn)行了詳細(xì)的討論,那么,這一節(jié),我們對(duì)于TCP協(xié)議的特點(diǎn)進(jìn)行進(jìn)一步的分析,這也是面試的重點(diǎn)和難點(diǎn)。

目錄

一、TCP 協(xié)議特點(diǎn)

1.1??連接的建立與斷開

1.1.1 面試題

1.2? TCP 狀態(tài)轉(zhuǎn)移(面試題)

1.3?流式服務(wù)特點(diǎn)

1.4?應(yīng)答確認(rèn)與超時(shí)重傳

1.5?滑動(dòng)窗口

二、多進(jìn)程、多線程處理并發(fā)?

三、UDP協(xié)議

3.1 UDP協(xié)議編程流程

3.2 UDP 協(xié)議特點(diǎn)

3.3 應(yīng)用場景

四、面試題

4.1 TCP和UDP的區(qū)別

4.2 同一個(gè)端口可不可以被一個(gè) TCP 和一個(gè) UDP 的應(yīng)用程序同時(shí)使用?

4.3 同一個(gè)應(yīng)用程序可以創(chuàng)建多個(gè)套接字嗎?


一、TCP 協(xié)議特點(diǎn)

? ? ? ?通過前面的學(xué)習(xí),我們知道:TCP 協(xié)議提供的是:面向連接、可靠的、字節(jié)流服務(wù)。

1.1??連接的建立與斷開

? ? ? ? 使用 TCP 協(xié)議通信的雙發(fā)必須先建立連接(三次握手),然后才能開始數(shù)據(jù)的讀寫。雙方都必須為該連接分配必要的內(nèi)核資源,以管理連接的狀態(tài)和連接上數(shù)據(jù)的傳輸。TCP 連接是全雙工的,雙方的數(shù)據(jù)可以通過一個(gè)連接進(jìn)行讀寫。完成數(shù)據(jù)交換之后,通信雙方都必須斷開連接以釋放系統(tǒng)資源(四次揮手)。 使用 tcpdump?抓包命令可以抓包觀察 TCP 連接的建立與關(guān)閉。該命令需要管理員權(quán)限,格式如下(假設(shè)兩個(gè)測試用的主機(jī) IP 地址為 192.168.43.214 和 192.168.43.160 ) :

三次握手發(fā)生在客戶端執(zhí)行 connect()的時(shí)候,該方法返回成功,則說明三次握手已經(jīng)建 立。三次握手示例圖如下:

客戶端執(zhí)行connect()會(huì)給服務(wù)器發(fā)送一個(gè)tcp報(bào)文,此時(shí)SYN標(biāo)志有效,還會(huì)發(fā)送一個(gè)序列號(hào);服務(wù)器收到報(bào)文,會(huì)發(fā)送報(bào)文回復(fù),此時(shí)SYN有效,發(fā)送一個(gè)序列號(hào),還回復(fù)會(huì)一個(gè)確認(rèn)號(hào)是客服端發(fā)送的序列號(hào)+1;客服端收到服務(wù)器的回復(fù),也會(huì)再次回復(fù)服務(wù)器,此時(shí)會(huì)發(fā)送確認(rèn)號(hào)是剛剛客戶端發(fā)送的序列號(hào)+1? ;

四次揮手發(fā)生在客戶端或服務(wù)端執(zhí)行 close()關(guān)閉連接的時(shí)候,示例圖如下:

當(dāng)一端要進(jìn)行close(),會(huì)給對(duì)方發(fā)送一個(gè)報(bào)文,此時(shí)FIN標(biāo)志有效,還有一個(gè)序列號(hào),然后對(duì)方收到報(bào)文,會(huì)回復(fù)對(duì)方已經(jīng)收到了,發(fā)送一個(gè)確認(rèn)號(hào)ACK,是剛剛發(fā)送的序列號(hào)+1;然后另一端也要close()關(guān)閉,也會(huì)給對(duì)方發(fā)送報(bào)文告訴對(duì)方字節(jié)要關(guān)閉了,FIN 序列號(hào),對(duì)方收到報(bào)文了,會(huì)回復(fù)對(duì)方已經(jīng)收到了,也發(fā)送一個(gè)確認(rèn)號(hào)ACK,確認(rèn)號(hào)是剛剛發(fā)送的序列號(hào)+1。

1.1.1 面試題

1、四次揮手的過程可以用三次完成嗎??

可以,四次揮手可以演化成三次揮手 。

? ? ? ? 當(dāng)一端close 發(fā)送報(bào)文過來,此時(shí)我也要close了,回復(fù)報(bào)文,和通知對(duì)方關(guān)閉的報(bào)文一起發(fā)送。

  1. 第一次揮手(FIN): 客戶端發(fā)送一個(gè)FIN報(bào)文,表示它要關(guān)閉到服務(wù)器的數(shù)據(jù)傳送。
  2. 第二次揮手(FIN): 服務(wù)器收到FIN后,直接發(fā)送一個(gè)FIN報(bào)文,表示它也要關(guān)閉到客戶端的數(shù)據(jù)傳送。
  3. 第三次揮手(ACK): 客戶端收到FIN后,發(fā)送一個(gè)ACK報(bào)文,確認(rèn)收到關(guān)閉請(qǐng)求,連接關(guān)閉。

2、揮手時(shí),可能受到什么樣的攻擊?

FIN Flood 攻擊

  • 原理:攻擊者發(fā)送大量的FIN包到目標(biāo)服務(wù)器。這些包會(huì)讓服務(wù)器嘗試關(guān)閉大量的連接,耗費(fèi)資源處理這些無效的連接終止請(qǐng)求。
  • 影響:服務(wù)器資源被耗盡,可能導(dǎo)致拒絕服務(wù)(DoS攻擊)。

3、為什么是三次握手,可不可以是兩次為什么?

? ? ? 握手只能是三次:例如客戶端連接服務(wù)器后然后關(guān)閉了,服務(wù)器收到了并回復(fù)客戶端,此時(shí)服務(wù)器就認(rèn)為和客戶端建立了鏈接,這個(gè)鏈接就一直保持著,但是客戶端已經(jīng)沒了,所以還需要客戶端第三次進(jìn)行確認(rèn)回復(fù),來確保雙方都保持鏈接。

4、三次握手時(shí)可能出現(xiàn)什么攻擊?

  • SYN Flood 攻擊

    • 原理:攻擊者發(fā)送大量的SYN請(qǐng)求包到目標(biāo)服務(wù)器,但不完成后續(xù)的握手步驟(即不發(fā)送ACK包)。目標(biāo)服務(wù)器會(huì)為每個(gè)SYN請(qǐng)求分配資源并等待ACK回應(yīng),這樣會(huì)導(dǎo)致服務(wù)器資源耗盡,無法處理合法用戶的請(qǐng)求。(當(dāng)有一個(gè)鏈接進(jìn)來就會(huì)先放到未完成三次握手隊(duì)列中,如果在短時(shí)間內(nèi)有人連續(xù)發(fā)送鏈接就會(huì)把未完成三次握手隊(duì)列塞滿,使真正要進(jìn)行鏈接的客戶端連接不上。)
    • 影響:造成服務(wù)器拒絕服務(wù)(DoS攻擊)。
  • SYN ACK Flood 攻擊

    • 原理:攻擊者在沒有發(fā)送初始SYN包的情況下,發(fā)送大量的SYN-ACK包到目標(biāo)服務(wù)器。服務(wù)器會(huì)浪費(fèi)資源去回復(fù)ACK,等待建立連接,導(dǎo)致資源耗盡。
    • 影響:和SYN Flood類似,可能導(dǎo)致拒絕服務(wù)。

1.2? TCP 狀態(tài)轉(zhuǎn)移(面試題)

? ? ? 下圖是 TCP 連接從建立到關(guān)閉整個(gè)過程中通信兩端狀態(tài)的變化。tcp狀態(tài)的改變是在建立連接和斷開連接的基礎(chǔ)上的 ,其中 CLOSED 是假想的起始點(diǎn),并不是一個(gè)實(shí)際的狀態(tài)。這種狀態(tài)變化就好比我們打電話通話處于不同的狀態(tài),但是只要雙方撥通了電話,那么就一直是通話中。只有在撥打電話和掛斷電話時(shí),狀態(tài)會(huì)發(fā)生變化。

? ? ? ? 上圖中,TIME_WAIT 狀態(tài)一般情況下是主動(dòng)關(guān)閉的一端才會(huì)出現(xiàn)的狀態(tài)。該狀態(tài)出現(xiàn)后,會(huì)維持一段長為 2MSL(Maximum Segment Life)的時(shí)間,才能完全關(guān)閉。MSL 是 TCP 報(bào)文 段在網(wǎng)絡(luò)中的最大生存時(shí)間,標(biāo)準(zhǔn)文檔 RFC1122 的建議值是 2min。?在 Linux 系統(tǒng)上,一個(gè) TCP 端口不能被同時(shí)打開多次(兩次及以上)。當(dāng)一個(gè) TCP 連接 處于 TIME_WAIT 狀態(tài)時(shí),我們將無法立即使用該連接占用著的端口來建立一個(gè)新連接,必須要等待這兩分鐘過去,才能繼續(xù)使用這個(gè)端口。

雙方同時(shí)關(guān)閉:

? ? ?雙方都執(zhí)行close(),都像對(duì)方發(fā)送FIN,雙發(fā)都變成FIN_WAIT_1狀態(tài),等到雙方都收到各自都收到對(duì)方發(fā)出的FIN,并發(fā)出ACK之后就會(huì)變成CLOSING狀態(tài),在等到雙方都收到對(duì)方的ACK之后就會(huì)變成TIME_WAIT狀態(tài)。

四次揮手演化成三次揮手:

? ? ? ? 主動(dòng)關(guān)閉端執(zhí)行close(),發(fā)FIN,被動(dòng)關(guān)閉端收到FIN,但此時(shí)被動(dòng)關(guān)閉端也要關(guān)閉了,就把ACK和FIN一起發(fā)送給主動(dòng)關(guān)閉端

connect()三次握手:

? ? ? 客戶端執(zhí)行connect()后進(jìn)行第一次進(jìn)行握手發(fā)出SYN狀態(tài)就變成了SYN_SENT狀態(tài),這個(gè)狀態(tài)非常短暫,會(huì)觀察不到,瞬間就沒了。

? ? ? 服務(wù)器收到SYN后,又給客戶端發(fā)出SYN,ACK后變成,SYN_RCVD狀態(tài)。

服務(wù)器和客戶端都完成三次握手狀態(tài)就會(huì)變成,ESTABLISHED。

close()四次揮手:

? ? ? ? 無論哪一方主動(dòng)執(zhí)行close()端,先發(fā)送FIN,然后主動(dòng)關(guān)閉端就會(huì)變成FIN_WAIT_1狀態(tài),然后對(duì)方收到FIN,再發(fā)ACK就會(huì)變成CLOSE_WAIT狀態(tài),主動(dòng)關(guān)閉端收到對(duì)方的回復(fù),就變成了FIN_WAIT_2狀態(tài)。此時(shí)兩次揮手結(jié)束。被動(dòng)關(guān)閉端執(zhí)行close(),會(huì)給主動(dòng)關(guān)閉端發(fā)送FIN,會(huì)變成LAST_ACK狀態(tài),主動(dòng)關(guān)閉端收到FIN并發(fā)送ACK,主動(dòng)關(guān)閉端狀態(tài)就變成了TIME_WAIT,然后被動(dòng)關(guān)閉段收到ACK,然后就消失了。

? ? ? ?TIME_WAIT會(huì)持續(xù)大概兩分鐘的時(shí)間。

如上圖所示:服務(wù)器會(huì)跟很多客戶端有連接,每個(gè)連接都有自己的狀態(tài)。每一個(gè)連接都會(huì)有自己的接收緩沖區(qū)和發(fā)送緩沖區(qū)。

使用命令:netstat -natp可以查看連接的狀態(tài)

面試題:

為什么TIME_WAIT狀態(tài)要持續(xù)一段時(shí)間?

1.可靠地終止TCP的連接。

2.保證讓遲來的TCP報(bào)文段有足夠的時(shí)間被識(shí)別并丟棄。

  1. 被動(dòng)關(guān)閉端關(guān)閉發(fā)FIN,主動(dòng)關(guān)閉端收到FIN,發(fā)ACK,變成TIME_WAIT,有可能被動(dòng)關(guān)閉端沒收到這個(gè)ACK,這個(gè)ACK在路上丟失了,過一會(huì)被動(dòng)關(guān)閉端沒收到主動(dòng)關(guān)閉端的ACK就會(huì)再次發(fā)FIN,如果TIME_WAIT狀態(tài)不持續(xù)直接關(guān)閉,那最后假如ACK丟失,被動(dòng)關(guān)閉端在發(fā)送FIN就沒人管它了。
  2. 在通訊的過程中,有一些數(shù)據(jù)正在發(fā)送,但還沒發(fā)送到,數(shù)據(jù)正在從A端到B端但還沒到,此時(shí)斷開接收端和發(fā)送端的連接,之后這個(gè)延遲的數(shù)據(jù)包到達(dá)了,但此時(shí)連接已經(jīng)斷開了,就會(huì)出現(xiàn)一些問題尤其是服務(wù)器。如果沒有TIME_WAIT狀態(tài),我們就可以立刻重新啟動(dòng)服務(wù)端,這樣延遲的數(shù)據(jù)包就會(huì)陸陸續(xù)續(xù)發(fā)到我們這個(gè)新啟動(dòng)的服務(wù)器里,雖然我們新啟動(dòng)的服務(wù)器用的是這個(gè)ip這個(gè)端口,延遲的數(shù)據(jù)包用的也是這個(gè)ip和端口,但是這些數(shù)據(jù)包是發(fā)給上個(gè)已經(jīng)結(jié)束的進(jìn)程的,不是發(fā)給我們這個(gè)新進(jìn)程的。因此就會(huì)讓TIME_WAIT狀態(tài)等待大概2分鐘,這倆分鐘是一個(gè)報(bào)文生存期最長時(shí)間的倆倍,這樣就會(huì)把我們網(wǎng)絡(luò)中延遲的數(shù)據(jù)包耗死,我們把這些延遲的數(shù)據(jù)一收延后丟掉,倆分鐘后網(wǎng)絡(luò)中就干凈了。

題目:

? ? ? ? 一個(gè)局域網(wǎng)內(nèi),有一個(gè)客戶端一個(gè)服務(wù)器,他們都已完成三次握手狀態(tài),沒有發(fā)送數(shù)據(jù),此時(shí)拔掉網(wǎng)線,服務(wù)器再close(),重新運(yùn)行服務(wù)器,運(yùn)行之后在插上網(wǎng)線,問此時(shí)客戶端跟服務(wù)器的狀態(tài)。

? ? ? ?網(wǎng)線拔掉之后,不進(jìn)行收發(fā)送數(shù)據(jù),雙方是不知道的,由于拔掉網(wǎng)線,關(guān)閉服務(wù)器,服務(wù)器會(huì)發(fā)送FIN,但是客戶端收不到,也不會(huì)回復(fù),服務(wù)器就等了倆分鐘后就關(guān)閉了,再重新啟動(dòng)服務(wù)器,此時(shí)服務(wù)器就是LISTEN狀態(tài)等待連接,客戶端還是完成三次握手狀態(tài)。

1.3?流式服務(wù)特點(diǎn)

? ? TCP 字節(jié)流的特點(diǎn),發(fā)送端執(zhí)行的寫操作次數(shù)和接收端執(zhí)行的讀操作次數(shù)之間沒有任何數(shù)量關(guān)系,應(yīng)用程序?qū)?shù)據(jù)的發(fā)送和接收是沒有邊界限制的。多次發(fā)送的數(shù)據(jù)會(huì)被對(duì)方一次接受,或者一次發(fā)送的數(shù)據(jù)被對(duì)方,分多次接受。

netstat -natp命令?可查看端口是否被占用 也能查看接收緩沖區(qū)和發(fā)送緩沖區(qū)有多少數(shù)據(jù)

  1. bind()會(huì)失敗的原因 :端口被占用或者把這個(gè)程序運(yùn)行了,又運(yùn)行了一個(gè),端口已經(jīng)分給第一個(gè)運(yùn)行的程序。
  2. recv()返回值為0是唯一判斷對(duì)方客戶端關(guān)閉鏈接的條件。
  3. connect()鏈接失敗原因:沒有運(yùn)行服務(wù)器,客戶端連接就會(huì)失敗。網(wǎng)斷了也鏈接不上。

修改循環(huán)收發(fā)的服務(wù)器端的代碼如下:
char buff[128]={0};
recv(sockfd,buff,1,0);
/*一次只接收一個(gè)字符*/

? ? ? ?客戶端發(fā)個(gè)hello,服務(wù)器將接收字符個(gè)數(shù)改成1,出現(xiàn)的結(jié)果是:循環(huán)5次把hello打印完,直到把buff里的數(shù)據(jù)打印完。客服端那里會(huì)一次收到5個(gè)ok。

? ? ? ? 這是因?yàn)榉?wù)器和客戶端都有一個(gè)接收緩沖區(qū)和發(fā)送緩沖區(qū),一端send()發(fā)送數(shù)據(jù),先把數(shù)據(jù)寫到發(fā)送緩沖區(qū)里,再通過底層協(xié)議把發(fā)送緩沖區(qū)的數(shù)據(jù)挪到對(duì)方的接受緩沖區(qū)中,然后對(duì)方再通過recv()把接收緩沖區(qū)中的數(shù)據(jù)讀出來。recv()發(fā)送成功只能說明成功將數(shù)據(jù)發(fā)達(dá)發(fā)送緩沖區(qū),對(duì)方并沒有收到。有可能會(huì)多次從接收緩沖區(qū)一次讀取,也有可能分多次讀取,就像我們購物從菜鳥驛站取快遞,我們?nèi)〕龅目爝f件也可能一次取完,也有可能還沒到菜鳥驛站,我們就需要分多次取。這就是TCP 粘包:連續(xù)多次send()發(fā)送的數(shù)據(jù),被對(duì)方recv()一次性收到。發(fā)送數(shù)據(jù)的次數(shù)跟接收數(shù)據(jù)的次數(shù)是不對(duì)應(yīng)的。所以會(huì)出現(xiàn)粘包。如何解決呢?(面試題)

解決粘包問題的常見方法有以下幾種:(面試題)

1. 使用定長消息

? ? ? ? ?通過規(guī)定每條消息的長度,接收方可以按照固定長度讀取數(shù)據(jù)。例如,如果消息長度固定為100字節(jié),接收方每次讀取100字節(jié)的數(shù)據(jù),就可以避免粘包問題。

2. 使用特殊分隔符

? ? ? ? 在每條消息的末尾添加特定的分隔符(如換行符、特殊字符等),接收方可以通過檢測分隔符來區(qū)分消息邊界。

3. 使用消息頭(長度前綴)

? ? ? ?在每條消息前添加一個(gè)消息頭,用于存儲(chǔ)消息的長度,接收方先讀取消息頭中的長度信息,再根據(jù)長度讀取具體的消息內(nèi)容。

1.4?應(yīng)答確認(rèn)與超時(shí)重傳

? ? ? ?TCP 發(fā)送的報(bào)文段交給 IP 層傳送的。但 IP 層只能提供盡最大努力的服務(wù),也就是說,TCP 下面的網(wǎng)絡(luò)所提供的是不可靠的傳輸。因此,TCP 必須采用適當(dāng)?shù)拇胧┎拍苁箖蓚€(gè)運(yùn)輸層之間的通信變得可靠。TCP 的可靠傳輸是通過使用應(yīng)答確認(rèn)和超時(shí)重傳來完成。下圖是通過 netstat 命令抓包看到的信息:

面試題:?

tcp的可靠性體現(xiàn)在:應(yīng)答確認(rèn)、超時(shí)重傳、去重、亂序重排、進(jìn)行流量控制滑動(dòng)窗口

  1. 應(yīng)答確認(rèn):給對(duì)方send()發(fā)送一個(gè)數(shù)據(jù),對(duì)方收到了,在底層會(huì)回復(fù)發(fā)送方表明收到數(shù)據(jù)了,A端給B端發(fā)送數(shù)據(jù),表面只能看到倆次交互,實(shí)際有四次,另外兩次我們看不到,但可以用tcpdump抓包命令看到。
  2. 超時(shí)重傳:給對(duì)方發(fā)送數(shù)據(jù)收,等了一段事件后沒有收到對(duì)方的回復(fù),就認(rèn)為這個(gè)數(shù)據(jù)丟失了,就會(huì)再重新發(fā)送一份數(shù)據(jù)給對(duì)方。
  3. 去重:給對(duì)方發(fā)送數(shù)據(jù),對(duì)方收到了,回復(fù)確認(rèn)收到信息,但回復(fù)這個(gè)信息丟失了,發(fā)送段沒收到,就會(huì)認(rèn)為發(fā)送的數(shù)據(jù)在路上丟失了,就會(huì)重新發(fā),然后接收端就會(huì)有倆個(gè)一樣的數(shù)據(jù),重復(fù)了就會(huì)去重。
  4. 亂序重排:后發(fā)送的數(shù)據(jù)比先發(fā)送的數(shù)據(jù)先到達(dá),這樣順序就會(huì)亂,但在接收到數(shù)據(jù)后,會(huì)對(duì)數(shù)據(jù)的順序進(jìn)行檢查。
  5. 滑動(dòng)窗口:?給對(duì)方發(fā)送數(shù)據(jù),一個(gè)字節(jié)一個(gè)字節(jié)發(fā)效率不高,就會(huì)有一個(gè)窗口,窗口左邊是已發(fā)送對(duì)方回復(fù)確認(rèn)的數(shù)據(jù),窗口內(nèi)是有已發(fā)送未收到確認(rèn)的和未發(fā)送的數(shù)據(jù),窗口右邊是超過窗口范圍內(nèi)外就不能發(fā)送的,窗口內(nèi)比如能夠發(fā)送100字節(jié),我們20字節(jié)一個(gè)包,這樣發(fā)送,發(fā)送20字節(jié),沒收到對(duì)方回復(fù),我們還能繼續(xù)發(fā)送,直到把這滑動(dòng)窗口內(nèi)的100字節(jié)數(shù)據(jù)全部發(fā)送完了,還沒收到對(duì)方回復(fù)收到的信號(hào),就不能再發(fā)送了,如果前面20字節(jié),對(duì)方回復(fù)收到了,這個(gè)窗口就向后移動(dòng),確保窗口內(nèi)數(shù)據(jù)有100個(gè)字節(jié),然后新到窗口內(nèi)的數(shù)據(jù)就能發(fā)送了。因?yàn)槿绻愎獍l(fā)送數(shù)據(jù),也不知道對(duì)方收沒收就到一直發(fā);或者就是對(duì)方一次性只能接受多少數(shù)據(jù),發(fā)太多也沒用。

下圖是無差錯(cuò)時(shí),數(shù)據(jù)交互的流程:發(fā)送端發(fā)送數(shù)據(jù) m1 給接收端,接收端收到數(shù)據(jù)后會(huì)給發(fā)送端一個(gè)確認(rèn)信息,以表明數(shù)據(jù)已經(jīng)被成功收到。在發(fā)送方未收到確認(rèn)信息前,M1 應(yīng)繼續(xù)被保留,直到確認(rèn)信息到達(dá)才能丟棄。

下圖是出現(xiàn)差錯(cuò)時(shí),數(shù)據(jù)交互的流程:

1.5?滑動(dòng)窗口

? ? ? ?TCP 協(xié)議是利用滑動(dòng)窗口實(shí)現(xiàn)流量控制的。一般來說,我們總是希望數(shù)據(jù)傳輸?shù)酶煲恍?#xff0c;不會(huì)一次只發(fā)一個(gè)字節(jié)。但是如果發(fā)送方把數(shù)據(jù)發(fā)得過快,接受方就可能來不及接收, 這就會(huì)造成數(shù)據(jù)的丟失。所謂流量控制就是讓發(fā)送方的發(fā)送速率不要太快,要讓接收方來的及接收。在 TCP 的報(bào)頭中有一個(gè)字段叫做接收通告窗口,這個(gè)字段由接收端填充,是接收端告訴發(fā)送端自己還有多少緩沖區(qū)可以接收數(shù)據(jù)。于是發(fā)送端就可以根據(jù)這個(gè)接收端的處理能力來發(fā)送數(shù)據(jù),而不會(huì)導(dǎo)致接收端處理不過來。所以發(fā)送端就會(huì)有一個(gè)發(fā)送窗口,這個(gè)發(fā)送窗口的大小是由接收端填充的接收通告窗口的大小決定的,并且窗口的位置會(huì)隨著發(fā)送端數(shù)據(jù)的發(fā)送和接收到接收端對(duì)數(shù)據(jù)的確認(rèn)而不斷的向右滑動(dòng),將之稱為滑動(dòng)窗口。發(fā)送方的滑動(dòng)窗口示意圖如下:

當(dāng)收到 36 的 ack,并發(fā)出 46-51 的字節(jié)后,窗口滑動(dòng)的示意圖如下:

二、多進(jìn)程、多線程處理并發(fā)?

? ? ? 如下圖所示, 當(dāng)一個(gè)客戶端與服務(wù)器建立連接以后,服務(wù)器端 accept()返回,進(jìn)而準(zhǔn)備循環(huán)接收客戶端發(fā)過來的數(shù)據(jù)。如果客戶端暫時(shí)沒發(fā)數(shù)據(jù),服務(wù)端會(huì)在第 40 行的 recv()阻 塞。此時(shí),其他客戶端向服務(wù)器發(fā)起連接后,由于服務(wù)器阻塞了,無法執(zhí)行 accept()接受連 接,也就是其他客戶端發(fā)送的數(shù)據(jù),服務(wù)器無法讀取。服務(wù)器也就無法并發(fā)同時(shí)處理多個(gè)客戶端。?

? ? ? 這個(gè)問題可以通過引入多線程和多進(jìn)程來解決。服務(wù)端接受一個(gè)客戶端的連接后,創(chuàng)建 一個(gè)線程或者進(jìn)程,然后在新創(chuàng)建的線程或進(jìn)程中循環(huán)處理數(shù)據(jù)。主線程(父進(jìn)程)只負(fù)責(zé)監(jiān)聽客戶端的連接,并使用 accept()接受連接,不進(jìn)行數(shù)據(jù)的處理。如下圖所示:

多線程處理并發(fā)的服務(wù)器端示例代碼 MultiThread.c 如下:主線程負(fù)責(zé)監(jiān)聽端口和接受客戶端連接,每接受到一個(gè)客戶端連接后,就創(chuàng)建一個(gè)新線程來處理該客戶端的通信。每個(gè)子線程會(huì)循環(huán)接收客戶端發(fā)送的數(shù)據(jù),并回復(fù)一個(gè)確認(rèn)消息"ok"。當(dāng)客戶端斷開連接時(shí),子線程會(huì)關(guān)閉相應(yīng)的客戶端套接字并退出。

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <assert.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <pthread.h>// 線程函數(shù),用來處理單個(gè)客戶端的收發(fā)數(shù)據(jù)
void* fun(void * arg)
{int c = (int)arg; // 將傳入的參數(shù)轉(zhuǎn)換為整數(shù)類型的客戶端套接字描述符while( 1 ){char buff[128] = {0}; // 用于接收數(shù)據(jù)的緩沖區(qū)// 接收客戶端發(fā)送的數(shù)據(jù),如果接收失敗或連接關(guān)閉,則退出循環(huán)if ( recv(c, buff, 127, 0) <= 0 ){break;}printf("recv(%d)=%s\n", c, buff); // 打印接收到的數(shù)據(jù)send(c, "ok", 2, 0); // 發(fā)送確認(rèn)消息給客戶端}printf("one client over(%d)\n", c); // 打印客戶端連接結(jié)束的消息close(c); // 關(guān)閉客戶端連接
}int main()
{int sockfd = socket(AF_INET, SOCK_STREAM, 0); // 創(chuàng)建套接字assert(sockfd != -1); // 確認(rèn)套接字創(chuàng)建成功struct sockaddr_in saddr, caddr; // 定義服務(wù)器和客戶端的地址結(jié)構(gòu)memset(&saddr, 0, sizeof(saddr)); // 將服務(wù)器地址結(jié)構(gòu)清零saddr.sin_family = AF_INET; // 設(shè)置地址族為AF_INETsaddr.sin_port = htons(6000); // 設(shè)置端口號(hào)為6000,并轉(zhuǎn)換為網(wǎng)絡(luò)字節(jié)序saddr.sin_addr.s_addr = inet_addr("127.0.0.1"); // 設(shè)置IP地址為127.0.0.1int res = bind(sockfd, (struct sockaddr*)&saddr, sizeof(saddr)); // 綁定套接字到指定的IP地址和端口assert(res != -1); // 確認(rèn)綁定成功listen(sockfd, 5); // 開始監(jiān)聽,最大連接數(shù)為5while( 1 ){int len = sizeof(caddr); // 客戶端地址結(jié)構(gòu)長度// 接受客戶端連接請(qǐng)求,返回客戶端套接字描述符int c = accept(sockfd, (struct sockaddr*)&caddr, &len);if ( c < 0 ){continue; // 如果接受失敗,繼續(xù)等待下一個(gè)連接}printf("accept c = %d\n", c); // 打印接受到的客戶端套接字描述符pthread_t id; // 定義線程id// 創(chuàng)建子線程處理客戶端連接,傳入客戶端套接字描述符作為參數(shù)pthread_create(&id, NULL, fun, (void*)c);}close(sockfd); // 關(guān)閉服務(wù)器套接字exit(0); // 退出程序
}

?多進(jìn)程處理并發(fā)的服務(wù)器端示例代碼 MultiProcess.c 如下:主進(jìn)程負(fù)責(zé)監(jiān)聽端口和接受客戶端連接,每接受到一個(gè)客戶端連接后,創(chuàng)建一個(gè)子進(jìn)程來處理該客戶端的通信。子進(jìn)程會(huì)循環(huán)接收客戶端發(fā)送的數(shù)據(jù),并回復(fù)一個(gè)確認(rèn)消息"OK"。當(dāng)客戶端斷開連接時(shí),子進(jìn)程會(huì)關(guān)閉相應(yīng)的客戶端套接字并退出。主進(jìn)程通過捕捉SIGCHLD信號(hào)來處理子進(jìn)程退出,防止產(chǎn)生僵尸進(jìn)程。

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <signal.h>// 處理客戶端連接的函數(shù)
void DealClientLink(int c, struct sockaddr_in caddr)
{while (1){char buff[128] = {0}; // 用于接收數(shù)據(jù)的緩沖區(qū)int n = recv(c, buff, 127, 0); // 接收客戶端發(fā)送的數(shù)據(jù)if (n <= 0) // 如果接收失敗或客戶端關(guān)閉連接,則退出循環(huán){break;}// 打印客戶端發(fā)送的數(shù)據(jù),包括客戶端的IP地址和端口號(hào)printf("%s:%d %s\n", inet_ntoa(caddr.sin_addr), ntohs(caddr.sin_port), buff);send(c, "OK", 2, 0); // 發(fā)送確認(rèn)消息給客戶端}printf("one client unlink\n"); // 打印客戶端斷開連接的消息close(c); // 關(guān)閉客戶端連接
}// 信號(hào)處理函數(shù),用于處理子進(jìn)程退出時(shí)的SIGCHLD信號(hào)
void sigfun(int sign)
{wait(NULL); // 等待子進(jìn)程結(jié)束,防止僵尸進(jìn)程
}int main()
{signal(SIGCHLD, sigfun); // 注冊(cè)SIGCHLD信號(hào)處理函數(shù),處理僵尸進(jìn)程int sockfd = socket(AF_INET, SOCK_STREAM, 0); // 創(chuàng)建套接字assert(-1 != sockfd); // 確認(rèn)套接字創(chuàng)建成功struct sockaddr_in saddr; // 定義服務(wù)器的地址結(jié)構(gòu)memset(&saddr, 0, sizeof(saddr)); // 將服務(wù)器地址結(jié)構(gòu)清零saddr.sin_family = AF_INET; // 設(shè)置地址族為AF_INETsaddr.sin_port = htons(6000); // 設(shè)置端口號(hào)為6000,并轉(zhuǎn)換為網(wǎng)絡(luò)字節(jié)序saddr.sin_addr.s_addr = inet_addr("127.0.0.1"); // 設(shè)置IP地址為127.0.0.1int res = bind(sockfd, (struct sockaddr*)&saddr, sizeof(saddr)); // 綁定套接字到指定的IP地址和端口assert(-1 != res); // 確認(rèn)綁定成功listen(sockfd, 5); // 開始監(jiān)聽,最大連接數(shù)為5while (1){struct sockaddr_in caddr; // 定義客戶端的地址結(jié)構(gòu)int len = sizeof(caddr); // 客戶端地址結(jié)構(gòu)長度int c = accept(sockfd, (struct sockaddr*)&caddr, &len); // 接受客戶端連接請(qǐng)求,返回客戶端套接字描述符assert(-1 != c); // 確認(rèn)接受成功// 打印接受到的客戶端連接成功的消息,包括客戶端的IP地址和端口號(hào)printf("%s:%d Link Success\n", inet_ntoa(caddr.sin_addr), ntohs(caddr.sin_port));pid_t pid = fork(); // 創(chuàng)建子進(jìn)程assert(-1 != pid); // 確認(rèn)子進(jìn)程創(chuàng)建成功if (0 == pid){DealClientLink(c, caddr); // 子進(jìn)程處理客戶端連接exit(0); // 必須結(jié)束子進(jìn)程,否則會(huì)有多個(gè)進(jìn)程調(diào)用accept}else{close(c); // 父進(jìn)程關(guān)閉客戶端連接描述符}}close(sockfd); // 關(guān)閉服務(wù)器套接字exit(0); // 退出程序
}

客戶端代碼 TcpClient.c 如下:客戶端首先創(chuàng)建一個(gè)套接字,然后連接到指定IP地址和端口號(hào)的服務(wù)器。連接成功后,客戶端進(jìn)入一個(gè)循環(huán),從標(biāo)準(zhǔn)輸入獲取用戶輸入的數(shù)據(jù),并將其發(fā)送到服務(wù)器。隨后,客戶端接收服務(wù)器的響應(yīng)并打印出來。如果用戶輸入"end",客戶端會(huì)退出循環(huán),關(guān)閉套接字并結(jié)束程序。

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <assert.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>int main()
{int sockfd = socket(AF_INET, SOCK_STREAM, 0); // 創(chuàng)建套接字assert(sockfd != -1); // 確認(rèn)套接字創(chuàng)建成功struct sockaddr_in saddr; // 定義服務(wù)器的地址結(jié)構(gòu)memset(&saddr, 0, sizeof(saddr)); // 將服務(wù)器地址結(jié)構(gòu)清零saddr.sin_family = AF_INET; // 設(shè)置地址族為AF_INETsaddr.sin_port = htons(6000); // 設(shè)置端口號(hào)為6000,并轉(zhuǎn)換為網(wǎng)絡(luò)字節(jié)序saddr.sin_addr.s_addr = inet_addr("127.0.0.1"); // 設(shè)置IP地址為127.0.0.1int res = connect(sockfd, (struct sockaddr*)&saddr, sizeof(saddr)); // 連接到服務(wù)器assert(res != -1); // 確認(rèn)連接成功while (1){char buff[128] = {0}; // 用于存儲(chǔ)用戶輸入的緩沖區(qū)printf("input:\n"); // 提示用戶輸入fgets(buff, 128, stdin); // 從標(biāo)準(zhǔn)輸入獲取用戶輸入if (strncmp(buff, "end", 3) == 0) // 如果用戶輸入"end",則退出循環(huán){break;}send(sockfd, buff, strlen(buff), 0); // 發(fā)送用戶輸入的數(shù)據(jù)到服務(wù)器memset(buff, 0, 128); // 清空緩沖區(qū)recv(sockfd, buff, 127, 0); // 接收服務(wù)器的響應(yīng)printf("buff=%s\n", buff); // 打印服務(wù)器的響應(yīng)}close(sockfd); // 關(guān)閉套接字exit(0); // 退出程序
}

三、UDP協(xié)議

3.1 UDP協(xié)議編程流程

? ? ?UDP 提供的是無連接、不可靠的、數(shù)據(jù)報(bào)服務(wù)。可以通俗的將TCP理解成打電話,UDP理解成發(fā)短信。

? ? ? ? socket()用來創(chuàng)建套接字,使用 udp 協(xié)議時(shí),選擇數(shù)據(jù)報(bào)服務(wù) SOCK_DGRAM。sendto() 用來發(fā)送數(shù)據(jù),由于 UDP 是無連接的,每次發(fā)送數(shù)據(jù)都需要指定對(duì)端的地址(IP 和端 口)。recvfrom()接收數(shù)據(jù),每次都需要傳給該方法一個(gè)地址結(jié)構(gòu)來存放發(fā)送端的地址。 recvfrom()可以接收所有客戶端發(fā)送給當(dāng)前應(yīng)用程序的數(shù)據(jù),并不是只能接收某一個(gè)客戶端的數(shù)據(jù)。

UDP 服務(wù)端編程示例代碼:

1. #include <stdio.h>
2. #include <stdlib.h>
3. #include <unistd.h>
4. #include <string.h>
5. #include <assert.h>
6. #include <sys/socket.h>
7. #include <netinet/in.h>
8. #include <arpa/inet.h>
9.
10. int main()
11. {
12.     int sockfd = socket(AF_INET,SOCK_DGRAM,0);
13.     assert( sockfd != -1 );
14.
15.     struct sockaddr_in saddr,caddr;
16.     memset(&saddr,0,sizeof(saddr));
17.     saddr.sin_family = AF_INET;
18.     saddr.sin_port = htons(6000);
19.     saddr.sin_addr.s_addr = inet_addr("127.0.0.1");
20.
21.     int res = bind(sockfd,(struct sockaddr*)&saddr,sizeof(saddr));
22.     assert( res != -1 );
23. 
24.     while( 1 )
25.     {
26.          int len = sizeof(caddr);
27.          char buff[128] = {0};
28.          recvfrom(sockfd,buff,127,0,(struct sockaddr*)&caddr,&len);
29.          printf("ip:%s,port:%d,buff=%s\n",inet_ntoa(caddr.sin_addr), ntohs(caddr.si
n_port),buff );
30.
31.         sendto(sockfd,"ok",2,0,(struct sockaddr*)&caddr,sizeof(caddr));
32.     }
33.
34.     close(sockfd);
35. }

UDP 客戶端編程示例代碼:

1. #include <stdio.h>
2. #include <stdlib.h>
3. #include <unistd.h>
4. #include <string.h>
5. #include <assert.h>
6. #include <sys/socket.h>
7. #include <netinet/in.h>
8. #include <arpa/inet.h>
9.
10. int main()
11. {
12. int sockfd = socket(AF_INET,SOCK_DGRAM,0);
13. assert( sockfd != -1 );
14.
15. struct sockaddr_in saddr;
16. memset(&saddr,0,sizeof(saddr));
17. saddr.sin_family = AF_INET;
18. saddr.sin_port = htons(6000);
19. saddr.sin_addr.s_addr = inet_addr("127.0.0.1");
20.
21. while( 1 )
22. {
23. char buff[128] = {0};
24. printf("input:\n");
25.
26. fgets(buff,128,stdin);
27.
28. if ( strncmp(buff,"end",3) == 0 )
29. {
30. break;
31. }
32.
33. sendto(sockfd,buff,strlen(buff),0,(struct sockaddr*)&saddr,sizeof(saddr));
34. memset(buff,0,128);
35.
36. int len = sizeof(saddr);
37. recvfrom(sockfd,buff,127,0,(struct sockaddr*)&saddr,&len);
38.
39. printf("buff=%s\n",buff);
40. }
41.
42. close(sockfd);
43. }

啟動(dòng)服務(wù)端和客戶端,再關(guān)掉服務(wù)端,還能再發(fā)送數(shù)據(jù)嘛?可以 因?yàn)閡dp是無連接的,只要服務(wù)端啟動(dòng),有人發(fā)數(shù)據(jù)就接受。關(guān)掉服務(wù)端對(duì)客戶端來說,絲毫沒有影響 ?

3.2 UDP 協(xié)議特點(diǎn)

? ? ? UDP 數(shù)據(jù)報(bào)服務(wù)特點(diǎn):發(fā)送端應(yīng)用程序每執(zhí)行一次寫操作,UDP 模塊就將其封裝成一 個(gè) UDP 數(shù)據(jù)報(bào)發(fā)送。接收端必須及時(shí)針對(duì)每一個(gè) UDP 數(shù)據(jù)報(bào)執(zhí)行讀操作,否則就會(huì)丟包,因此它不會(huì)出現(xiàn)粘包現(xiàn)象。 并且,如果用戶沒有指定足夠的應(yīng)用程序緩沖區(qū)來讀取 UDP 數(shù)據(jù),則 UDP 數(shù)據(jù)將被截?cái)唷?/p>

3.3 應(yīng)用場景

? ? ?tcp和udp應(yīng)用分場景,例如下載一個(gè)文件,肯定是要完整的下載下來,數(shù)據(jù)不能丟失。如果實(shí)時(shí)通話視頻時(shí),那就用udp,因?yàn)橹皇且串?dāng)下的你,如果視頻過程中網(wǎng)不好,數(shù)據(jù)沒發(fā)出去,再重新發(fā),這樣慢慢的就會(huì)變成錄屏,因?yàn)閠cp有接收緩沖區(qū),重新發(fā)的數(shù)據(jù)都會(huì)被對(duì)方,接收到接受緩沖區(qū),對(duì)方要全部讀完,所以這一幀數(shù)據(jù)沒發(fā)送成功就不要了。

四、面試題

4.1 TCP和UDP的區(qū)別

tcp是面向連接的可靠的流式服務(wù),udp是無連接不可靠的數(shù)據(jù)報(bào)服務(wù) 。

  1. tcp建立連接要進(jìn)行三次握手,而udp不需要建立連接直接指定地址發(fā)數(shù)據(jù)就行
  2. tcp在發(fā)送數(shù)據(jù)時(shí)有應(yīng)答確認(rèn),超時(shí)重傳機(jī)制,而udp發(fā)送數(shù)據(jù)成功就成功,失敗了也不會(huì)重發(fā)。
  3. tcp會(huì)出現(xiàn)粘包,udp不會(huì)出現(xiàn)粘包。

4.2 同一個(gè)端口可不可以被一個(gè) TCP 和一個(gè) UDP 的應(yīng)用程序同時(shí)使用?

? ? ? ?是的,可以,同一個(gè)端口可以同時(shí)被一個(gè) TCP 應(yīng)用程序和一個(gè) UDP 應(yīng)用程序使用。TCP 和 UDP 是兩個(gè)不同的傳輸層協(xié)議,它們的連接和數(shù)據(jù)傳輸方式不同,因此它們可以在相同的端口號(hào)上共存。操作系統(tǒng)和網(wǎng)絡(luò)棧通過區(qū)分傳輸層協(xié)議(TCP 或 UDP)來將數(shù)據(jù)包正確地交付給對(duì)應(yīng)的應(yīng)用程序。例如,假設(shè)你有一個(gè) TCP 服務(wù)在端口 8080 上運(yùn)行,同時(shí)你也可以在相同的端口 8080 上運(yùn)行一個(gè) UDP 服務(wù)。這兩個(gè)服務(wù)不會(huì)互相干擾,因?yàn)椴僮飨到y(tǒng)能夠根據(jù)協(xié)議類型將到達(dá)端口 8080 的 TCP 數(shù)據(jù)包和 UDP 數(shù)據(jù)包區(qū)分開來并分別處理。

4.3 同一個(gè)應(yīng)用程序可以創(chuàng)建多個(gè)套接字嗎?

同一個(gè)應(yīng)用程序可以創(chuàng)建多個(gè)套接字。套接字是網(wǎng)絡(luò)通信的基礎(chǔ),它允許程序發(fā)送和接收數(shù)據(jù)。應(yīng)用程序創(chuàng)建多個(gè)套接字的原因有很多,包括但不限于以下幾個(gè)方面:

  1. 多協(xié)議支持:一個(gè)應(yīng)用程序可能需要同時(shí)支持多種協(xié)議,例如同時(shí)使用 TCP 和 UDP,這時(shí)它需要分別為 TCP 和 UDP 創(chuàng)建不同的套接字。

  2. 多端口監(jiān)聽:一個(gè)服務(wù)器應(yīng)用程序可能需要監(jiān)聽多個(gè)端口,以便提供不同的服務(wù)或支持不同的協(xié)議版本。例如,一個(gè)應(yīng)用程序可以同時(shí)監(jiān)聽 80 端口(HTTP)和 443 端口(HTTPS)。

  3. 客戶端連接管理:對(duì)于一個(gè) TCP 服務(wù)器,每當(dāng)一個(gè)客戶端連接到服務(wù)器時(shí),服務(wù)器通常會(huì)為每個(gè)客戶端連接創(chuàng)建一個(gè)新的套接字。這允許服務(wù)器同時(shí)處理多個(gè)客戶端連接。

  4. 多線程或多進(jìn)程通信:應(yīng)用程序可能使用多個(gè)套接字來實(shí)現(xiàn)多線程或多進(jìn)程間的通信。例如,一個(gè)線程或進(jìn)程負(fù)責(zé)監(jiān)聽網(wǎng)絡(luò)連接,另一個(gè)線程或進(jìn)程負(fù)責(zé)處理數(shù)據(jù)。

至此,已經(jīng)講解完畢!篇幅較長,慢慢消化,以上就是全部內(nèi)容!請(qǐng)務(wù)必掌握,創(chuàng)作不易,歡迎大家點(diǎn)贊加關(guān)注評(píng)論,您的支持是我前進(jìn)最大的動(dòng)力!下期再見!

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

相關(guān)文章:

  • 做網(wǎng)站用java還是c語言專業(yè)推廣引流團(tuán)隊(duì)
  • 網(wǎng)站維護(hù)一般多久上海優(yōu)化外包
  • 法院文化建設(shè)網(wǎng)站女孩短期技能培訓(xùn)班
  • 網(wǎng)站后臺(tái)管理系統(tǒng)制作教程長春網(wǎng)站優(yōu)化哪家好
  • 營銷型網(wǎng)站制作哪家好網(wǎng)絡(luò)營銷的特點(diǎn)
  • 網(wǎng)站建設(shè)方案及預(yù)算百度上做優(yōu)化一年多少錢
  • 湛江網(wǎng)站的建設(shè)網(wǎng)站關(guān)鍵詞優(yōu)化推廣哪家快
  • 網(wǎng)站中的qq客服怎么做班級(jí)優(yōu)化大師是干什么用的
  • 如何推廣運(yùn)營網(wǎng)站百度付費(fèi)推廣
  • 網(wǎng)站建設(shè)頭部代碼網(wǎng)站描述和關(guān)鍵詞怎么寫
  • 長沙私人做網(wǎng)站現(xiàn)在推廣平臺(tái)哪家最好
  • wordpress和emlog重慶seo和網(wǎng)絡(luò)推廣
  • 網(wǎng)站開發(fā)文檔管理工具韓國網(wǎng)站
  • 淄博網(wǎng)站建設(shè)相關(guān)文章如何快速推廣
  • 天津做網(wǎng)站優(yōu)化公司上海網(wǎng)絡(luò)推廣優(yōu)化公司
  • 如何在網(wǎng)站后臺(tái)找到死鏈接群站優(yōu)化之鏈輪模式
  • 老河口做網(wǎng)站免費(fèi)的外貿(mào)b2b網(wǎng)站
  • 單位網(wǎng)站建設(shè)工作功勞網(wǎng)絡(luò)營銷策劃書包括哪些內(nèi)容
  • 湖南城鄉(xiāng)建設(shè)網(wǎng)站全網(wǎng)絡(luò)品牌推廣
  • 怎么給公司建網(wǎng)站河南網(wǎng)站建設(shè)定制
  • 商城網(wǎng)站搜狗優(yōu)化排名
  • wordpress頁面的評(píng)論功能嘉興網(wǎng)站建設(shè)方案優(yōu)化
  • 有了網(wǎng)站源碼怎么做網(wǎng)站武漢百度推廣多少錢
  • 自己搭建網(wǎng)站的步驟seo搜索引擎優(yōu)化實(shí)訓(xùn)總結(jié)
  • 基于ipv6的網(wǎng)站開發(fā)鄭州百度推廣開戶
  • 網(wǎng)站服務(wù)器搭建的步驟采集站seo課程
  • 海南網(wǎng)站建設(shè)服務(wù)外貿(mào)谷歌seo
  • 萊蕪在線沙總寧波seo在線優(yōu)化方案
  • 鄭州英文網(wǎng)站建設(shè)刷排名seo
  • 網(wǎng)站建設(shè)與管理教學(xué)計(jì)劃長沙網(wǎng)站se0推廣優(yōu)化公司