Linux网络编程API函数初步剖析

今天我们来分析一下前几篇博文中提到的网络编程中几个核心的API,探究一下当我们调用每个API时,内核中具体做了哪些准备和初始化工作。

1、socket(family,type,protocol)

当我们在开发网络应用程序时,使用该系统调用来创建一个套接字。该API所做的工作如下所示:

该系统调用主要完成两个任务:“创建套接字”和“为套接字绑定文件句柄”。

socket{}<include/linux/net.h>结构定义如下:

struct socket {
socket_state state; //socket状态
unsigned long flags; //标识,如SOCK_ASYNC_NOSAPCE
const struct proto_ops *ops; //协议特定的socket操作集
struct fasync_struct *fasync_list; //异步唤醒队列
struct file *file; //指向文件的指针
struct sock *sk; //指向下一层中的sock结构
wait_queue_head_t wait; //等待在这个socket上的任务列表
short type; //数据包的类型
};

在创建socket套接字时,就是要完成ops、file和sk等这些成员的初始化。

1). 创建套接字:sock_create()

根据family参数值在全局数组struct net_proto_family net_families[]里找到我们所指定的地址簇。不同类型的地址簇都有一个struct net_proto_family{}类型的对象,例如我们常见的IPv4的inet_family_ops,IPv6的inet6_family_ops,X25协议的ax25_family_ops等。在内核是初始化时,这些模块会在自己的初始化函数内部调用sock_register()接口将各自的地址簇对象注册到net_families[]数组里。

我们分析的焦点集中在IPv4协议簇,即inet_family_ops对象上。重点是inet_create函数,该函数的主要任务就是创建一个socket套接字,并对其中相关结构体成员进行必要的初始化。至于它创建套接字时的依据和原理等到我们讲协议栈时大家就明白了,这里主要是让大家对其流程执行流程有个感性的把握。

sock_alloc()函数中我们创建一个struct socket{}类型的对象,假如叫做A,将socket()系统调用的第二参数type字段赋值给A->type。

在inet_create()函数中,我们根据type的值,在全局数组struct inet_protosw inetsw[]里找到我们对应的协议转换开关。而inetsw[]数组是在inet_init()函数里被初始化的:


       其中inetsw_array[]是一个比较重要的数据结构,定义在af_inet.c文件中:

根据type的值,就可以确定struct socket{}->ops,到底是inet_stream_ops、inet_dgram_ops或者inet_sockraw_ops。然后,对应地,就以tcp_prot、udp_prot或raw_prot为输入参数,实例化一个struct sock{}对象sk=sk_alloc()。紧接着建立socket{}和sock{}的关联,最后将socket()系统调用的第三个参数protocol付给sock{}对象中的属性sk_protocol。

看不懂别着急,我说过,这里只是给大家梳理整体流程,等到我们讲了协议栈章节,然后再回头看本篇,就感觉这些东西就太小儿科了。

2). 为套接字绑定文件句柄:sock_map_fd()

我们都知道网络套接字也是一种系统IO,所以不可避免的要与文件系统打交道。每个套接字都对应一个已打开的文件标识符,所以在套接字初始化完成后,就要将其和本地一个唯一的文件标识符关联起来,即建立socket{}和file{}之间的关联关系。

2、bind (sockfd, sockaddr, addrlen)

该系统调用在内核中的执行过程如下:

重点是socket->ops->bind()回调接口。我们现在已经知道了,针对IPv4而言,这里的ops无非就是inet_stream_opsinet_dgram_opsinet_sockraw_ops对象。碰巧的是,这三个对象中的bind函数指针均指向inet_bind()函数。只有原始套接字的情况,这里会去调用raw_prot对象的bind回调函数,即raw_bind()。

3、listen(sockfd, backlog)

这里我们可以看到面向无连接的套接字和原始套接字是不用listen的,只有流式套接字才有效。

4、connect(sockfd, sockaddr, addrlen)

从这幅图中我们确实看到,connect()系统调用不但可以面向连接的套接字,也可用于无连接及原始套接字。

5、accept(sockfd, sockaddr, addrlen)

同样地,我们看到只有面向连接的流式套接字调用accept()才有意义。最终调用的是tcp_prot对象的accept成员函数。

本篇主要进一步分析了网络编程中常见的几个API函数内部的调用流程,一方面可以使大家对这些API的有了更深的认识,不是仅仅停留在形而上的层面;另一方面为后面分析协议栈的实现原理,奠定坚实的基础。

未完,待续…

