第一讲:网络基础概念——TCP/IP协议栈、OSI七层模型、IP地址与端口号、字节序

各位同学,咱们今天聊点基础的。别小看基础,我见过太多人写了好几年网络程序,结果被一个字节序问题坑得死去活来。嗯,咱们就从最底层开始,把地基打牢。

1.1 为什么需要分层?——从寄快递说起

你想想看,如果你要给远方的朋友寄一本书,你会怎么做?

  • 你只需要写好地址、贴上邮票、扔进邮筒。
  • 至于这封信是坐火车还是飞机,经过哪些中转站,你根本不用管。
  • 邮局系统把这些细节全包了。

网络通信也是一样的道理。如果让每个程序员都去处理网线信号、路由转发、数据重传……那代码就没法写了。所以,网络协议被分成了若干层,每层只管自己的事。

核心思想:分层是为了解耦。每一层只关心上下两层,不越级,不越权。

1.2 OSI七层模型——教科书里的理想国

OSI(开放系统互连)模型是国际标准化组织搞出来的理论框架。说实话,它太完美了,完美到实际没人完全照着它实现。但作为学习工具,它非常有用。

层级 名称 一句话说明 常见协议/设备
7 应用层 你直接用的软件 HTTP、FTP、SMTP
6 表示层 数据格式转换、加密 SSL/TLS、JPEG
5 会话层 建立/管理/终止会话 NetBIOS、RPC
4 传输层 端到端可靠传输 TCP、UDP
3 网络层 寻址和路由 IP、ICMP、路由器
2 数据链路层 帧传输、差错控制 以太网、交换机
1 物理层 比特流、电信号 网线、集线器

我个人习惯把七层模型记成一句话:“物链网传会表应”。从下往上背,考试面试都管用。

1.3 TCP/IP协议栈——实际干活的那套

OSI是理论,TCP/IP才是现实。互联网上跑的,全是TCP/IP这套。它只有四层,比OSI精简得多。

应用层(HTTP, FTP, DNS, SSH) ≈ OSI 5~7层:应用层 + 表示层 + 会话层 传输层(TCP, UDP) ≈ OSI 第4层:端到端通信 网络层(IP, ICMP, ARP) ≈ OSI 第3层:寻址与路由 网络接口层(以太网, WiFi) ≈ OSI 1~2层:物理层 + 数据链路层

说白了,TCP/IP就是把OSI的上三层合并成一层(应用层),下面两层合并成一层(网络接口层)。中间两层——传输层和网络层,是核心中的核心。我写网络程序十几年,90%的时间都在跟这两层打交道。

1.4 IP地址与端口号——门牌号与房间号

IP地址就像你家的门牌号。比如 192.168.1.100,它标识了互联网上的一台主机。但一台主机上可能跑着很多服务:Web服务、SSH服务、数据库服务……你怎么区分它们?

这时候就需要端口号了。端口号就像门牌号后面的房间号。IP地址把你带到这栋楼,端口号把你带到具体的房间。

关键公式: 套接字(Socket)= IP地址 + 端口号

端口号的范围是 0~65535,其中:

  • 0~1023: 知名端口,需要root权限才能用。比如HTTP用80,HTTPS用443,SSH用22。
  • 1024~49151: 注册端口,给应用程序用。
  • 49152~65535: 动态/私有端口,客户端临时使用。

避坑指南: 我曾经在项目里写死了一个端口号 8080,结果部署到客户环境发现跟另一个服务冲突了。后来我改成从配置文件读取,再也没出过问题。记住:端口号不要硬编码

1.5 字节序——大端与小端

这是新手最容易踩的坑,没有之一。我当年第一次写跨平台网络程序时,就被这个坑得加班到凌晨三点。

什么叫字节序?说白了,就是一个多字节整数在内存里怎么存放。

  • 大端(Big-Endian): 高位字节存低地址。就像我们写数字一样,从左到右是高位到低位。
  • 小端(Little-Endian): 低位字节存低地址。x86架构的CPU就是小端。

举个例子,整数 0x12345678 在内存中的存放方式:

地址 大端 小端
0x00 0x12 0x78
0x01 0x34 0x56
0x02 0x56 0x34
0x03 0x78 0x12

网络协议规定:所有多字节整数在网络上传输时,必须使用大端字节序。这就是所谓的“网络字节序”。

C语言里提供了四个函数来做转换:

#include <arpa/inet.h>

uint32_t htonl(uint32_t hostlong);   // 主机→网络(32位)
uint16_t htons(uint16_t hostshort);  // 主机→网络(16位)
uint32_t ntohl(uint32_t netlong);    // 网络→主机(32位)
uint16_t ntohs(uint16_t netshort);   // 网络→主机(16位)

警告: 千万不要自己写字节序转换!不要用指针强转!不要用memcpy!我见过有人这么干,结果在ARM和x86上跑出完全不同的结果。老老实实用系统提供的函数,它们经过了无数项目的验证。

1.6 实战小技巧——用代码验证字节序

想知道你的机器是大端还是小端?写几行代码就知道了:

#include <stdio.h>

int main() {
    unsigned int x = 0x12345678;
    unsigned char *p = (unsigned char *)&x;
    
    if (p[0] == 0x78) {
        printf("小端机器\n");
    } else if (p[0] == 0x12) {
        printf("大端机器\n");
    } else {
        printf("未知字节序\n");
    }
    return 0;
}

我建议你把这段代码在你的开发板上跑一下。相信我,亲眼看到内存布局,比背十遍概念都管用。

1.7 本章小结

咱们这一讲聊了四个核心概念:

  • OSI七层模型——理论框架,帮你理解网络的全貌。
  • TCP/IP协议栈——实际干活的东西,重点掌握传输层和网络层。
  • IP地址与端口号——门牌号+房间号,定位一个网络服务的唯一方式。
  • 字节序——大端小端,网络传输必须用大端,用系统函数做转换。

这些概念会贯穿整个课程。后面每一讲,我们都会回到这些基础上来。你想想看,如果连IP地址和端口号都搞不清楚,怎么写socket程序?如果字节序搞反了,发出去的数据对方根本看不懂。

嗯,今天就到这儿。把这些基础嚼烂了,后面咱们就开始写真正的网络代码了。


公众号:蓝海资料掘金营,微信deep3321