三個字節的歷險

引子

無論你相不相信,每一個程序的日誌都記錄了一個完整的故事,一個由代碼和數據相互交談而產生的故事。就像大家人類的故事同樣,這裏面既有平常雜事,又有奇事趣聞,天然也少不了各類喜怒哀樂。只要你仔細聆聽,就必定可以聽懂。程序員

請注意,當我在說這些話的時候,我並非在打一個比喻,而是在描述確鑿的事實,就像太陽的東昇西落那樣無可置疑。每一個日誌中記錄的故事,在那個世界都曾經真切的發生過。若是你仍是不相信,請閱讀下面這個故事,它是個人一段親身經歷。這個故事就記錄在一個程序日誌裏,而這個日誌文件至今還躺在主人的筆記本電腦中的某個位置,隨時供人查閱。算法

哦,對了,你還不知道我是誰?其實跟故事自己比起來,這並不那麼重要。我只是數字空間(Cyberspace)裏一個普通的字節(Byte)。我能夠隨身攜帶8個比特(Bit)的信息,請叫我0x30。數據庫

相遇,啓程

按照大家人類的說法,我是個男生。爲了敘述方便,姑且按照這樣的方式來定義吧——奇數值的字節是女生,偶數值的字節是男生。這樣說的話,如今緊挨着我坐一塊兒的這兩位,0x35和0x32,一位是女生,而另外一位就是男生嘍。一樣是爲了敘述方便,下面我給他們各起一我的類的名字,0x35叫小麗,0x32叫小明。而我呢,若是大家願意,能夠叫我小白。編程

在咱們三個碰到一塊兒以前,咱們已經在數據庫裏睡了幾個世紀之久(固然是按照數字空間的時間單位)。多半是由於受到某個網絡請求的觸發,咱們被加載到了內存裏。此刻,咱們位於一個很龐大的對象實例的內部。我粗略估算了一下,同咱們一塊兒被加載進來的,大概還有幾千個字節,他們大部分都存儲在這個對象內部的一個List中。c#

等了一會,負責執行CPU指令的進程老大跑過來告訴咱們,「你們注意,大家立刻就要被髮送到一條TCP長鏈接上去,對方正在等待大家所攜帶的信息。因爲這是一個串行的通道,因此,大家所在的整個對象首先要被序列化(Serialization)。」安全

我正在困惑之際,沒想到小麗率先替我問出了心中的疑問:服務器

「序列化?那是什麼意思啊?」網絡

她說話的聲音很好聽,就像某個電臺的女主播。我不由有些恍惚。分佈式

「序列化就是按字節前後排成一隊,就好像回龍觀西大街上的汽車要通過北郊農場橋,全部車最後都必須併到一個車道上去。」進程老大耐心地向咱們解釋道。工具

而後,咱們按照一種被稱爲protobuf的數據格式完成了序列化。聽說,這種數據格式是由一個「不存在」的國際化大公司設計出來的。無論那麼多了,如今重要的是,咱們原來在同一個對象實例內部的全部字節被排成了長長的一隊,並且我發現,隊伍中還插入了一些額外的字節。最後隊伍的總長度達到了3400個字節。

在序列化以後的隊伍中,小麗,小明,和我,咱們三個仍然挨在一塊兒。我排在第1461個字節位置,而他們倆都在我前面。也就是說,小麗排在第1459個字節位置,小明排在第1460個字節位置。

算法小哥告誡咱們,隊伍一旦排好,順序就絕對不能再變了,並且全部字節一個都不能少。不然,到了接收端,咱們將沒法被反序列化(Deserialization)。這固然也包括新插入的那些字節,好比站在隊頭的那幾個,他們攜帶着整個隊伍的長度信息,相當重要。

「夥計們,如今大家已經組成一個完整的消息(Message)了。一帆風順!」說完,進程老大就將咱們由字節組成的整個消息隊伍扔到了協議棧的buffer之中。

協議棧的buffer就像一間很大的候車大廳,咱們並排坐在裏面,當心地保持着原來的次序。

負責維護協議棧的內核程序告訴咱們,TCP通道上正在進行流量控制(Flow Control),緣由是TCP另外一端的接收窗口目前不容許咱們發送。因此,須要咱們耐心等待。

