為了能使操作系統(tǒng)提供的網(wǎng)絡(luò)接口更好地排查網(wǎng)絡(luò)通信問(wèn)題,我們需要熟悉操作系統(tǒng)提供的一些網(wǎng)絡(luò)接口函數(shù)。
接下來(lái)我們先討論一下這些網(wǎng)絡(luò)接口函數(shù)的使用注意事項(xiàng)
為了能使操作系統(tǒng)提供的網(wǎng)絡(luò)接口更好地排查網(wǎng)絡(luò)通信問(wèn)題,我們需要熟悉操作系統(tǒng)提供的一些網(wǎng)絡(luò)接口函數(shù)。
接下來(lái)我們先討論一下這些網(wǎng)絡(luò)接口函數(shù)的使用注意事項(xiàng):
1.以上函數(shù)如果調(diào)用出錯(cuò)后,返回值均為-1;但是返回值是-1,不一定代表出錯(cuò),這還得根據(jù)對(duì)應(yīng)的套接字模式(阻塞與非阻塞模式)。
2.默認(rèn)使用的socket函數(shù)創(chuàng)建的套接字是阻塞模式的,可以調(diào)用相關(guān)接口函數(shù)將其設(shè)置為非阻塞模式。阻塞模式和非阻塞模式的套接字,對(duì)
服務(wù)器的連接服務(wù)器和網(wǎng)絡(luò)數(shù)據(jù)的收發(fā)行為影響很大。詳情如下:
阻塞模式下,connect函數(shù)如果不能立刻連上服務(wù)器,會(huì)導(dǎo)致執(zhí)行流阻塞在那里一會(huì)兒,直到connect連接成功或失敗或網(wǎng)絡(luò)超時(shí);而非阻塞模式下,無(wú)論是否連接成功connect將立即返回,此時(shí)如果未連接成功,返回值將是-1,錯(cuò)誤碼是EINPROGRESS,表示連接操作仍然在進(jìn)行中。Linux平臺(tái)后續(xù)可以通過(guò)使用select/poll等函數(shù)檢測(cè)該socket是否可寫(xiě)來(lái)判斷連接是否成功。
阻塞套接字模式下,send函數(shù)如果由于對(duì)端tcp窗口太小,不足以將全部數(shù)據(jù)發(fā)送出去,將阻塞執(zhí)行流,直到出錯(cuò)或超時(shí)或者全部發(fā)送出去為止;同理recv函數(shù)如果當(dāng)前協(xié)議棧系統(tǒng)緩沖區(qū)中無(wú)數(shù)據(jù)可讀,也會(huì)阻塞執(zhí)行流,直到出錯(cuò)或者超時(shí)或者讀取到數(shù)據(jù)。send和recv函數(shù)的超時(shí)時(shí)間可以參考下文關(guān)于常用socket選項(xiàng)的介紹。
非阻塞套接字模式下,如果由于對(duì)端tcp窗口太小,不足以將數(shù)據(jù)發(fā)出去,它將立刻返回,不會(huì)阻塞執(zhí)行流,此時(shí)返回值為-1,錯(cuò)誤碼是EAGAIN或EWOULDBLOCK,表示當(dāng)前數(shù)據(jù)發(fā)不出去,希望你下次再試。但是返回值如果是-1,也可能是真正的出錯(cuò)了,也可能得到錯(cuò)誤碼EINTR,表示被linux信號(hào)中斷了,這點(diǎn)需要注意一下。recv函數(shù)與send函數(shù)情形一樣。
3.send函數(shù)雖然名稱叫“send”,但是其并不是將數(shù)據(jù)發(fā)送到網(wǎng)絡(luò)上去,只是將數(shù)據(jù)從應(yīng)用層緩沖區(qū)中拷貝到協(xié)議棧內(nèi)核緩沖區(qū)中,具體什么時(shí)候發(fā)送到網(wǎng)絡(luò)上去,與協(xié)議棧本身行為有關(guān)系,這點(diǎn)需要特別注意。所以即使send函數(shù)返回一個(gè)大于0的值n,也不能表明已經(jīng)有n個(gè)字節(jié)發(fā)送到網(wǎng)絡(luò)上去了。同樣的道理,recv函數(shù)也不是從網(wǎng)絡(luò)上收取數(shù)據(jù),只是從協(xié)議棧內(nèi)核緩沖區(qū)拷貝數(shù)據(jù)至應(yīng)用層緩沖區(qū),并不是真正地從網(wǎng)絡(luò)上收數(shù)據(jù),所以調(diào)用recv時(shí),操作系統(tǒng)的協(xié)議棧已經(jīng)將數(shù)據(jù)從網(wǎng)絡(luò)上收到自己的內(nèi)核緩沖區(qū)中了,recv僅僅是一次數(shù)據(jù)拷貝操作而已。
4.由于套接字實(shí)現(xiàn)是收發(fā)全雙工的,收和發(fā)通道相互獨(dú)立,不會(huì)相互影響,shutdown函數(shù)是用來(lái)選擇關(guān)閉socket收發(fā)通道中某一路,其how參數(shù)取值一般有三個(gè):SHUT_RD/SHUT_WR/SHUT_RDWR,SHUT_RD表示關(guān)閉收消息鏈路,即該套接字不能再收取數(shù)據(jù),同理SHUT_WR表示關(guān)閉套接字發(fā)消息鏈路,但是這里有個(gè)問(wèn)題,有時(shí)候我們需要等待緩沖區(qū)中數(shù)據(jù)發(fā)送完后再關(guān)閉連接怎么辦?這里就要用到套接字選項(xiàng)LINGER,關(guān)于這個(gè)選項(xiàng)請(qǐng)參考下文常見(jiàn)的套接字選項(xiàng)介紹。最后,SHUT_RDWR同時(shí)關(guān)閉收消息鏈路和發(fā)消息鏈路。
通過(guò)上面的分析我們得出結(jié)論,shutdown函數(shù)并不會(huì)要求操作系統(tǒng)底層回收套接字等資源,真正會(huì)回收資源是close函數(shù),這個(gè)函數(shù)會(huì)要求操作系統(tǒng)回收相關(guān)套接字資源,并釋放對(duì)ip地址與端口號(hào)二元組的占用,但是由于tcp四次揮手最后一個(gè)階段有個(gè)TIME_WAIT狀態(tài),導(dǎo)致與該socket相關(guān)的端口號(hào)資源不會(huì)被立即釋放,有時(shí)候?yàn)榱诉_(dá)到釋放端口用來(lái)復(fù)用,我們會(huì)設(shè)置套接字選項(xiàng)SOL_REUSEPORT。綜合起來(lái),我們關(guān)閉一個(gè)套接字,一般會(huì)先調(diào)用shutdown函數(shù)再調(diào)用close函數(shù),這就是所謂的優(yōu)雅關(guān)閉:
5.常見(jiàn)的套接字選項(xiàng)
嚴(yán)格意義上說(shuō)套接字選項(xiàng)是有不同層級(jí)的(level),如socket級(jí)別、TCP級(jí)別、IP級(jí)別,這里我們不區(qū)分具體的級(jí)別。
這兩個(gè)選項(xiàng)用于設(shè)置阻塞模式下套接字,SO_SNDTIMEO用于在send數(shù)據(jù)由于對(duì)端tcp窗口太小,發(fā)不出去而最大的阻塞時(shí)長(zhǎng);SO_RCVTIMEO用于recv函數(shù)因接受緩沖區(qū)無(wú)數(shù)據(jù)而阻塞的最大阻塞時(shí)長(zhǎng)。如果你需要獲取它們的默認(rèn)值,請(qǐng)使用getsockopt函數(shù)。
操作系統(tǒng)底層協(xié)議棧默認(rèn)有這樣一個(gè)機(jī)制,為了減少網(wǎng)絡(luò)通信次數(shù),會(huì)將send等函數(shù)提交給tcp協(xié)議棧的多個(gè)小的數(shù)據(jù)包合并成一個(gè)大的數(shù)據(jù)包,最后再一次性發(fā)出去,也就是說(shuō),如果你調(diào)用send函數(shù)往內(nèi)核協(xié)議棧緩沖區(qū)拷貝了一個(gè)數(shù)據(jù),這個(gè)數(shù)據(jù)也許不會(huì)馬上發(fā)到網(wǎng)絡(luò)上去,而是要等到協(xié)議棧緩沖區(qū)積累到一定量的數(shù)據(jù)后才會(huì)一次性發(fā)出去,我們把這種機(jī)制叫做nagle算法。默認(rèn)打開(kāi)了這個(gè)機(jī)制,有時(shí)候我們希望關(guān)閉這種機(jī)制,讓send的數(shù)據(jù)能夠立刻發(fā)出去,我們可以選擇關(guān)閉這個(gè)算法,這就可以通過(guò)設(shè)置套接字選項(xiàng)TCP_NODELAY,即關(guān)閉nagle算法。
linger這個(gè)單詞本身的意思,是“暫停、逗留”。這個(gè)選項(xiàng)的用處是用于解決,當(dāng)需要關(guān)閉套接字時(shí),協(xié)議棧發(fā)送緩沖區(qū)中尚有未發(fā)送出去的數(shù)據(jù),等待這些數(shù)據(jù)發(fā)完的最長(zhǎng)等待時(shí)間。
一個(gè)端口,尤其是作為服務(wù)器端端口在四次揮手的最后一步,有一個(gè)為T(mén)IME_WAIT的狀態(tài),這個(gè)狀態(tài)一般持續(xù)2MSL(MSL,maximum segment life, 最大生存周期,RFC上建議是2分鐘)。這個(gè)狀態(tài)存在原因如下:1. 保證發(fā)出去的ack能被送達(dá)(超時(shí)會(huì)重發(fā)ack)2. 讓遲來(lái)的報(bào)文有足夠的時(shí)間被丟棄,反過(guò)來(lái)說(shuō),如果不存在這個(gè)狀態(tài),那么可以立刻復(fù)用這個(gè)地址和端口號(hào),那么可能會(huì)收到老的連接遲來(lái)的數(shù)據(jù),這顯然是不好的。為了立即回收復(fù)用端口號(hào),我們可以通過(guò)開(kāi)啟套接字SO_REUSEADDR/SO_REUSEPORT。
默認(rèn)情況下,當(dāng)一個(gè)連接長(zhǎng)時(shí)間沒(méi)有數(shù)據(jù)來(lái)往,會(huì)被系統(tǒng)
防火墻之類的服務(wù)關(guān)閉。為了避免這種現(xiàn)象,尤其是一些需要長(zhǎng)連接的應(yīng)用場(chǎng)景下,我們需要使用心跳包機(jī)制,即定時(shí)從兩端定時(shí)發(fā)一點(diǎn)數(shù)據(jù),這種行為叫做“?;?rdquo;。而tcp協(xié)議棧本身也提供了這種機(jī)制,那就是設(shè)置套接字SO_KEEPALIVE選項(xiàng),開(kāi)啟這個(gè)選項(xiàng)后,tcp協(xié)議棧會(huì)定時(shí)發(fā)送心跳包探針,但是這個(gè)默認(rèn)時(shí)間比較長(zhǎng)(2個(gè)小時(shí)),我們可以繼續(xù)通過(guò)相關(guān)選項(xiàng)改變這個(gè)默認(rèn)值。
二、常用的網(wǎng)絡(luò)故障排查工具
1.ping
ping命令可用于測(cè)試網(wǎng)絡(luò)是否連通。
2.telnet
例如:
telnet 120.55.94.78 8888
結(jié)合ping和telnet命令我們就可以判斷一個(gè)服務(wù)器地址上的某個(gè)端口號(hào)是否可以對(duì)外提供服務(wù)。由于我們使用的開(kāi)發(fā)機(jī)器以windows居多,默認(rèn)情況下,windows系統(tǒng)的telnet命令是沒(méi)有打開(kāi)的,我們可以在【控制面板】- 【程序】- 【程序和功能】- 【打開(kāi)或關(guān)閉Windows功能】中打開(kāi)telnet功能。
3.host命令
host 命令可以
解析域名得到對(duì)應(yīng)的ip地址。
常見(jiàn)的選項(xiàng)有:
-a (all)顯示所有選項(xiàng),netstat默認(rèn)不顯示LISTEN相關(guān)
-t (tcp)僅顯示tcp相關(guān)選項(xiàng)
-u (udp)僅顯示udp相關(guān)選項(xiàng)
-n 拒絕顯示別名,能顯示數(shù)字的全部轉(zhuǎn)化成數(shù)字。(重要)
-l 僅列出有在 Listen (監(jiān)聽(tīng)) 的服務(wù)狀態(tài)
-p 顯示建立相關(guān)鏈接的程序名(macOS中表示協(xié)議 -p protocol)
-r 顯示路由信息,路由表
-e 顯示擴(kuò)展信息,例如uid等
-s 按各個(gè)協(xié)議進(jìn)行統(tǒng)計(jì) (重要)
-c 每隔一個(gè)固定時(shí)間,執(zhí)行該netstat命令。
5. lsof命令
lsof,即list opened filedescriptor,即列出當(dāng)前操作系統(tǒng)中打開(kāi)的所有文件描述符,socket也是一種file descriptor,常見(jiàn)的選項(xiàng)是:
-i 列出系統(tǒng)打開(kāi)的socket fd
-P 不要顯示端口號(hào)別名
-n 不要顯示ip地址別名(如localhost會(huì)用127.0.0.1來(lái)代替)
+c w 程序列名稱最大可以顯示到w個(gè)字符。
常見(jiàn)的選項(xiàng)組合為lsof –i –Pn:可以看到列出了當(dāng)前偵聽(tīng)的socket,和連接socket的tcp狀態(tài)。
6.pstack
嚴(yán)格意義上來(lái)說(shuō),這個(gè)不算網(wǎng)絡(luò)排查故障和調(diào)試命令,但是我們可以利用這個(gè)命令來(lái)查看某個(gè)進(jìn)程的線程數(shù)量和線程調(diào)用堆棧是否運(yùn)行正常。指令使用格式:
pstack pid即pstack 進(jìn)程號(hào)。
7.nc命令
即netcat命令,這個(gè)工具在排查網(wǎng)絡(luò)故障時(shí)非常有用,因而被業(yè)績(jī)稱為網(wǎng)絡(luò)界的“瑞士軍刀”。常見(jiàn)的用法如下:
nc l 0.0.0.0 8888
nc 0.0.0.0 8888
我們知道客戶端連接服務(wù)器一般都是操作系統(tǒng)隨機(jī)分配一個(gè)可用的端口號(hào)連接到服務(wù)器上去,這個(gè)指令甚至可以指定使用哪個(gè)端口號(hào)連接,如:
nc –p 12345 127.0.0.1 8888
客戶端使用端口12345去連接服務(wù)器127.0.0.1::8888。
8 .tcpdump
這個(gè)是linux系統(tǒng)自帶的抓包工具,功能非常強(qiáng)大,默認(rèn)需要開(kāi)啟root權(quán)限才能使用。其常見(jiàn)的選項(xiàng)有:
-i 指定網(wǎng)卡
-X –XX 打印十六進(jìn)制的網(wǎng)絡(luò)數(shù)據(jù)包
-n –nn 不顯示ip地址和端口的別名
-S 以絕對(duì)值顯示包的ISN號(hào)(包序列號(hào))
常用的過(guò)濾條件有如下形式:
tcpdump –i any ‘port 8888’
tcpdump –i any ‘tcp port 8888’
tcpdump –i any ‘tcp src port 8888’
tcpdump –i any ‘tcp src port 8888 and udp dst port 9999’
tcpdump -i any \'src host 127.0.0.1 and tcp src port 12345\' -XX -nn -vv關(guān)于tcpdump命令接下來(lái)將會(huì)以對(duì)tcp三次握手和四次揮手的包數(shù)據(jù)進(jìn)行抓包來(lái)分析。
三、tcp三次握手和四次揮手過(guò)程解析
熟練地掌握tcp三次握手和四次揮手過(guò)程的每一個(gè)細(xì)節(jié)是我們排查網(wǎng)絡(luò)問(wèn)題的基礎(chǔ)。下面我們來(lái)通過(guò)tcpdump抓包能實(shí)戰(zhàn)一下三次握手的過(guò)程,假設(shè)我的服務(wù)器端的地址是 127.0.0.0.1 : 12345,使用nc命令創(chuàng)建一個(gè)服務(wù)器程序并在這個(gè)地址上進(jìn)行偵聽(tīng):nc –v -l 127.0.0.0.112345;然后在客戶端機(jī)器上開(kāi)啟tcpdump工具;然后在客戶端使用nc命令創(chuàng)建一個(gè)客戶端去連接服務(wù)器;
三次握手過(guò)程是客戶端先給服務(wù)器發(fā)送一個(gè)SYN,然后服務(wù)器應(yīng)答一個(gè)SYN+ACK,應(yīng)答的序列號(hào)是遞增1的,表示應(yīng)答哪個(gè)請(qǐng)求,即從4004096087遞增到4004096088,接著客戶端再應(yīng)答一個(gè)ACK。這個(gè)時(shí)候,我們發(fā)現(xiàn)發(fā)包序列號(hào)和應(yīng)答序列號(hào)都變成1了,這是tcpdump使用相對(duì)序號(hào),我們加上-S選項(xiàng)后就變成絕對(duì)序列號(hào)了。
還有一種情況就是客戶端訪問(wèn)一個(gè)很遙遠(yuǎn)的ip,或者網(wǎng)絡(luò)繁忙,服務(wù)器對(duì)客戶端發(fā)送的網(wǎng)絡(luò)SYN報(bào)文沒(méi)有應(yīng)答,會(huì)出現(xiàn)什么情況呢?
我們先將防火墻的已有規(guī)則都清理掉: iptables -F;然后給防火墻的INPUT鏈上增加一個(gè)規(guī)則,丟棄本地網(wǎng)卡lo(也就是127.0.0.1這個(gè)回環(huán)地址)上的所有SYN包。接著,我們看到tcpdump抓到的數(shù)據(jù)包。連接不上,一共重試了5次,重試的時(shí)間間隔是1秒,2秒,4秒,8秒,16秒,最后返回失敗。這個(gè)重試次數(shù)在/proc/sys/net/ipv4/tcp_syn_retries 內(nèi)核參數(shù)中設(shè)置,默認(rèn)為6。
四次揮手與三次握手基本上類似。實(shí)際的網(wǎng)絡(luò)開(kāi)發(fā)中,尤其是高QPS的服務(wù)器程序,可能在在服務(wù)器程序所在的系統(tǒng)上留下大量非ESTABLISHED的中間狀態(tài),如CLOSE_WAIT/TIME_WAIT,我們可以使用以下指令來(lái)統(tǒng)計(jì)這些狀態(tài)信息:
netstat -n | awk \'/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}\'
得到結(jié)果可能類似。
下面看下一般比較關(guān)心的三種TCP狀態(tài)
服務(wù)端收到建立連接的SYN沒(méi)有收到ACK包的時(shí)候處在SYN_RECV狀態(tài)。有兩個(gè)相關(guān)系統(tǒng)配置:
1.net.ipv4.tcp_synack_retries,整形,默認(rèn)值是5
對(duì)于遠(yuǎn)端的連接請(qǐng)求SYN,內(nèi)核會(huì)發(fā)送SYN + ACK數(shù)據(jù)報(bào),以確認(rèn)收到上一個(gè) SYN連接請(qǐng)求包。這是三次握手機(jī)制的第二個(gè)步驟。這里決定內(nèi)核在放棄連接之前所送出的 SYN+ACK 數(shù)目。不應(yīng)該大于255,默認(rèn)值是5,對(duì)應(yīng)于180秒左右時(shí)間。通常我們不對(duì)這個(gè)值進(jìn)行修改,因?yàn)槲覀兿M鸗CP連接不要因?yàn)榕紶柕膩G包而無(wú)法建立。
2.net.ipv4.tcp_syncookies
一般服務(wù)器都會(huì)設(shè)置net.ipv4.tcp_syncookies=1來(lái)防止SYN Flood攻擊。假設(shè)一個(gè)用戶向服務(wù)器發(fā)送了SYN報(bào)文后突然死機(jī)或掉線,那么服務(wù)器在發(fā)出SYN+ACK應(yīng)答報(bào)文后是無(wú)法收到客戶端的ACK報(bào)文的(第三次握手無(wú)法完成),這種情況下服務(wù)器端一般會(huì)重試(再次發(fā)送SYN+ACK給客戶端)并等待一段時(shí)間后丟棄這個(gè)未完成的連接,這段時(shí)間的長(zhǎng)度我們稱為SYN Timeout,一般來(lái)說(shuō)這個(gè)時(shí)間是分鐘的數(shù)量級(jí)(大約為30秒-2分鐘)。這些處在SYNC_RECV的TCP連接稱為半連接,并存儲(chǔ)在內(nèi)核的半連接隊(duì)列中,在內(nèi)核收到對(duì)端發(fā)送的ack包時(shí)會(huì)查找半連接隊(duì)列,并將符合的requst_sock信息存儲(chǔ)到完成三次握手的連接的隊(duì)列中,然后刪除此半連接。大量SYNC_RECV的TCP連接會(huì)導(dǎo)致半連接隊(duì)列溢出,這樣后續(xù)的連接建立請(qǐng)求會(huì)被內(nèi)核直接丟棄,這就是SYN Flood攻擊。能夠有效防范SYN Flood攻擊的手段之一,就是SYN Cookie。SYN Cookie原理由D. J. Bernstain和 Eric Schenk發(fā)明。SYN Cookie是對(duì)TCP服務(wù)器端的三次握手協(xié)議作一些修改,專門(mén)用來(lái)防范SYN Flood攻擊的一種手段。它的原理是,在TCP服務(wù)器收到SYN包并返回SYN+ACK包時(shí),不分配一個(gè)專門(mén)的數(shù)據(jù)區(qū),而是根據(jù)這個(gè)SYN包計(jì)算出一個(gè)cookie值。在收到ACK包時(shí),TCP服務(wù)器在根據(jù)那個(gè)cookie值檢查這個(gè)TCP ACK包的合法性。如果合法,再分配專門(mén)的數(shù)據(jù)區(qū)進(jìn)行處理未來(lái)的TCP連接。觀測(cè)服務(wù)上SYN_RECV連接個(gè)數(shù)為:7314,對(duì)于一個(gè)高并發(fā)連接的通訊服務(wù)器,這個(gè)數(shù)字比較正常。
發(fā)起TCP連接關(guān)閉的一方稱為client,被動(dòng)關(guān)閉的一方稱為server。被動(dòng)關(guān)閉的server收到FIN后,但未發(fā)出ACK的TCP狀態(tài)是CLOSE_WAIT。出現(xiàn)這種狀況一般都是由于server端代碼的問(wèn)題,如果你的服務(wù)器上出現(xiàn)大量CLOSE_WAIT,應(yīng)該要考慮檢查代碼。
根據(jù)三次握手?jǐn)嚅_(kāi)連接規(guī)定,發(fā)起socket主動(dòng)關(guān)閉的一方 socket將進(jìn)入TIME_WAIT狀態(tài)。TIME_WAIT狀態(tài)將持續(xù)2MSL。TIME_WAIT狀態(tài)下的socket不能被回收使用。具體現(xiàn)象是對(duì)于一個(gè)處理大量短連接的服務(wù)器,如果是由服務(wù)器主動(dòng)關(guān)閉客戶端的連接,將導(dǎo)致服務(wù)器端存在大量的處于TIME_WAIT狀態(tài)的socket, 甚至比處于Established狀態(tài)下的socket多的多,嚴(yán)重影響服務(wù)器的處理能力,甚至耗盡可用的socket,停止服務(wù)。TIME_WAIT是TCP協(xié)議用以保證被重新分配的socket不會(huì)受到之前殘留的延遲重發(fā)報(bào)文影響的機(jī)制,是必要的邏輯保證。和TIME_WAIT狀態(tài)有關(guān)的系統(tǒng)參數(shù)有一般有3個(gè)。
四、 關(guān)于跨系統(tǒng)與跨語(yǔ)言之間的網(wǎng)絡(luò)通信連通問(wèn)題
如何在Java語(yǔ)言中去解析C++的網(wǎng)絡(luò)數(shù)據(jù)包,如何在C++中解析Java的網(wǎng)絡(luò)數(shù)據(jù)包,對(duì)于很多人來(lái)說(shuō)是一件很困難的事情,所以只能變著法子使用第三方的庫(kù)。其實(shí)使用tcpdump工具可以很容易解決與分析。
首先,我們需要明確字節(jié)序列這樣一個(gè)概念,即我們說(shuō)的大端編碼(big endian)和小端編碼(little endian),x86和x64系列的cpu使用小端編碼,而數(shù)據(jù)在網(wǎng)絡(luò)上傳輸,以及Java語(yǔ)言中,使用的是大端編碼。那么這是什么意思呢?
我們舉個(gè)例子,看一個(gè)x64機(jī)器上的32位數(shù)值在內(nèi)存中的存儲(chǔ)方式:i在內(nèi)存中的地址序列是0x003CF7C4~ 0x003CF7C8,值為40 e2 01 00。十六進(jìn)制0001e240正好等于10進(jìn)制123456,也就是說(shuō)小端編碼中權(quán)重高的的字節(jié)值存儲(chǔ)在內(nèi)存地址高(地址值較大)的位置,權(quán)重值低的字節(jié)值存儲(chǔ)在內(nèi)存地址低(地址值較小)的位置,也就是所謂的高高低低。相反,大端編碼的規(guī)則應(yīng)該是高低低高,也就是說(shuō)權(quán)值高字節(jié)存儲(chǔ)在內(nèi)存地址低的位置,權(quán)值低的字節(jié)存儲(chǔ)在內(nèi)存地址高的位置。
一個(gè)C++程序的int32值123456不作轉(zhuǎn)換地傳給Java程序,那么Java按照大端編碼的形式讀出來(lái)的值是:十六進(jìn)制40E20100 = 十進(jìn)制1088553216。所以,我們要么在發(fā)送方將數(shù)據(jù)轉(zhuǎn)換成網(wǎng)絡(luò)字節(jié)序(大端編碼),要么在接收端再進(jìn)行轉(zhuǎn)換。以上就是小編要和大家分享的服務(wù)器開(kāi)發(fā)中網(wǎng)絡(luò)數(shù)據(jù)分析與故障排查經(jīng)驗(yàn),希望對(duì)您有所幫助。