第12章 网络 (1)

目录

12.1 互联的计算机

12.2 ISO/OSI 和TCP/IP 参考模型

12.3 通过套接字通信

12.3.1 创建套接字

12.3.2 使用套接字

12.3.3 UDP套接字

12.4 网络实现的分层模型


本专栏文章将有70篇左右,欢迎+关注,查看后续文章。

网络相关的头文件数目巨大,所以单独放在 include/net,而不是 include/linux。

12.1 互联的计算机

分层处理各种传输问题。

12.2 ISO/OSI 和TCP/IP 参考模型

ISO:国际标准化组织。

        该组织定义了7层模型,即 OSI 模型。

        OSI:开放系统互联。

实际使用上图左侧的4层模型。

物理层:

        网卡和线缆等物理设备。(PHY层)

数据链路层:

        网卡驱动程序中实现。(MAC层)

网络层和传输层:

        内核协议栈中实现。

12.3 通过套接字通信

网络不能通过 open("/dev/net")来通信。

创建socket,将生成一个文件描述符,存在有对应inode。

        然后像操作文件一样,进行read / write读写。

12.3.1 创建套接字

创建套接字时,须指定地址族(即Domain),通信类型(即Type)。

int   socket(int   domain,    int   type,    int   protocol);

        domain 值有:

                AF_UNIX、AF_LOCAL         本地通信使用。

                AF_INET、AF_INET6           IP协议。

                AF_NETLINK                        netlink 报文。

                AF_PACKET                         RAW 报文。

        type 值有:

                SOCK_STREAM                   数据流,如 TCP。

                SOCK_DGRAM                    数据报,如 UDP。

                SOCK_RAW                         当domain 为AF_PACKET时使用。

        protocol:

                若为0,则内核选择默认协议。

AF_PACKET:

        作用:

                1. 允许应用层直接操作数据链路层的报文。如以太网帧。

                2. 可绕过网络层和传输层,进行直接收发,避免被过滤掉。

        使用场景:

                1. 高性能的数据包处理。

                2. 网络监控,抓包( tcpdump )。

                3. 应用层自定义特殊网络协议。

socket 系统调用实现:

        sock_create:

                1. sock   =   sock_alloc( );

                        sock->type   =   type;

                2. struct net_proto_family   *pf    =    net_families [ family ];

                        pf->create(net,   sock,   protocol,   kern);

                        // 即调用 inet_create 或 inet6_create 或 netlink_create 等。

bind 用于将socke 与本地IP/端口绑定。

int   bind(int   sockfd,    struct   sockaddr   *addr,    socklen_t   addrlen);

struct   sockaddr {                 // 可容纳任意地址族的通用地址结构。

        sa_family_t         sa_family;

        char                    sa_data [14];

}

struct   sockaddr_in {         // IPv4专用的地址结构体,可用struct sockaddr强转得到。

        sa_family_t            sin_family;

        __be16                  sin_port;         // 大端。

        struct in_addr         sin_addr;

        unsigned char         __pad[  ];

};

        struct   in_addr  {

                in_addr_t    s_addr;         // 具体IPv4的地址值。

        };

网络字节序是大端。

长度为1个字节的数据不区分大小端。

如何判断CPU是大端?小端?

int   main( )

{

        unsigned int   i   =   0x12345678;

        if ( *(( unsigned char * ) &i )   ==   0x78)

                printf("Little Endian \n");

        else (* (( unsigned char * ) &i )   ==   0x12)

                printf("Big Endian \n");

}

小端:低位字节在低地址,高位字节在高地址。

大端:高位字节在低地址,低位字节在高地址。

12.3.2 使用套接字

如何将一个套接字关联到eth0等网络接口?

        int   bind(int   sockfd,    const   struct sockaddr   *addr,    socklen_t   addrlen);

                addr:即本地接口的IP。

组播可以跨网段通信。

广播分为:

        物理广播:

                局域网所有设备。目的MAC为:FF-FF-FF-FF-FF-FF。

        逻辑广播:

                特定子网的所有设备。如 192.168.1.255。

编程举例: 将本地接口加入 224.0.0.1多播组。

struct   ip_mreq   {

        struct   in_addr      imr_multiaddr;         // 请求加入或退出的组播IP(目)

