zoukankan      html  css  js  c++  java
  • 网络问题

    网络编程本质

    整理自《Linux内核源代码情景分析》,以unix_socket为例

    struct socket {
        socket_state            state;          /* 初始值为SS_UNCONNECTED */
        unsigned long           flags;
        struct proto_ops        *ops;
        struct inode            *inode;         /* 取得一个inode结构是取得一个socket结构的必要条件,socket创建时会调用sock_map_fd,与文件描述符挂钩 */
        struct fasync_struct    *fasync_list;
        struct file             *file;
        struct sock             *sk;            /* 底层数据结构,非常大, sock面向底层驱动程序,socket面向进程和系统调用 */
        wait_queue_head_t       wait;
        short                   type;           /* socket类型,SOCK_STREAM, SOCK_RAW, SOCK_DGRAM */
        unsigned char           passcred;
    }
    

    sys_socket()——创建插口

    unix_socket *unix_socket_table[ UNIX_HASH_SIZE + 1 ];
    将插口sock结构挂在unix_socket_table的最后一个队列中,unix_socket_table[ UNIX_HASH_SIZE ],用杂凑值作下标(0 ~ UNIX_HASH_SIZE-1)不会访问到该队列。
    sock结构中包含有receive_queue接收队列与write_queue发送队列,队列中的元素为sk_buff,每个数据包都占一个sk_buff
    接收到一个packet时,packet头部包含目标插口地址,根据该地址杂凑值找到相应队列,并扫描队列找到匹配的sock结构

    sys_bind()——指定插口地址

    将sock结构转移到相应的队列中。
    对于常规的路径名地址,需要所创建的文件的i节点号码唯一(如果文件系统中已存在具有相同路径名的文件,则bind失败,否则新建文件,该文件在进程exit后依然存在,需要unlink()或者remove())
    对于抽象地址,需要根据抽象地址的杂凑值在unix_socket_table的相应队列里检查该地址是否已经存在
    TCP要求四元组唯一,但多数实现(包括Linux)强制实施更严格的约束:如果主机上有任何可匹配到本地端口的TCP连接,则本地端口不能被重用(即对bind()的调用)
    启用SO_REUSEADDR可解放这个限制,使得更接近TCP的需求。大多数TCP服务器应开启该选项

    sys_listen()——设定server插口

    改变当前的状态为TCP_LISTEN
    设定几个参数,如最大队列长度max_ack_backlog,
    设置credentials,具体就是进程的用户号、组号、进程号

    struct    sock *sk = sock->sk;
    ...
    sk->max_ack_backlog = backlog;
    sk->state = TCP_LISTEN;
    sk->peercred.pid = current->pid;
    sk->peercred.uid = current->uid;
    sk->peercred.gid = current->gid;
    

    sys_accept()——接受连接请求

    阻塞,在接受新连接或超时时返回
    通过sock_alloc()分配新的socket结构,但并不分配与socket配对的sock结构,这是由请求方在connect()时通过sk_buff结构传过来的

    Q:代码中未对接收到的报文,即sk_buff结构作检查,如何确定是连接请求报文?
    A:只有连接请求报文会挂入到server插口的receive_queue中,数据报文只能挂入到建立了连接的新插口的receive_queue中

    sys_connect()——请求连接

    分配一个sock结构,并将该地址通过连接请求报文发送给server方
    server方接受后记录server方传过来的地址
    标记当前状态为已连接

    0.0.0.0, 127.0.0.1, 127.0.0.2, 本机IP四者区别:

    环回的特征是数据包不经过IP层以下的协议栈,直接被拷贝到环回地址socket的缓冲区中

    首先假设本机有多个网卡:eth0 :192.168.0.1 eth1:192.168.1.1 lo: 127.0.0.1

    1. 监听0.0.0.0创建Socket,那么无论使用127.0.0.1或127.0.0.2或本机ip都可以建立tcp连接,也就是不论通过127.0.0.1或127.0.0.2或192.168.0.1、192.168.1.1都能连接成功。
    0.0.0.0建立tcp连接的时候也可以通过绑定IP_ADDR_ANY来实现。
    
    2. 但是监听127.0.0.1,创建Socket,那么用本机地址或127.0.0.2建立tcp连接不成功,反过来也是如此;也就是,监听时采用的地址为192.168.0.1,就只能用192.168.0.1进行连接。IP层会过滤掉不同的IP。
    
    3. 监听localhost? localhost是个域名,性质跟 “www.baidu.com” 差不多。不能直接绑定套接字,必须先gethostbyname转成IP才能绑定
    


    那么问题来了,环回地址必须是127.0.0.1么?

    答案:不是必须!IPv4 的环回地址是保留地址之一 127.0.0.1。尽管只使用 127.0.0.1 这一个地址,但地址 127.0.0.0 到 127.255.255.255 均予以保留。此地址块中的任何地址都将环回到本地主机中。此地址块中的任何地址都绝不会出现在任何网络中。
    可以做一个简单的测试,用ssh root@127.2.3.4 然后登录看看是不是还是本机?不用修改ip,随意一个此范围内长度ip地址均可以ping通,并且通过ssh登录到本机。
    如下图,lo地址为127.0.0.1,netmask为255.0.0.0,所有与netmask掩码运算的结果 = inet & netmask都被视为环回地址。

    环回的特征是数据包不经过IP层以下的协议栈,直接被拷贝到环回地址socket的缓冲区中

    ip输出函数先检查地址是不是环回地址
    1.如果是环回地址 直接交给环回驱动程序处理 返回ip输入函数
    2.如果不是环回地址 检查是不是广播或者多播地址。如果是广播地址或者组播地址数据报复制一份传给环回接口。然后在送到以太网上,因为广播和多播包含主机本身
    3.如果不是广播或者多播地址 再检查是不是本机地址 如果是本机地址 则交给环回驱动程序处理,环回驱动程序返回给ip输入函数
    从上面可以看出 ping127.0.0.1 数据包是不经过网卡的, ping本机则是需要经过网卡的

    所以,
    1.ping 127.0.0.1是检查TCP/IP协议栈是否正常。
    2.ping 本地IP 是检查你网卡、配置是否正常。

    网络相关配置文件

    #eth0网卡的IP相关参数
    /etc/sysconfig/network-scripts/ifcfg-eth0
    #主机名,修改主机名后需要重启电脑
    /etc/sysconfig/network
    #DNS IP
    /etc/resolv.conf
    #私有IP对应的主机名
    /etc/hosts
    
    
    #TCP/IP上的各种协议对应的端口
    /etc/services
    #定义IP数据包协议的相关数据
    /etc/protocols
    

    网络配置命令

    #重启网络的所有参数,会主动读取所有的网络配置文件,命令同service network restart
    /etc/init.d/network restart
    #查看网络接口
    ifconfig
    #启动(关闭)eth0接口
    ifup eth0    (ifdown eth0)
    #查看路由表
    #找到最佳匹配后,从接口发送到下一个网关。
    #掩码为0.0.0.0表示默认路由
    #网关为0.0.0.0表示未设置网关,通常可直接交付
    #网关与接口不需要在同一网段, 如PPPoE
    #ref: https://www.zhihu.com/question/54007586
    route -n
     
    

    网络排错

    https://www.jianshu.com/p/36eaea218c0c

  • 相关阅读:
    Leetcode No.121
    Leetcode No.97 ***
    (描述需要改进) Leetcode No.71 **
    (描述需要改进)Leetcode No.68 **
    Leetcode No.72 ***
    【笔记】存储位置/修改表/字符集.【3(完结创建表)】
    redis 事件驱动模型解析
    redis 官网文档学习笔记 简单翻译
    redis 官网文档 sentinel 简单翻译 && 简单总结QA
    redis 学习笔记 总
  • 原文地址:https://www.cnblogs.com/dirge/p/11496614.html
Copyright © 2011-2022 走看看