爲了打發無聊的時間,我就跟前面的小麗和小明他們攀談了起來。聊了一陣子,小明忽然提議說:「反正也閒得無聊,要不咱們一塊兒玩個遊戲?」

「好啊好啊,真心話大冒險怎麼樣?」我趕忙附和,並用眼角的餘光掃了一下小麗。

小麗遲疑了一下,「這個有點太刺激了,要不咱們仍是來個文藝點的吧。每一個人講一個故事如何?看誰講的故事精彩!」

對於女人的建議,我和小明天然都欣然採納,「那就這樣,按順序來,女士優先吧。」

小明又補充道:「畢竟咱們都生活在數字空間,並且,大家知道嗎——咱們的聽衆基本上都是程序員。因此我建議,故事最好要跟程序員或編程有關。」

小麗聽完白了他一眼,不置能否。

接下來,小麗用她悽婉的語調講述了一個令咱們全部人都感動不已的故事。

我在死神前轉身,只因你一輩子的堅守

窗外是淅淅瀝瀝的小雨,而她的心情也跌落到了谷底。她想象着那些冰冷的雨滴,滴答滴答,落在肌膚上,浸透她的全身。

古靈兒躺在牀上,睜着大眼睛瞪着天花板。她從藥瓶中又倒出了一粒藥片,動做嫺熟地一口吞進了肚子裏。已是第十粒安定片了,卻了無睡意。她如今有點懷疑網上的說法,安眠藥吃到100片真的能致人死命嗎?

她如今還有最後一件事要作,這也是她最後的一點但願了。她把已經關機了好幾天的手機從新拿出來,開機,而後把卸載掉的各類社交軟件也從新安裝了回來。

除了那些討債的人,沒有任何人聯繫她。她的愛人沒有任何消息,沒有短信,沒有留言,沒有未接電話。

那些上門討債的人曾經告訴過她,她的老公攜款潛逃了,帶走了他們的血汗錢。直到如今,古靈兒也都不相信,那個深愛她的男人會拋下她無論。但事實彷佛無可爭辯,她老公的朋友圈最後一次更新仍是在兩個月前,也就是他忽然消失不見的前幾天。

最近這些日子,是她人生中最痛苦的一段經歷。爲了躲避那些上門討債的人,她搬了好幾回住處,卸掉了全部的社交軟件,甚至不少時候不得不把手機關掉,但手機號卻一直堅持沒換,由於她怕老公聯繫不到她。至今她仍然心存幻想,幻想着忽然收到他的消息,而後他會向她解釋全部這一切。

其實她對最近發生的事情一直都很迷惑。他老公的公司經營得一直還算順利,甚至在他消失的前不久,他還躊躇滿志地告訴她公司立刻就要進入下一輪融資了。直到兩個月前,一羣人忽然闖進她們的家裏,宣稱她老公的公司產生了上億的壞帳,並要求她替丈夫還債。而她居然也真的聯繫不到他了。

如今她知道了,幻想終歸是幻想,她的心從新變得冰冷。她又拿出藥瓶,吞下一粒。此刻,她頭腦中只剩下一個念頭,就這麼吃下去,直到永遠睡去。

忽然,「叮」的一聲,手機收到了一條推送。她一把抓過手機,居然是來自她已經好久不玩的一款App——名字叫「微愛」的情侶軟件。那是好久好久以前,她和她的前男朋友,也是她的初戀,一塊兒在「微愛」上開通的私有空間。本是小孩子的把戲,兩我的天天堅持澆灌一棵虛擬的愛情樹。早在兩年前他們分手以後,她就再也不玩了。沒想到,那個像傻瓜同樣的他卻還在堅持。手機推送告訴她,對方已經連續澆水超過2000天了,他們的愛情樹又升到了一個新的等級。

思緒如快速生長的草木通常,觸及到了一段塵封的記憶。

兩年前,那也是一個雨天,五月的一個雨天。天氣本應是溫暖宜人的,但那天卻格外陰冷。就像今天同樣。

「咱們仍是分手吧。」有多少次想說,卻始終沒法開口。終於,古靈兒仍是把那個句子吐了出來。

蘇秦表情木然地站在那裏,本來就黯淡無光的眼睛完全蒙上了一層灰紗。

再也不有悲傷和爭吵。該吵的,該鬧的,該哭泣的,都已通過去了。在一塊兒三年多了,兩我的之間細小的戰役也斷斷續續地持續了三年。