UNIX网络编程——揭开网络编程常见API的面纱【上】的更多相关文章

  1. UNIX网络编程——揭开网络编程常见API的面纱【下】

    Linux网络编程数据收发的API流程分析        只要把数据在协议栈中的流动线路和脉络弄清楚了,关于协议栈的实现部分,理解起来就轻松多了.在网络编程章节的数据接收过程中,我们主要介绍过read ...

  2. 揭开网络编程常见API的面纱【上】

    Linux网络编程API函数初步剖析 今天我们来分析一下前几篇博文中提到的网络编程中几个核心的API,探究一下当我们调用每个API时,内核中具体做了哪些准备和初始化工作. 1.socket(famil ...

  3. UNIX环境高级编程——TCP/IP网络编程 常用网络信息检索函数

    UNIX环境高级编程——TCP/IP网络编程   常用网络信息检索函数 gethostname()   getppername()   getsockname()   gethostbyname() ...

  4. --系统编程-网络-tcp客户端服务器编程模型、socket、htons、inet_ntop等各API详解、使用telnet测试基本服务器功能

    PART1 基础知识 1. 字节序 网络字节序是大端字节序(低地址存放更高位的字节), 所以,对于字节序为小端的机器需要收发网络数据的场景,要对这些数据进行字节序转换. 字节序转换函数,常用的有四个: ...

  5. Unix环境高级编程(十七)网络IPC套接字

    通过网络套接字可以使得不同计算机上运行的进程相互通信. 1.创建套接字 #include <sys/socket.h> Int socket( int domain, int type, ...

  6. (50)LINUX应用编程和网络编程之五 Linux信号(进程间通信)

                                                                                 信号实现进程间的通信 3.5.1.什么是信号 ...

  7. (46)LINUX应用编程和网络编程之一Linux应用编程框架

    3.1.1.应用编程框架介绍 3.1.1.1.什么是应用编程 (1)整个嵌入式linux核心课程包括5个点,按照学习顺序依次是:裸机.C高级.uboot和系统移植.linux应用编程和网络编程.驱动. ...

  8. 黑马程序员:Java编程_网络编程

    =========== ASP.Net+Android+IOS开发..Net培训.期待与您交流!=========== 网络编程就是两个(或多个)设备(例如计算机)之间的数据传输,更具体的说,网络编程 ...

  9. (47)LINUX应用编程和网络编程之二Linux文件属性

    Linux下的文件系统为树形结构,入口为/ 树形结构下的文件目录: 无论哪个版本的Linux系统,都有这些目录,这些目录应该是标准的.各个Linux发行版本会存在一些小小的差异,但总体来说,还是大体差 ...

随机推荐

  1. github学习(三)

    Git学习(二) 分支学习: 创建新分支dev:git branch dev 切换到dev分支:git checkout dev 可以简写为一句话:git checkout -b dev 可以用命令g ...

  2. [HNOI 2016]序列

    Description 题库链接 给你一个长度为 \(n\) 的序列 \(A\) ,给出 \(q\) 组询问.每次询问 \([l,r]\) ,求该区间内所有的子序列中最小值的和. \(1\leq n, ...

  3. [HAOI2007]分割矩阵

    题目描述 将一个a*b的数字矩阵进行如下分割:将原矩阵沿某一条直线分割成两个矩阵,再将生成的两个矩阵继续如此分割(当然也可以只分割其中的一个),这样分割了(n-1)次后,原矩阵被分割成了n个矩阵.(每 ...

  4. Codeforces 700E. Cool Slogans

    Description 给定一个串 \(S\),求一个序列 \(a_i\),满足 \(a_i\) 是原串的子串,且 \(a_i\) 在 \(a_{i-1}\) 中至少出现两次,求这个序列的最大的长度 ...

  5. UVA4731:Cellular Network

    根据排序不等式可知,逆序和最小(就是两个向量坐标一个递增一个递减,那么乘起来就最小) 所以排一下序,然后做一下线性dp即可 #include<cstdio> #include<cst ...

  6. 51 nod 1495 中国好区间

    1495 中国好区间 基准时间限制:0.7 秒 空间限制:131072 KB 分值: 80 难度:5级算法题   阿尔法在玩一个游戏,阿尔法给出了一个长度为n的序列,他认为,一段好的区间,它的长度是& ...

  7. ●BZOJ 1767 [Ceoi2009]harbingers

    题链: http://www.lydsy.com/JudgeOnline/problem.php?id=1767 题解: 斜率优化DP,单调栈,二分 定义 DP[i] 表示从 i 节点出发,到达根所花 ...

  8. SQL_SERVER_2008升级SQL_SERVER_2008_R2的方法

    SQL 2008升级到SQL 2008 R2. 说到为什么要升级是因为,从另一台机器上备份了一个数据库,到我的机器上还原的时候提示"948错误,意思就是不能把高版本的数据库附加到低版本上,所 ...

  9. 认识Json解析json生成json

    .markdown-body hr::after,.markdown-body::after { clear: both } .loopLine,.messageLine0 { } .markdown ...

  10. Docker 基础 : Dockerfile

    Dockerfile 是一个文本格式的配置文件,用户可以使用 Dockerfile 快速创建自定义的镜像.我们会先介绍 Dockerfile 的基本结构及其支持的众多指令,并具体讲解通过执行指令来编写 ...