TCP/IP协议栈在Linux内核中的运行时序分析

本文深入探讨了Linux内核中TCP/IP协议栈的运行时序,从Linux操作系统架构到协议栈的分层设计,详细阐述了从应用层到物理层的数据传输过程,包括TCP与UDP的发送与接收流程,以及IP层和数据链路层的处理。通过配套视频和代码分析,帮助读者理解Linux内核网络协议栈的工作原理。
摘要由CSDN通过智能技术生成

本文主要是讲解TCP/IP协议栈在Linux内核中的运行时序,文章较长,里面有配套的视频讲解,建议收藏观看。

1 Linux概述

  1.1 Linux操作系统架构简介

Linux操作系统总体上由Linux内核和GNU系统构成,具体来讲由4个主要部分构成,即Linux内核、Shell、文件系统和应用程序。内核、Shell和文件系统构成了操作系统的基本结构,使得用户可以运行程序、管理文件并使用系统。

内核是操作系统的核心,具有很多最基本功能,如虚拟内存、多任务、共享库、需求加载、可执行程序和TCP/IP网络功能。我们所调研的工作,就是在Linux内核层面进行分析。

Linux内核协议栈相关视频讲解:Linux内核网络协议栈详解

底层原理到徒手实现 TCP/IP网络协议栈视频讲解:底层原理到徒手实现 TCP/IP网络协议栈

C/C++ Linux服务器开发高级架构学习视频点击:C/C++Linux服务器开发/Linux后台架构师-学习视频

1.2 协议栈简介

  OSI(Open System Interconnect),即开放式系统互联。 一般都叫OSI参考模型,是ISO(国际标准化组织)组织在1985年研究的网络互连模型。

ISO为了更好的使网络应用更为普及,推出了OSI参考模型。其含义就是推荐所有公司使用这个规范来控制网络。这样所有公司都有相同的规范,就能互联了。

OSI定义了网络互连的七层框架(物理层、数据链路层、网络层、传输层、会话层、表示层、应用层),即ISO开放互连系统参考模型。如下图。

每一层实现各自的功能和协议,并完成与相邻层的接口通信。OSI的服务定义详细说明了各层所提供的服务。某一层的服务就是该层及其下各层的一种能力,它通过接口提供给更高一层。各层所提供的服务与这些服务是怎么实现的无关。

  osi七层模型已经成为了理论上的标准,但真正运用于实践中的是TCP/IP五层模型。

  TCP/IP五层协议和osi的七层协议对应关系如下:

在每一层实现的协议也各不同,即每一层的服务也不同.下图列出了每层主要的协议。

  1.3 Linux内核协议栈

  Linux的协议栈其实是源于BSD的协议栈,它向上以及向下的接口以及协议栈本身的软件分层组织的非常好。
  Linux的协议栈基于分层的设计思想,总共分为四层,从下往上依次是:物理层,链路层,网络层,应用层。
  物理层主要提供各种连接的物理设备,如各种网卡,串口卡等;链路层主要指的是提供对物理层进行访问的各种接口卡的驱动程序,如网卡驱动等;网路层的作用是负责将网络数据包传输到正确的位置,最重要的网络层协议当然就是IP协议了,其实网络层还有其他的协议如ICMP,ARP,RARP等,只不过不像IP那样被多数人所熟悉;传输层的作用主要是提供端到端,说白一点就是提供应用程序之间的通信,传输层最著名的协议非TCP与UDP协议末属了;应用层,顾名思义,当然就是由应用程序提供的,用来对传输数据进行语义解释的“人机界面”层了,比如HTTP,SMTP,FTP等等,其实应用层还不是人们最终所看到的那一层,最上面的一层应该是“解释层”,负责将数据以各种不同的表项形式最终呈献到人们眼前。
  Linux网络核心架构Linux的网络架构从上往下可以分为三层,分别是:
  用户空间的应用层。
  内核空间的网络协议栈层。
  物理硬件层。
  其中最重要最核心的当然是内核空间的协议栈层了。
  Linux网络协议栈结构Linux的整个网络协议栈都构建与Linux Kernel中,整个栈也是严格按照分层的思想来设计的,整个栈共分为五层,分别是 :
  1,系统调用接口层,实质是一个面向用户空间应用程序的接口调用库,向用户空间应用程序提供使用网络服务的接口。
  2,协议无关的接口层,就是SOCKET层,这一层的目的是屏蔽底层的不同协议(更准确的来说主要是TCP与UDP,当然还包括RAW IP, SCTP等),以便与系统调用层之间的接口可以简单,统一。简单的说,不管我们应用层使用什么协议,都要通过系统调用接口来建立一个SOCKET,这个SOCKET其实是一个巨大的sock结构,它和下面一层的网络协议层联系起来,屏蔽了不同的网络协议的不同,只吧数据部分呈献给应用层(通过系统调用接口来呈献)。
  3,网络协议实现层,毫无疑问,这是整个协议栈的核心。这一层主要实现各种网络协议,最主要的当然是IP,ICMP,ARP,RARP,TCP,UDP等。这一层包含了很多设计的技巧与算法,相当的不错。
  4,与具体设备无关的驱动接口层,这一层的目的主要是为了统一不同的接口卡的驱动程序与网络协议层的接口,它将各种不同的驱动程序的功能统一抽象为几个特殊的动作,如open,close,init等,这一层可以屏蔽底层不同的驱动程序。
  5,驱动程序层,这一层的目的就很简单了,就是建立与硬件的接口层。
  可以看到,Linux网络协议栈是一个严格分层的结构,其中的每一层都执行相对独立的功能,结构非常清晰。
  其中的两个“无关”层的设计非常棒,通过这两个“无关”层,其协议栈可以非常轻松的进行扩展。在我们自己的软件设计中,可以吸收这种设计方法。