該走的終會走。

這並不能怪古靈兒。整日面對這樣一個吊兒郎當的男人,哪一個女人又會有安全感呢?提及來有點諷刺,蘇秦,跟古代那個「頭懸樑,錐刺股」的蘇秦是同一個名字,可怎麼就沒有一點相像呢?那時,他畢業後幹程序員這一行也好幾年了,不只沒得到任何升職機會,甚至工資都沒漲過太多。他這性格從上大學那會就一直這樣,平日裏嘻嘻哈哈,東遊西逛,歷來不會認認真真地鑽研點東西,還常常花大把的時間打遊戲。幹程序員這一行的,本應是個高薪行業,他周圍的不少同窗早就買房買車了。

而古靈兒的家裏人一直對她的婚事催個不停。稍微有點理智的女人,都會像她這樣選擇的。跟蘇秦相比,她後來的丈夫無論哪一點,都要強上一萬倍。沒想到......

一樣沒想到的是,在這樣晦暗的時刻,他又以另外的一種方式出如今她的面前。她忽然想起了他過去全部的好,想起了他騎着自行車帶着她遊遍了北京城的每個角落,想起了那一天,當她最後轉身離開的時候,蘇秦向着她在雨中的背影大聲地喊:「靈兒,我會等你回來的!」

古靈兒從牀上爬起來,走到穿衣鏡前。她緩緩地脫去身上的睡衣,扔在一邊,鏡中出現了她憔悴的面容、赤裸而日漸瘦弱的身體。一股巨大的悲傷感向她襲來,如洶涌的波濤通常。她再也控制不住本身,兩行熱淚奪眶而出。

她蹲在臥室的地上哭了半個小時。最後,她拿起手機,從通信簿中找到了蘇秦的號碼。

遙遠的路途

「講完了?」

面對我和小明的問題,小麗點了點頭。

「你這故事跟程序員一點關係也沒有啊!」小明提醒她。

「怎麼沒有?那個前男朋友就是個程序員呢!」小麗反駁說。

「但故事情節跟程序員不要緊啊......」小明大概是感受有點說不清楚,換了個問法,「那他會寫複雜的程序嗎?他是個技術高手嗎?」

「不是。這根本不重要!」小麗一臉不耐煩地回答。

「你這故事是真的嗎?」我問小麗。

「固然了!我就曾參與過那次關鍵的推送,我是那條推送消息裏的一個字節。」小麗回答問題的同時,也說出了本身的來歷。

「後來他們怎麼樣了呢?」我繼續追問。

「她和蘇秦嗎?固然是幸福地在一塊兒了啊。」

「那古靈兒的老公欠的的那些債務呢?最後怎麼解決的呢?」

小麗對這個問題彷佛也不太感興趣,「我也不清楚了。估計蘇秦會幫她打官司吧。她應該沒有義務承擔這筆債務吧。」

正在這時,協議棧的內核程序忽然又跑過來,向咱們喊道,「快點準備!立刻要出發了!對方的TCP接收窗口已經打開。」說着,他打量了一下全部這3400個字節,補充道,「如今buffer裏字節太多,對方的接收窗口一會兒裝不下。這樣吧,前面2000個字節先走!」

緊接着,協議棧各個協議層的子程序又在咱們這2000個字節的最前面加上了20個字節的TCP頭,而後又在前面增長了20個字節的IP頭,最後,又在這2040個字節的前面和後面都增長了鏈路層的幀頭和幀尾。

咱們從內網上行路由器的一個出口出來,進入了一條光纖通道。路由器上的路由程序告訴咱們,下一站是廣州的一個節點。

「咱們這一站要走3000多千米啊!」

「可不是嘛!」

咱們三個又開始閒聊起來。

小麗感到很困惑,「咱們的出發地點和目的地好像都在北京,爲何要先到廣州走一圈呢?」

「這多是一種路由策略。我猜多是網絡運營商在南方的線路資源比較便宜,纔會給出這樣的路由。也多是一種配置錯誤。」我根據我僅有的一點網絡知識,向小麗解釋。

「對咱們會有什麼影響嗎?」小麗不無擔憂的問。