        struct   in_addr      imr_interface;         // 发送接口的 IP 地址(源)

};

struct   ip_mreq    mreq;

mreq.imr_multiaddr.s_addr    =    inet_addr("224.0.0.1");

mreq.imr_interface.s_addr    =    htonl(INADDR_ANY);

setsockopt(sockfd,    IPPROTO_IP,    IP_ADD_MEMBERSHIP,    &mreq,    sizeof(mreq));

recvfrom(sockfd,    buffer,    sizeof(buffer),    0,    NULL,    NULL);

TCP客户端与服务器编程

1. TCP客户端

sockfd    =    socket(AF_INET,    SOCKET_STREAM,    0);

connect(sockfd,    (struct sockaddr *)server,    sizeof(*server));

        //server中填充了服务器IP/PORT

write(sockfd,  ,  );

read(sockfd,  , );

注意:

        客户端无需使用 bind 函数。即不将 socket和 IP/port 绑定。因为:

                通常客户端主动发送报文。

                发送时,内核根据目的IP,查找路由表可确定出接口的IP,作为报文源IP。

                        而源端口号由内核动态分配一个可用。

2. TCP服务器

sockfd    =    socket(AF_INET,    SOCKET_STREAM,    0);

bind(sockfd,    (struct sockaddr *)server,    sizeof(*server));

        //socket与服务器IP、port 绑定。

listen(sockfd,    SOMAXCONN);

        // SOMAXCONN,即为创建的等待队列长度。

clientfd    =    accept(sockfd,    (struct sockaddr *)client,    &client_size);

read(clientfd,    buf,    1000);

write(clientfd,    buf,    1000);

netstat:查看TCP/IP连接状态。

        netstat -atnp:tcp连接状态。

                -a         display all sockets

                -n         不解析域名。

                -p         display PID/Program name for sockets

                -t         TCP

                -u         UDP

netstat     -antp         查看tcp连接状态。

netstat     -anup         查看udp连接状态。

12.3.3 UDP套接字

12.4 网络实现的分层模型

建议阅读 fs_initcall(inet_init);

1. 网络层:

struct   packet_type    ip_packet_type;

struct   packet_type    ipv6_packet_type;

struct   packet_type     ip_packet_type    =    {

        .type     =    cpu_to_be16(ETH_P_IP), // IP协议

        .func     =    ip_rcv,

};

2. 传输层

struct   proto    tcp_prot;

struct   proto    udp_prot;

struct   inet_protosw    inetsw_array[  ]     =

{

        {

                .type           =      SOCK_STREAM,

                .protocol     =      IPPROTO_TCP,

                .prot            =      &tcp_prot,                       //函参为:struct   sock    *sk;

                .ops             =      &inet_stream_ops,         //函参为:struct   socket    *sock;

        },

        {

                .type            =     SOCK_DGRAM,

                .protocol      =     IPPROTO_UDP,

                .prot             =     &udp_prot,

                .ops              =     &inet_dgram_ops,

        },

        ...

}

const struct   proto_ops    inet_dgram_ops    =    {

        .family            =    PF_INET,

        .bind               =    inet_bind,

        .connect         =    inet_dgram_connect,

        .accept           =    sock_no_accept,

        .listen             =    sock_no_listen,

        .setsockopt     =    sock_common_setsockopt,

        .getsockopt     =    sock_common_getsockopt,

        .sendmsg        =     inet_sendmsg,

        .recvmsg         =     inet_recvmsg,

};

struct  proto    udp_prot    =    {

        .name             =    "UDP",

        .connect         =     ip4_datagram_connect,

        .destroy          =     udp_destroy_sock,

        .setsockopt     =     udp_setsockopt,

        .getsockopt     =    udp_getsockopt,

        .sendmsg        =     udp_sendmsg,

        .recvmsg         =     udp_recvmsg,

};

struct   proto_ops    inet_dgram_ops 中的

        .connect    =    inet_dgram_connect,

int   inet_dgram_connect(struct socket   *sock,     struct sockaddr   *uaddr,

  int   addr_len,     int   flags)

{

        struct   sock    *sk     =     sock->sk;

        return     sk->sk_prot->connect(sk,     uaddr,     addr_len);

                //即struct proto    udp_prot中的 ip4_datagram_connect。

}

  • 18
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

山下小童

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值