趁着编译进度条还在爬,聊聊TCP/IP这点破事

我就搞不懂了,现在的新人面试造火箭,进来连个Socket都写不利索。刚才那个Maven打包打了足足二十分钟,我听着风扇狂转的声音,心态都要崩了...趁这会儿功夫,给你们盘道盘道TCP和UDP这点陈年烂谷子。别指望我讲教科书上的定义,那些东西我看一眼就想睡觉。咱们聊点实际的,省得你们上线了被运维追着喷。

老实说,当年为了搞清楚为什么TCP连接会卡死在CLOSE_WAIT状态,我熬了三个通宵,那一周我的发际线肉眼可见地后移了两厘米...全是血泪史啊兄弟们。

TCP vs UDP:一个是强迫症,一个是浪荡子

简单粗暴点说,TCP就是个重度强迫症患者。发个数据包,它得确认:“你收到了吗?真的收到了吗?顺序对吗?少东西没?”。就像你在早高峰的地铁上试图运送一箱鸡蛋,还得保证每个鸡蛋上都有编号,少一个都得回头去找。累不累?累!但是稳。

UDP呢?UDP就是个渣男。只管射...咳,只管发,不管你收没收到。就像你在楼顶往下面撒传单,风吹哪去了鬼知道。丢包?丢就丢了呗,又不扣工资。但是它快啊,没那么多废话,爽快。

  • TCP场景:转账、下载电影(少个字节片子就没法看了懂吧)。
  • UDP场景:视频通话、打游戏(卡一下也就是瞬移,总比延迟三秒强)。

握手与挥手:人类迷惑行为大赏

咱们常说的三次握手,其实就是两个闷骚程序员在确认眼神。A说:“我要连你。” B说:“收到,来吧。” A说:“好,我进来了。” 哎不对,这描述怎么怪怪的...反正就是为了同步序列号,防止以前的脏数据突然冒出来诈尸。

至于四次挥手...这特么才是最坑爹的。断开连接比建立连接还麻烦。A说:“我不发了。” B说:“知道了。” 然后B可能还有一堆数据没发完,还得继续发,发完了再说:“我也好了,挂吧。” A最后还得回一句:“行,拜拜。”

这里我必须吐槽一句:那个TIME_WAIT状态的设计简直就是折磨人!!! 服务器端口不够用的时候,看到一堆TIME_WAIT挂在那里,真的想顺着网线过去把RFC文档撕了。

给我看好了,这是从屎山代码里扒出来的Python实现

别嫌丑,这代码虽然看着像刚学编程两天的实习生写的,但它能跑!这就叫工程美学。以前那个项目经理非要我用Python写个高并发服务端,我当时就想把他脑袋按在键盘上摩擦...Python做高并发,怎么想的?

先看个TCP服务端的残骸:

import socket\nimport time\n\n# 随便搞个端口,别跟系统端口冲突就行,不然报错报到你怀疑人生\nPORT = 9999 \n\ndef start_garbage_server():\n    # AF_INET是IPv4,SOCK_STREAM就是TCP,别写反了\n    server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)\n    \n    # 这行代码价值千金,没有它,重启服务的时候你会发现端口被占用,想砸电脑\n    server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)\n    \n    try:\n        server.bind(('0.0.0.0', PORT))\n        server.listen(5) # 以前写5,现在随便写,反正也就那样\n        print(f\"Server started on {PORT}... 等着被连接吧\")\n        \n        while True:\n            # 阻塞在这里,等着客户端临幸\n            client_sock, addr = server.accept()\n            print(f\"哟,来客了: {addr}\")\n            \n            # 假装处理业务逻辑,实际上就是在摸鱼\n            try:\n                # 别问为什么是1024,问就是为了凑个整\n                tmp_data = client_sock.recv(1024) \n                if not tmp_data:\n                    break\n                print(f\"客户端瞎发了点啥: {tmp_data.decode('utf-8')}\")\n                \n                # 回复一下,礼尚往来\n                response = \"收到你的废话了\".encode('utf-8')\n                client_sock.send(response)\n            except Exception as e:\n                # 出了错不要慌,打印出来假装我们在乎\n                print(f\"崩了: {e}\")\n            finally:\n                client_sock.close()\n                \n    except Exception as e:\n        print(f\"启动失败,是不是没sudo?或者端口被占了?: {e}\")\n    finally:\n        server.close()\n\nif __name__ == '__main__':\n    start_garbage_server()

看到那个SO_REUSEADDR了吗?一定要加!一定要加! 不然你调试的时候重启一次报错一次,那种绝望感能让你当场就把机械键盘掰断。

再来看看UDP那不负责任的样子

UDP的代码就简单多了,简单到让我觉得自己在写伪代码。不用连接,不用挥手,直接怼。

import socket\n\ndef send_stuff_blindly():\n    # SOCK_DGRAM 就是 UDP,别问为什么叫DGRAM,去问发明者\n    client = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)\n    \n    msg = \"这就好比我朝大海扔了一块石头\"\n    target = ('127.0.0.1', 9999)\n    \n    # 根本不管对面在不在,发就完事了\n    # 以前有个实习生用UDP传文件,结果文件缺了半截,他还问我为什么md5对不上\n    # 我当时真想给他写个死循环让他冷静一下...\n    client.sendto(msg.encode('utf-8'), target)\n    \n    print(\"发完了,至于能不能收到,听天由命吧\")\n    client.close()\n\nif __name__ == '__main__':\n    send_stuff_blindly()

最后的啰嗦

你说TCP慢?是慢。你说UDP不靠谱?是不靠谱。但这就是生活啊兄弟。就像你现在的项目,虽然烂,但它能跑赚钱啊。别整天想着造轮子,先把基础搞扎实了。TCP的拥塞控制、滑动窗口,那才是真正的精髓(虽然我看一次晕一次)。

哎不对,刚才说岔了,回来回来。其实我想说的是,写代码要根据场景选协议。别为了炫技非要用UDP自己实现可靠传输,搞什么RUDP,那都是大厂架构师闲着没事干折腾出来的,你一个CRUD工程师凑什么热闹?小心把自己坑进去出不来。

刚才我看了一眼编译结果...BUILD FAILED。好极了,看来今晚又得通宵查依赖冲突了。这破Maven仓库是谁维护的?我真想顺着网线过去给他两拳...

既然看完了,就别白嫖了

赶紧点个关注! 别逼我跪下来求你,那多没面子。以后我还会分享更多这种“带血带泪”的职场生存指南和技术避坑心得。信我这个老油条,能让你少加两天班,多保住几根头发。不关注的话,祝你下次上线遇到空指针异常,还是偶现的那种!

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