「咱們中間會通過更多的路由節點,延遲會高一些。若是跳轉節點過多,而咱們的IP包頭的TTL (Time To Live)又被耗盡的話,咱們可能被總體丟棄掉。」我忽然意識到把問題說得太嚴重了,趕忙又補充了一句,「不過這種丟棄的狀況不多見啦。」

雖然嘴上這樣說,但我內心卻產生了一股不祥的預感。多是懼怕的緣由,小麗也有些臉色發白。

小明忍不住插嘴道,「你說大家倆,說這些沒用的幹嗎呢?咱們又控制不了。這不是本身嚇本身嗎?」

「是啊是啊。」我隨聲附和,「反正這一趟路途遙遠,咱們有的是時間,那咱們繼續以前講故事的遊戲好了。第二個誰講呢?」

「我講我講!」小明搶着說,「我此次要講一個真正的跟程序員有關的故事。主人公但是個頂級的技術高手!」

黑客與女孩

我要講的這個故事啊,可跟個人親身經歷有關。故事的主人公,是一名身懷絕技的網絡黑客。他的黑客技術登峯造極,只要動動手指就能讓幾千個節點的網絡癱瘓!他的程序可以穿透各類防火牆,到達別人根本沒法到達的地方。

小明神祕的語氣吸引了咱們。我和小麗聚精會神地聽着。

黑客就像網絡世界的一個俠盜,他遊歷世界,劫富濟貧,專門收拾那些爲富不仁的有錢人。

有一天,黑客在海邊度假的時候,碰到了一個女孩。他深深地愛上了她。

哦,對了,我開頭忘了介紹,咱們的這位黑客主人公雖然技術高超,但表達能力欠缺,並且有嚴重的社交恐懼症。他好幾年都獨來獨往,從不與外人接觸,整日都泡在網絡上。他大概沒有勇氣向女孩當面表達他的愛意。因此,他開始用本身的方式去接觸她。

他入侵了女孩的家庭網絡,潛伏在她的我的電腦裏面,注視着她的一舉一動。令他灰心喪氣的是,女孩的生活中原來有另一個男人,那多是她的男友或者丈夫。黑客感受到自尊心受到了傷害,他順藤摸瓜,很快黑進了那個男人所在公司的網絡。

隨後的發現令他大吃一驚。那是一個作互聯網金融業務的公司,那個男人是公司的老闆。黑客發現公司的帳目存在嚴重的問題,公司非法集資,帳目混亂,彷彿在故意掩蓋着一個陰謀。他想親口去告訴那個女孩,但好幾回他又退卻了。女孩怎麼可能會相信他呢?

如今,對於咱們的黑客來講,這已經不是一個私人恩怨的問題了,這涉及到不少人的財產安全。這是一個社會正義的問題!因而,黑客利用那家公司服務器的一個「SQL注入」的漏洞,讓公司服務器集體癱瘓,再也沒法啓動,同時又將隱藏的公司帳目提取了出來,並公開發布在了網上。

分手

「知道嗎?我,就是這名黑客的工具程序中攜帶的某個字節。我還親自參與了那次的入侵行動!」

對於他的身世來歷,小明提及來一臉的自豪。

「那最後黑客跟那個女孩在一塊兒了嗎?」小麗問。

「可能在一塊兒了吧。不過這根本不重要!」此次輪到小明不耐煩了。

如今,咱們已經通過了許多中途的路由器節點。在每個節點上,路由程序都會根據20個字節的IP頭中的目的地址去查閱路由表,決定下一跳把咱們發送到哪裏。眼看着離目的地愈來愈接近了。

這時,咱們來到了一個新的路由節點。路由程序告訴咱們一個很差的消息,前面的路線要通過一個以太局域網,這個局域網的MTU (Maximum Transmission Unit,最大傳輸單元) 是1500字節。因此,咱們這2000個字節要被分紅兩部分分別發送。路由程序解釋說,在IP協議裏,這叫分片(Fragmentation),並詳細解釋了分片的過程。

具體的分片過程稍微有點複雜。首先,分片是IP層的策略,所以它不識別TCP層的封裝。也就是說,20個字節的TCP頭加上原來2000個字節的數據,這總共2020個字節,在IP層協議執行分片的時候都當作數據。前面的1480個字節分到第一個數據片,他們這一隊前面加上20個字節的IP頭,成爲1500個字節的新IP包,剛好能夠經過MTU的限制。而從第1481個字節開始,後面的540個字節,將分到第二個數據片。這個數據片一樣在前面加上20個字節的IP頭,成爲一個560字節長度的IP包。