2 代码简介

本文采用的测试代码是一个非常简单的基于socket的客户端服务器程序,打开服务端并运行,再开一终端运行客户端,两者建立连接并可以发送hello\hi的信息,server端代码如下:

#include <stdio.h>     /* perror */
#include <stdlib.h>    /* exit    */
#include <sys/types.h> /* WNOHANG */
#include <sys/wait.h>  /* waitpid */
#include <string.h>    /* memset */
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <errno.h>
#include <arpa/inet.h>
#include <netdb.h> /* gethostbyname */

#define true        1
#define false       0

#define MYPORT      3490    /* 监听的端口 */
#define BACKLOG     10      /* listen的请求接收队列长度 */
#define BUF_SIZE    1024

int main()
{
    int sockfd;
    if ((sockfd = socket(PF_INET, SOCK_DGRAM, 0)) == -1)
    {
        perror("socket");
        exit(1);
    }

    struct sockaddr_in sa;         /* 自身的地址信息 */
    sa.sin_family = AF_INET;
    sa.sin_port = htons(MYPORT);     /* 网络字节顺序 */
    sa.sin_addr.s_addr = INADDR_ANY; /* 自动填本机IP */
    memset(&(sa.sin_zero), 0, 8);    /* 其余部分置0 */

    if (bind(sockfd, (struct sockaddr *)&sa, sizeof(sa)) == -1)
    {
        perror("bind");

        exit(1);
    }

    struct sockaddr_in their_addr; /* 连接对方的地址信息 */
    unsigned int sin_size = 0;
    char buf[BUF_SIZE];
    int ret_size = recvfrom(sockfd, buf, BUF_SIZE, 0, (struct sockaddr *)&their_addr, &sin_size);
    if(ret_size == -1)
    {
        perror("recvfrom");
        exit(1);
    }
    buf[ret_size] = '\0';
    printf("recvfrom:%s", buf); 
}

client端代码如下:

#include <stdio.h>     /* perror */
#include <stdlib.h>    /* exit    */
#include <sys/types.h> /* WNOHANG */
#include <sys/wait.h>  /* waitpid */
#include <string.h>    /* memset */
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <errno.h>
#include <arpa/inet.h>
#include <netdb.h> /* gethostbyname */

#define true 1
#define false 0

#define PORT 3490       /* Server的端口 */
#define MAXDATASIZE 100 /* 一次可以读的最大字节数 */

int main(int argc, char *argv[])
{
    int sockfd, numbytes;
    char buf[MAXDATASIZE];
    struct hostent *he;            /* 主机信息 */
    struct sockaddr_in server_addr; /* 对方地址信息 */
    if (argc != 2)
    {
        fprintf(stderr, "usage: client hostname\n");
        exit(1);
    }

    /* get the host info */
    if ((he = gethostbyname(argv[1])) == NULL)
    {
        /* 注意:获取DNS信息时,显示出错需要用herror而不是perror */
        /* herror 在新的版本中会出现警告,已经建议不要使用了 */
        perror("gethostbyname");
        exit(1);
    }

    if ((sockfd = socket(PF_INET, SOCK_DGRAM, 0)) == -1)
    {
        perror("socket");
        exit(1);
    }

    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(PORT); /* short, NBO */
    server_addr.sin_addr = *((struct in_addr *)he->h_addr_list[0]);
    memset(&(server_addr.sin_zero), 0, 8); /* 其余部分设成0 */
 
    if ((numbytes = sendto(sockfd, 
                           "Hello, world!\n", 14, 0, 
                           (struct sockaddr *)&server_addr, 
                           sizeof(server_addr))) == -1)
    {
        perror("sendto");
        exit(1);
    }

    close(sockfd);

    return true;
}

简单来说,主要流程如下图所示:

专攻Linux内核,从事内核开发的朋友,需要学习资料可以看看下面的视频是否需要学习,点击:Linux内核学习资料 获取

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值