资讯动态

Linux Socket编程入门:从零跑通第一个TCP通信实例

发布时间:2026/10/25 19:23:21 来源:尧图企业网站定制
简介这份文档面向希望系统掌握 Linux 网络编程的开发者与学习者围绕 Socket 这一核心接口梳理从进程通信原理到 TCP 连接管理的完整知识链条。内容涵盖网络中进程如何唯一标识与通信、Socket 的“一切皆文件”设计思想以及 socket()、bind()、listen()、connect()、accept()、read()/write()、close() 等基本函数的用法并详解 TCP 三次握手建立连接与四次挥手释放连接的过程最后配有一个可动手实践的服务端与客户端示例帮助读者把理论落到代码。资源包为 1 个 docx 文档约 77KB篇幅紧凑、结构清晰适合作为入门梳理或复习提纲。目前已有 241 人学习适合具备一定 C 语言与操作系统基础、想打通网络编程关键概念的读者参考。1. 从一通“打不通的电话”说起Linux Socket 编程到底是什么你有没有遇到过这种情况两台电脑明明连着同一个网络想让它们互相传一句话结果折腾半天不是连不上就是收不到很多新手第一次接触网络编程时脑子里全是问号——浏览器能打开网页、聊天软件能发消息这些进程之间到底是怎么“说上话”的答案就是 Socket。你可以把它想成一部电话IP 地址是你家的地址端口号是门牌号协议是你们约定的语言TCP 还是 UDP。Linux Socket 编程说白了就是教你在代码里“装一部电话、拨号、说话、挂断”。它是网络编程的地基网页服务器、聊天工具、文件传输底层几乎都靠它。这份资源就是带你从零把这部“电话”装起来适合刚学完 C/C 基础、想搞懂网络通信到底怎么跑起来的新手。2. 为什么值得学先搞懂 Socket 在网络里站哪个位置2.1 网络进程通信靠的是“三元组”而不是 PID本地两个程序通信操作系统给每个进程一个 PID靠它就能找到对方。但到了网络上PID 只在各自机器里有效换台机器就完全对不上号。TCP/IP 协议族给出的办法是用 IP 地址标识主机用“协议 端口”标识主机上的某个应用进程三者合起来就是一个三元组能唯一锁定网络中的一次通信。这就是 Socket 存在的理由。它把这套寻址逻辑封装成一套函数接口你不需要自己去拼 IP 包头只要调用几个函数就能让两个进程隔着网络交换数据。入门阶段你只要记住一句话Socket 是应用程序和 TCP/IP 协议栈之间的那扇门数据从这扇门出去也从这扇门进来。2.2 “一切皆文件”Socket 为什么长得像文件操作Unix/Linux 有个很朴素的设计哲学——一切皆文件。普通文件的操作是“打开 open → 读写 read/write → 关闭 close”Socket 把这套模式原样搬了过来socket()相当于打开read()/write()或send()/recv()相当于读写close()相当于关闭。区别只在于普通文件背后是磁盘Socket 背后是网卡和协议栈。理解这一点你后面看那些函数就不会觉得陌生。我第一次学的时候就是把 Socket 描述符当成一个“文件句柄”来理解的它就是个整数你拿着这个整数去读写内核帮你把数据送到网络另一端。这个类比不严谨但对入门足够用。2.3 先分清 TCP 和 UDP两种“说话方式”选哪个创建 Socket 时有个参数叫类型最常用的是SOCK_STREAM和SOCK_DGRAM分别对应 TCP 和 UDP。TCP 像打电话先拨通三次握手确认对方在然后按顺序说话说完挂断丢包会自动重传。UDP 像寄明信片写好地址扔出去不保证对方收到也不保证顺序但胜在快、开销小。新手入门建议从 TCP 开始因为它的连接建立和释放过程清晰出错时也更容易定位。等你把 TCP 的“装电话—拨号—通话—挂断”跑通了再回头看 UDP会发现它简单得有点“随意”。这份资源的例子用的就是 TCP跟着走一遍你对“连接”两个字的理解会具体很多。3. 动手前的准备六个核心函数就是全部家当3.1 服务端和客户端的函数分工一张表看清Socket 编程的函数不多但新手容易搞混“谁该调哪个”。下面这张表按角色分好你写代码时对照着看就行函数服务端客户端作用一句话socket()要调要调创建 Socket 描述符bind()要调通常不调绑定 IP 和端口listen()要调不调把 Socket 设为监听状态connect()不调要调主动发起连接accept()要调不调接受连接返回新描述符send()/recv()要调要调收发数据close()要调要调关闭连接服务端的顺序是socket → bind → listen → accept → recv/send → close客户端是socket → connect → send/recv → close。这个顺序不是随便定的每一步都在为下一步做准备少一步就会在运行时报错。3.2 socket() 和 bind()装电话和报门牌号socket()的三个参数分别是协议域、类型和协议。入门阶段你基本固定写AF_INETIPv4、SOCK_STREAMTCP、0让系统选默认协议。它返回一个整数描述符后面所有操作都靠它。bind()是把地址“报”给这个 Socket。服务端必须绑定一个明确的 IP 和端口客户端才知道往哪连。这里有个新手必踩的点端口号要用htons()转成网络字节序IP 用htonl(INADDR_ANY)表示监听本机所有网卡。原文里特意强调过主机字节序和网络字节序不一致曾引发过线上问题别偷懒省掉转换。// 服务端创建并绑定 Socket 的核心片段 int sockfd socket(AF_INET, SOCK_STREAM, 0); // 创建 TCP Socket struct sockaddr_in servaddr; memset(servaddr, 0, sizeof(servaddr)); servaddr.sin_family AF_INET; // IPv4 servaddr.sin_addr.s_addr htonl(INADDR_ANY); // 监听所有本机地址 servaddr.sin_port htons(6666); // 端口转网络字节序 bind(sockfd, (struct sockaddr*)servaddr, sizeof(servaddr));这段代码里memset先把结构体清零避免残留垃圾值htons和htonl负责字节序转换bind失败会返回 -1实际写的时候要判断返回值并打印errno不然出错你只能干瞪眼。3.3 listen()、connect()、accept()拨号、接听和转接listen()把主动 Socket 变成被动监听状态第二个参数是等待队列长度入门写 10 就够。connect()是客户端发起连接参数是服务端地址。accept()是服务端从队列里取出一个连接返回一个全新的描述符——注意这个新描述符才是用来和这个客户端通信的原来的监听描述符继续留着接下一个电话。这里有个容易绕晕的地方监听 Socket 和已连接 Socket 是两个东西。一个服务器通常只有一个监听 Socket但每接受一个客户端就多一个已连接 Socket。原文用“电话总机”和“分机”来类比很贴切你按这个理解就不会混。3.4 send()/recv() 和 close()说话与挂断连接建立后双方用send()发数据、recv()收数据。recv()返回 0 表示对方关闭了连接返回小于 0 表示出错大于 0 是实际收到的字节数。新手常犯的错是收到数据后不判断返回值直接当字符串用结果缓冲区里混着旧数据。close()关闭描述符但要注意它只是把引用计数减一真正触发 TCP 断开是在计数归零时。所以服务端处理完一个客户端记得把accept返回的那个描述符关掉否则连接会越积越多。4. 第一个能跑的例子服务端监听 6666客户端发消息4.1 把代码拆成四个文件先跑通再理解这份资源给的例子结构很清晰mysocket.h声明一个封装类mysocket.cpp实现服务端和客户端的创建与收发server.cpp和client.cpp分别是两个入口。你新建一个目录把这四个文件放进去先别急着改原样编译跑一遍看到效果再回头读代码。编译命令如下两个可执行文件分别生成g server.cpp mysocket.cpp -o server g client.cpp mysocket.cpp -o client编译时如果报找不到头文件检查一下mysocket.h是否和 cpp 文件在同一目录。-o后面是输出文件名你可以改成自己喜欢的名字但后面运行时要对上。4.2 服务端先跑它会在 accept 那里“等电话”先执行./server终端会打印“waiting for clients request”然后停住。这不是卡死是accept()在阻塞等待客户端连接。服务端的流程是创建 Socket、绑定 6666 端口、监听、进入accept循环。收到连接后它用recv循环读取客户端发来的消息并打印。// server.cpp 入口逻辑就三行 mysocket server; if (server.createserver(AF_INET, SOCK_STREAM, 0, 6666)) { server.recvmsg(); // 内部 accept 后循环 recv }createserver里依次做了socket、bind、listen任何一步失败都会打印错误并返回 false。这种“封装成类”的写法对新手很友好你不需要在 main 里堆一长串函数调用先看入口再看实现层次清楚。4.3 客户端再跑改掉那个 IP否则连的是别人客户端代码里有一行需要你手动改inet_pton(AF_INET, 192.168.1.18, ...)。这个 IP 是示例里写死的服务端地址你在自己机器上跑要改成127.0.0.1本机回环否则它会去连局域网里那台并不存在的机器然后报 connect 错误。// client.cpp 里连接服务端的关键行 inet_pton(AF_INET, 127.0.0.1, servaddr.sin_addr); // 本机测试改成回环地址 connect(m_isocket, (struct sockaddr*)servaddr, sizeof(servaddr));改完重新编译客户端先确保服务端还在运行再执行./client。客户端会进入sedmsg循环不断发送“msg 0.”“msg 1.”这样的消息服务端那边会逐条打印出来。看到两边都有输出你的第一个 Socket 程序就算跑通了。4.4 看到结果之后回头读这三处代码跑通只是第一步真正学到东西的是回头读代码。重点看三处一是createserver里bind之前的memset和字节序转换这是最容易漏的二是recvmsg里accept返回新描述符后recv用的是新描述符而不是监听描述符三是客户端sedmsg里send失败后continue而不是退出这种容错写法在实际项目里很常见。你还可以做个小实验把服务端listen的第二个参数改成 1然后同时开三个客户端观察连接排队现象。这种“改一个参数看结果”的玩法比死记函数原型有效得多。5. 新手避坑指南这五个坑我替你踩过了5.1 现象bind 报“Address already in use”原因上一次运行的服务端没正常退出端口还被占用着或者你用了TIME_WAIT状态下的端口。解决先ps找到残留进程杀掉或者在bind之前给 Socket 设置SO_REUSEADDR选项让系统允许复用处于等待状态的端口。入门阶段最省事的办法是换个端口号比如 6667。5.2 现象客户端 connect 一直失败或超时原因九成是 IP 写错了。示例里的192.168.1.18是别人机器的地址你本机测试必须用127.0.0.1。另外检查服务端是否真的在运行、端口是否一致。解决先用ping确认地址可达再用netstat看服务端端口有没有在监听两步就能定位。5.3 现象服务端收到了乱码或者空字符串原因recv收到的数据不保证以\0结尾你直接printf(%s)就会读到缓冲区里的残留内容。原文代码里在recv后手动补了buff[n] \0这一步不能省。解决每次recv后按返回长度截断并且memset清空缓冲区再用。5.4 现象程序跑一会儿就卡住不动原因accept、recv、connect默认都是阻塞的没有数据时就会一直等。新手容易在单线程里先recv再send双方都在等对方说话就死锁了。解决入门阶段先保证收发顺序明确客户端先发、服务端后收等熟悉了再学非阻塞和多路复用。5.5 现象编译报错说某个函数未声明原因头文件没包含全。Socket 相关函数分散在sys/socket.h、netinet/in.h、arpa/inet.h里少一个就报错。解决按原文mysocket.cpp顶部的 include 列表对照补齐别凭记忆写。6. 跑通之后往哪走三条继续深入的路第一个例子跑通说明你已经跨过了“敢动手”这道坎。接下来怎么走我给你三条具体的路按难度排好你挑一条先走就行。第一条是补基础。把 TCP 三次握手和四次挥手对应到代码里的哪一行搞清楚connect触发 SYNaccept在第三次握手后返回close触发 FIN。你可以用tcpdump抓一次本机回环的包亲眼看看那几个标志位比看十遍文字描述都管用。这一步花半天后面所有网络问题你都有底气排查。第二条是改例子。把现在的“客户端狂发、服务端只收”改成“一问一答”客户端发一句服务端回一句客户端收到再发下一句。这个改动会逼你处理收发顺序和缓冲区边界是理解“面向连接”最直接的方式。改完再试着让服务端支持两个客户端轮流说话你会自然碰到阻塞的问题那时候再去查select或epoll动机就足了。第三条是换场景。把 TCP 换成 UDP用sendto/recvfrom重写一遍感受一下“不需要连接”是什么体验。然后对比两者在丢包、顺序、开销上的差别你就能理解为什么视频通话用 UDP、文件传输用 TCP。这个对比做完面试里问“TCP 和 UDP 区别”你就不用背了。下面这张表可以当你接下来两周的路线参考阶段做什么检验标准第 1 周抓包看三次握手对照代码能说出 connect 和 accept 各在第几次握手返回第 2 周改成一问一答再加一个客户端两个客户端能交替通信不卡死第 3 周用 UDP 重写并对比能说清什么场景该选哪种我自己的血泪经验是每次改完代码先别急着加新功能把当前版本完整跑三遍确认稳定了再往下走。网络编程的坑很多是“改一处、崩一片”不给自己留后悔药调试时间会翻倍。从那以后我每次动 Socket 代码都强制先跑一遍最小例子确认环境没问题再动业务逻辑。希望这份入门路径能帮到你先把第一个 6666 端口跑通剩下的路自然就展开了。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