我忽然意識到,第1481個字節位置,因爲裏面多算了20個字節的TCP頭,因此在原來的數據中其實是第1461個字節。而我不就正好排在第1461個字節位置嗎?這就是說,我將被分到第二個數據片,而小明和小麗在第一個數據片!

真是糟糕透了。

當我把這個分片的結果告訴他們的時候,我確信我看到了小麗眼中現出擔心的神色。

「那咱們還會再見面嗎?」小麗問。

「固然會了。兩個數據片分頭到達目的主機以後,還會在協議棧裏重組(Reassembly)的。」我故做輕鬆地解釋,但心中不安的感受卻愈來愈強。

「真惋惜!咱們還沒聽到你講的故事呢。」小明開玩笑地說。

「等重組以後,我立刻講給大家。」

我剛說完,分片操做就已經完成。載着小明和小麗的數據片「嗖」地一聲被髮射出去。而我仍站在原來的buffer中,向他們揮手告別。

防火牆

我在第二個數據片中,緊跟在20個字節的IP頭後面,排在數據的第1個位置。一路上,我陷入了沉思。

咱們又通過了幾個路由節點,慢慢地靠近了目的主機。

「停!」忽然有人大喊一聲。原來是目的主機上的防火牆程序,他要求咱們停下來接受檢查。

「不對啊,大家這個數據包的目的端口沒有在白名單內!」最後,防火牆程序下告終論。

「什麼!」幾乎數據片內的全部字節都喊了起來。

防火牆程序無奈地攤開雙手,「真是抱歉。但規則就是規則。大家只能被丟棄掉!」

我所在的數據片內一片騷亂。

「等一下!」排在第1個數據位置的我大喊一聲。此刻我忽然冷靜了下來,看來面前是個工做在四層的防火牆程序。我彷佛明白怎麼回事了,「請聽我說!並非目的端口不對,而是根本沒有目的端口。咱們是IP分片以後的第二個數據片,只有IP頭,沒有TCP頭。你要看的目的端口號應該在TCP頭內部,但咱們這裏根本沒有。」

防火牆程序狐疑地看了我一眼,「那你說怎麼辦?」

「若是我沒猜錯的話,咱們的第一個數據片在以前已經經過了你這裏。你能夠去查查記錄,看有沒有一個已經經過的數據包,具備跟咱們徹底相同的一個Identification字段(注:是IP頭用於分片和重組的一個字段)。若是有的話,咱們就應該也能經過。」

「好,大家等着,我去查一下。」

時間彷彿過了一個世紀之久。看來這個防火牆在處理分片狀況下的過濾算法效率並不高。

我心中那種不安的感受彷佛又逐漸升騰起來。沒錯,若是幸運的話,小明和小麗他們應該已經經過了這裏,但若是咱們倒是先他們一步到達的話......後果然是不堪設想!

還好,防火牆程序最後終於從新返回,對咱們點了點頭,「大家能夠經過了。」

重逢

咱們在目的主機的IP層成功完成了重組 (Reassembly),從新變成了2020個字節的隊伍(包括20字節的TCP頭)。

我又從新和小麗、小明他們挨在了一塊兒。一切彷彿一如從前,但又變得有點不一樣。

小麗和小明如今手拉着手,親密地偎依在一塊兒。他們見到我,微笑着同我打招呼:「你怎麼這麼慢啊!終於又見到你了!」

我胸中涌起了一股相似嫉妒的情緒。

忽然間,我徹底明白了,從頭到尾。

「我如今就給大家講第三個故事。」我頓了一頓,「很湊巧,故事的男主人公也叫蘇秦,而女主人公也叫古靈兒。」

他們吃驚地瞪大了眼睛。

第三個故事

蘇秦是計算機系畢業的,畢業後作了程序員的工做。可是,他知道本身並不擅長作這份工做,因此一直沒有信心。

他和古靈兒在畢業以前就開始戀愛了,那是一份純粹的愛情。然而,畢業以後他慢慢地發現,隨着生活而來的物質壓力卻愈來愈大,讓他喘不過氣來。他當心翼翼地維繫着這份感情,但終於她仍是離他而去了。

