Linux系统编程——网络:从 OSI 到 UDP 通信实践

目录

[一、OSI 与 TCP/IP 模型](#一、OSI 与 TCP/IP 模型)

[1.OSI 模型(7 层)](#1.OSI 模型(7 层))

[2.TCP/IP 模型(4 层)](#2.TCP/IP 模型(4 层))

[二、Linux 网络配置:命令与文件](#二、Linux 网络配置:命令与文件)

[1. 永久配置 IP](#1. 永久配置 IP)

[2.临时配置 IP](#2.临时配置 IP)

3.网络调试命令

[三、网络编程核心概念:Socket 与字节序](#三、网络编程核心概念:Socket 与字节序)

1.Socket(套接字)

2.字节序

[四、UDP 通信:无连接的传输协议](#四、UDP 通信:无连接的传输协议)

[1.UDP 通信流程](#1.UDP 通信流程)

2.核心函数解析

[2.1 创建套接字:socket()](#2.1 创建套接字:socket())

[2.2 绑定地址:bind()](#2.2 绑定地址:bind())

[2.3 发送数据:sendto()](#2.3 发送数据:sendto())

[2.4 接收数据:recvfrom()](#2.4 接收数据:recvfrom())

[五、UDP 通信代码示例](#五、UDP 通信代码示例)

1.服务端代码(server.c)

2.客户端代码(client.c)

3.编译运行

4.运行结果


一、OSI 与 TCP/IP 模型

1.OSI 模型(7 层)

网络通信是分层协作的,先理清最基础的分层逻辑。

OSI 模型从下到上是物理层→数据链路层→网络层→传输层→会话层→表示层→应用层

层级 描述 常用协议
7 应用层 为应用程序或用户请求提供各种请求服务。 HTTP、FTP、STMP、POP3、TELENT、NNTP、IMAP4、FINGER
6 表示层 数据编码、格式转换、数据加密。 LPP、NBSSP
5 会话层 创建、管理和维护会话。 SSL、TLS、DAP、LDAP
4 传输层 数据通信。 TCP、UDP
3 网络层 IP选址及路由选择。 IP、ICMP、RIP、IGMP、OSPF
2 数据链路层 提供介质访问和链路管理。 以太网、网卡、交换机、PPTP、L2TP、ARP、ATMP
1 物理层 管理通信设备和网络媒体之间的互联互通。 物理线路、光纤、中继器、集线器、双绞线、网络适配器
  • **物理层:**负责把主机中的数据转换成电信号,再通过网络介质(双绞线、光纤、无线信道等)来传输。该层描述了通信设备的机械、电气、功能等特性。
  • **数据链路层:**负责物理相邻(通过网络介质相连)的主机间的数据传输,主要作用包括物理地址寻址、数据帧封装、差错控制等。该层可分为逻辑链路控制子层(LLC)和介质访问控制子层(MAC)。
  • **网络层:**负责数据传输的路由选择和网际互连。
  • **传输层:**管理网络通信两端的数据传输,提供可靠或不可靠的传输服务。
  • **会话层:**负责信息传输的组织和协调,管理进程会话过程。
  • **表示层:**为不同主机间的通信提供统一的数据表示形式。
  • **应用层:**为网络用户提供各种服务,例如电子邮件、文件传输等。

2.TCP/IP 模型(4 层)

TCP/IP 模型是 OSI 模型的简化版,对应关系是:

  • 网络接口层 → OSI 的数据链路 + 物理层,负责监视数据在主机和网络之间的交换。
  • 网络层 → 对应 OSI 网络层,主要解决主机到主机的通信问题。
  • 传输层 → 对应 OSI 传输层,为应用层实体提供端到端的通信功能,保证了数据包的顺序传送及数据的完整性。
  • 应用层→ OSI 的应用 + 会话 + 表示层,为用户提供所需要的各种服务。

二**、**Linux 网络配置:命令与文件

在 Linux 里配置网络,常用这几个操作:

1. 永久配置 IP

编辑网络配置文件:

bash 复制代码
sudo vim /etc/network/interfaces

在文件里设置 IP(手动 / 自动),修改后重启网络服务生效:

bash 复制代码
sudo /etc/init.d/networking restart

2.临时配置 IP

用 ifconfig 临时设 IP(重启失效):

bash 复制代码
ifconfig ens33 192.168.0.13/24 up

3.网络调试命令

  • 查本机 IP:ifconfig
  • 测试联网:ping www.baidu.com
  • 查网络连接:netstat -anp

三、网络编程核心概念:Socket 与字节序

1.Socket(套接字)

Socket 是 "网络文件描述符",打开网络设备后获得它,就能通过它收发数据。

  • 标识一个网络连接:IP+端口(IP 定位主机,端口定位主机上的应用程序)
  • 端口范围:1~65535

2.字节序

网络设备和主机的字节存储顺序可能不同:

  • 网络字节序:大端存储(高位字节存在低地址)
  • 主机字节序:小端存储(Intel/AMD/ARM CPU 都是小端)

四、UDP 通信:无连接的传输协议

UDP 是 "用户数据报协议",特点是无连接、低延迟、不可靠(发太快会丢包),适合音视频这类对实时性要求高的场景。

1.UDP 通信流程

服务端步骤:

  1. socket():创建套接字
  2. bind():绑定 IP 和端口
  3. recvfrom():接收客户端数据
  4. sendto():给客户端回发数据
  5. close():关闭套接字

客户端步骤:

  1. socket():创建套接字
  2. sendto():给服务端发数据
  3. recvfrom():接收服务端回复
  4. close():关闭套接字

函数调用关系:

2.核心函数解析

2.1 创建套接字:socket()

cpp 复制代码
int socket(int domain, int type, int protocol);
  • domain:地址族(PF_INET= 网络程序)
  • type:套接字类型(SOCK_DGRAM=UDP)
  • protocol:协议(填 0 自动适配)
  • 返回值:成功返回套接字 fd,失败返回 - 1

2.2 绑定地址:bind()

cpp 复制代码
int bind(int sockfd, struct sockaddr *my_addr, socklen_t addrlen);
  • sockfd:要绑定的套接字 fd

  • my_addr:网络地址结构(IPv4 用 struct sockaddr_in)

    cpp 复制代码
    struct sockaddr_in
    {
        u_short sin_family; // 地址族(AF_INET)
        u_short sin_port;   // 端口(要转网络字节序)
        struct in_addr sin_addr; // IP地址
    };
  • 返回值:成功 0,失败 - 1

2.3 发送数据:sendto()

cpp 复制代码
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen);
  • dest_addr:目标主机的地址结构
  • 返回值:成功返回发送的字节数,失败 - 1

2.4 接收数据:recvfrom()

cpp 复制代码
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen);
  • src_addr:可选,存发送方的地址
  • 返回值:成功返回接收的字节数,失败 - 1

五、UDP 通信代码示例

1.服务端代码(server.c)

cpp 复制代码
#include <arpa/inet.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <time.h>
#include <unistd.h>

typedef struct sockaddr *(SA);

int main(int argc, char **argv)
{
  // 创建UDP套接字
  int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
  if (-1 == sockfd)
  {
    perror("socket");
    return 1;
  }

  // 初始化服务端/客户端地址结构
  struct sockaddr_in ser, cli;
  bzero(&ser, sizeof(ser));
  bzero(&cli, sizeof(cli));
  ser.sin_family = AF_INET;
  ser.sin_port = htons(50000); // (Host to Network Short)将主机字节序转为网络字节序(大端序)
  ser.sin_addr.s_addr = inet_addr("192.168.0.159");
  
  // 绑定套接字到指定IP和端口
  int ret = bind(sockfd, (SA)&ser, sizeof(ser));
  if (-1 == ret)
  {
    perror("bind");
    return 1;
  }

  // 循环接收客户端数据并回复
  socklen_t len = sizeof(cli);
  while (1)
  {
    char buf[512] = {0};
    recvfrom(sockfd, buf, sizeof(buf), 0, (SA)&cli, &len);
    printf("recv:%s\n", buf);
    time_t tm;
    time(&tm);
    struct tm *info = localtime(&tm);
    sprintf(buf, "%s %d:%d:%d", buf, info->tm_hour, info->tm_min, info->tm_sec);
    sendto(sockfd, buf, strlen(buf) + 1, 0, (SA)&cli, len);
  }
  close(sockfd);

  return 0;
}

2.客户端代码(client.c)

cpp 复制代码
#include <arpa/inet.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <time.h>
#include <unistd.h>

typedef struct sockaddr *(SA);

int main(int argc, char **argv)
{
  // 创建UDP套接字
  int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
  if (-1 == sockfd)
  {
    perror("sockfd");
    return 1;
  }
  // 初始化服务端地址结构
  struct sockaddr_in ser;
  bzero(&ser, sizeof(ser));
  ser.sin_family = AF_INET;
  ser.sin_port = htons(50000);
  ser.sin_addr.s_addr = inet_addr("192.168.0.159");

  // 循环向服务端发送数据并接收回复
  int i = 10;
  while (i--)
  {
    char buf[512] = "hello";
    sendto(sockfd, buf, strlen(buf), 0, (SA)&ser, sizeof(ser));
    bzero(buf, sizeof(buf));
    recvfrom(sockfd, buf, sizeof(buf), 0, NULL, NULL);
    printf("recv:%s\n", buf);
    sleep(1);
  }
  close(sockfd);

  return 0;
}

3.编译运行

bash 复制代码
# 编译服务端
gcc udp_server.c -o server
# 编译客户端
gcc udp_client.c -o client

# 启动服务端
./server
# 另开终端启动客户端
./client

4.运行结果

相关推荐
爱潜水的小L6 小时前
自学嵌入式day37,网络编程
开发语言·网络·php
ベadvance courageouslyミ6 小时前
网络编程基础(一)
网络·udp·ip
饼饼饼6 小时前
从 0 到 1:前端 CI/CD 实战 ( 第一篇: 云服务器环境搭建)
运维·前端·自动化运维
小二·6 小时前
AI工程化实战《五》:私有化部署全栈指南——Qwen/Qwen-VL 本地化落地与生产级运维(万字深度长文)
运维·人工智能
叹了口丶气6 小时前
CentOS 7编译Python3.10时,SystemError: <built-in function compile> returned NULL
linux·运维·centos
Lueeee.6 小时前
FFMPEG输出模块初始化
linux·ffmpeg
lusasky6 小时前
实现智能体调用海量api
网络
知识分享小能手6 小时前
Ubuntu入门学习教程,从入门到精通,Ubuntu 22.04的远程登录(6)
linux·学习·ubuntu
TimberWill6 小时前
MinIO整合SpringBoot实现获取文件夹目录结构及文件内容
java·linux·springboot
2401_865854887 小时前
服务器的windows和Linux系统有什么区别
linux·运维·服务器