蘇秦今後暗下決心,他發誓必定要把失去的再從新找回來。他今後奮發圖強,真能夠說是「頭懸樑,錐刺股」了。他用了差很少兩年的時間,閱讀了大量的技術書籍,在工做中也不遺餘力,終於成長成了一名真正的技術高手。升職,加薪,一切彷佛都唾手可及。可是,這時候古靈兒早已步入了別人的婚姻殿堂。原來一切都已不可挽回。

蘇秦白天上班,晚上則搖身一變,成爲一名網絡黑客。他對古靈兒仍然沒有死心。他入侵了她的家庭網絡,又入侵了她老公的公司網絡。她老公確實是個有錢人,但卻在用人上目光短淺。多是爲了壓低成本,他們公司幾乎只招一些工資低的程序員,結果作出來的產品漏洞百出。蘇秦根據這些漏洞泄露出來的數據,發現了公司的帳目問題。

他也說不清本身到底是出於何種動機,也沒仔細想過這樣作的後果,總之他出手了。他選了一個好日子,對古靈兒他老公的公司網絡發動了致命的一擊。

結果公司倒閉了,老闆作賊心虛,居然本身跑路了。那個男人果真是個靠不住的人。蘇秦沒想到這件事給古靈兒帶來了無盡的麻煩,但她把社交軟件都刪掉了,並且搬了住處,他一時竟也找不到她。

直到有一天,他接到了古靈兒的來電。

整個故事的結尾

「因此說,大家倆講的,其實只是一個故事的兩個部分!」我大聲地向小麗和小明宣佈道。

這時,協議棧的內核程序剛剛剝掉了TCP頭,咱們又從新變成了2000個字節的數據。緊接着,一箇中斷產生,咱們被從內核態拋到了應用層,等待應用層程序的進一步處理。

他們兩個吃驚地說不出話來。看到他們的樣子,我心裏竟浮起一絲復仇的快感。我臉一紅,趕忙壓制住了這種情緒。

「你——說的都是真的?」小明驚得說話都有些結巴了。

我慢條斯理的開始分析,「知道爲何咱們三個會碰到一塊兒嗎?這並非湊巧。由於咱們三個都和蘇秦有關!」

「那你呢?你和蘇秦又是什麼關係呢?」

「我屬於蘇秦調試程序的時候使用的一份測試數據。」我指了指由全部這2000個字節組成的字節隊列,補充道,「準確地說,咱們都屬於這份測試數據。他應該正在測試大數據包的狀況下究竟會發生什麼。」

此時,應用層的程序發現了咱們的到達,正準備調用protobuf的反解子例程執行反序列化操做。

「什麼?你是說咱們幾個之因此在這兒,只是在進行一次調試?」小明和小麗表示都不太相信。

「沒錯。並且咱們立刻就會知道結果了。」我觀察了下週圍的執行環境,更加確信了我推測的正確。

他們倆面面相覷。

沒錯!就是如今了!我大聲地衝他們喊道:「知道着這意味着什麼嗎?」

「什麼?」

「這意味着正在調試的程序還不太穩定!」

一瞬間,進程拋出的異常有如原子彈爆炸。刺眼的白光照亮了支離破碎的整個內存空間。全部的東西在一點點消失,操做系統開始回收異常發生後的現場。

「到底發生了什麼?」就在咱們你們被完全地從內存中抹掉以前,小明竭嘶底裏地大喊。

「這個接收程序忘了進行「粘包」處理!」我想起了咱們出發時由3400個字節組成的完整消息,直到如今,後面仍有1400個字節沒有到達。

在一片白光之中,世界從新歸於一片寂靜。

咱們雖然已再也不存在於這片內存之中,但程序的日誌早已把這發生的一切都記錄了下來。此刻,日誌文件正靜靜地躺在世界的某個角落,等待着喜歡聽故事的你來隨時查閱。

(完)

最後的彩蛋

應該不少同窗已經猜到了,故事中這三個字節的取值並非隨意選的。它們要表達的意思你看出來了嗎?

另外,對作技術開發工做的同窗來講,文中出現的技術描述細節中,最有實用價值的實際上是應用層的「粘包」處理問題。文中解釋了它出現的緣由,你看懂了嗎?

其它精選文章

相關文章
相關標籤/搜索