Linux网络


*************基本概念***************
【1】计算机与网络发展的7个阶段
    1.    批处理(20世纪50年代)
        是指实现将用户每一个数据装入卡带或者磁带。并有计算机按照必定的顺序读取,
        是用户索要执行的这些程序和数据可以一并批量获得处理的方式。
    2.    分时系统(20世纪60年代)
        是指多个终端(包含鼠标、键盘、显示器等输入输出设备组成,最初还包括打
        印机)与一台计算机链接,容许多个用户同时使用一台计算机的系统。
        特性:多路性、独占性、交互性和及时性。
    3.计算机之间的通讯(20世纪70年代)
    4.    计算机网络的产生(20世纪80年代)
    5.    互联网的普及(20世纪90年代)
    6.    以互联网技术为中心的时代(2000年)
    7.    从“单纯创建链接”到“安全创建链接”(2010年)
【2】网络体系结构即指网络的层次结构和每层所使用协议的集合
【3】
    1. 协议
        一组控制数据通讯的规则。
        三要素:语法(包括数据格式、编码及信号电平等)、
                语义(包括用于协议和差错处理的控制信息)、
                时序(包括速度匹配和排序)。
    2. 标准
        一致赞成的规则。
        分类:
        事实上的标准:实际状况或者习惯
        合法标准:法律或者规章制度
    3. 标准化组织
        缓慢发展:
            ISO:国际标准化组织
            ITU-T:国际电联-电信标准部
            ANSI:美国国家标准化局
            IEEE:电气电子工程师协会(主要是以太网、局域网方面的)
            EIA:电子工业协会(物理传输标准、光钎传输)
        快速发展:
            论坛:帧中继论坛、ATM论坛
            
        管理机构:FCC 联邦通讯委员会

        Internet标准:RFC
        
【4】OSI开放系统互联模型
    OSI模型是一个理想化的模型,还没有有完整的实现

    应用层    应用程序:FTP、E-mail、Telnet
    表示层    数据格式定义、数据转换/加密
    会话层    创建通讯进程的逻辑名字与物理名字之间的联系
    传输层    差错处理/恢复,流量控制,提供可靠的数据传输
    网络层    数据分组、路由选择
    数据链路层  数据组成可发送、接收的帧
    物理层    传输物理信号、接口、信号形式、速率
    
【5】7层通讯
    (1)应用层:指定特定应用的协议(好比发送和接受文件的软件按钮,发送者输入“早上好”并附上收件人,按下发送按钮,接受者收到信息会将其存储在硬盘或者非易失存储器(数据不会由于断电而丢失的一种存储设备)上,这些都是在应用层上的)
    (2)表示层:设备固有数据格式和网络标准数据格式的转换(接受者和发送者若是使用的邮件客户端不同,那么就会出现问题,如何实现用户之间的通讯,那么就须要在表示层来起做用,使得在不一样的客户端上拥有相同的网络格式)
    (3)会话层:通讯管理,负责创建或者断开通讯链接(发送者一次性发送5份邮件,那么接受者如何接受,是一次性接受全部的文件而后断开链接仍是没接受一次就断开,而后在此进行,发送者同理)
    (4)传输层:管理两个节点(互联的网络中断)之间的数据传输。负责可靠传输(确保数据被可靠地传送到目标地址)(确保发送者和接受者之间的通讯,会话层负责决定创建链接和断开链接的时机,而传输层进行实际的创建和断开处理)
    (5)网络层:地址管理与路由选择,做用:在网络相互链接的环境中,将数据从发送端主机发送到接受端主机
    (6)数据链路层:互连设备之间传送和识别数据帧
    (7)物理层:以“0”、“1”表明的电压的高低、灯光的闪灭。界定链接器和网络的规格。
【6】TCP/IP协议:传输控制/网际协议(Transfer Control Protocol/Internet Protocol) 又称做网络通信协议
    TCP/IP协议是Internet事实上的工业标准。
    传输控制/网际协议(Transfer Control Protocol/Internet Protocol) 又称做网络通信协议

    应用层  TFTP,HTTP,SNMP,FTP,SMTP,DNS,Telnet

    传输层    TCP,UDP

    网络层    IP,ICMP,RIP,OSPF,BGP,IGMP

    物理层    SLIP,CSLIP,PPP,ARP,RARP,MTU  ISO2110,IEEE802.1,EEE802.2

    TCP(Transport Control Protocol)传输控制协议
    UDP(User Datagram Protocol)用户数据报协议
    IP(Internetworking Protocol)网间协议
    HTTP(Hypertext Transfer Protocol) 超文本传输协议
    SMTP(Simple Mail Transfer Protocol)简单邮件传输协议
    
【7】UDP和TCP
    共同点:同为传输层协议
    不一样点:
        TCP:有链接,可靠
        UDP:无链接,不保证可靠
    
    TCP:(全双工)
        TCP(即传输控制协议):是一种面向链接的传输层协议,
        它能提供高可靠性通讯(即数据无误、数据无丢失、数据
        无失序、数据无重复到达的通讯)
        适用状况:
            适合于对传输质量要求较高,以及传输大量数据的通讯。
            在须要可靠数据传输的场合,一般使用TCP协议
            MSN/QQ等即时通信软件的用户登陆帐户管理相关的功能一般采用TCP协议
            
            TCP“三次握手”
            
    UDP:(全双工)
        UDP(User Datagram Protocol)用户数据报协议,是不可靠
        的无链接的协议。在数据发送前,由于不须要进行链接,因此
        能够进行高效率的数据传输。
        适用状况:
            发送小尺寸数据(如对DNS服务器进行IP地址查询时)
            在接收到数据,给出应答较困难的网络中使用UDP。(如:无线网络)
            适合于广播/组播式通讯中。
            MSN/QQ/Skype等即时通信软件的点对点文本通信以及音视频通信一般采用UDP协议
            流媒体、VOD、VoIP、IPTV等网络多媒体服务中一般采用UDP方式进行实时数据传输
【8】socket
    是一个编程接口
    是一种特殊的文件描述符 (everything in Unix is a file)
    并不只限于TCP/IP协议
    面向链接 (Transmission Control Protocol - TCP/IP)、
    无链接 (User Datagram Protocol -UDP 和 Inter-network Packet Exchange - IPX)
    
    在OSI模型中,主要位于会话层和传输层之间

    类型:
        流式套接字(SOCK_STREAM)
            提供了一个面向链接、可靠的数据传输服务,数据无差错、无重复的发送且按发送顺序接收。内设置流量控制,避免数据流淹没慢的接收方。数据被看做是字节流,无长度限制。
        --------> TCP协议
        数据报套接字(SOCK_DGRAM)
            提供无链接服务。数据包以独立数据包的形式被发送,不提供无差错保证,数据可能丢失或重复,顺序发送,可能乱序接收。
        --------> UDP协议
        原始套接字(SOCK_RAW)
            能够对较低层次协议如IP、ICMP直接访问。    
【9】IP地址
    IP地址是Internet中主机的惟一标识
    Internet中的主机要与别的机器通讯必须具备一个IP地址
    IP地址为32位(IPv4)或者128位(IPv6)
    每一个数据包都必须携带目的IP地址和源IP地址,路由器依靠此信息为数据包选择路由

    ipv4表示形式:点分十进制形式,如202.38.64.10,最后都会转换为一个32位的无符号整数。

    IP地址分类(基于ipv4地址前八位来区分)
    A类  0000 0000 - 0111 1111     0.x.x.x - 127.x.x.x
    B类  1000 0000 - 1011 1111     128.x.x.x - 191.x.x.x
    C类  1100 0000 - 1101 1111  192.x.x.x - 223.x.x.x
    D类  1110 0000 - 1110 1111     224.x.x.x - 239.x.x.x   表示组播地址
    E类  1111 0000 - 1111 1111  240.x.x.x - 255.x.x.x   属于保留测试

    127.x.x.x 表示主机地址
    
    子网掩码:表示连接的主机最大数
    A类 255.0.0.0
    B类 255.255.0.0
    C类 255.255.255.0
    
    192.168.2.x
        192.168.2.1 表示网关
        192.168.2.255 表示该网段下的广播地址
    
    #include <arpa/inet.h>

    将点分十进制ip地址转化为网络字节序的整型数据
    in_addr_t inet_addr(const char *cp);
    
    将网络字节序的整型数据转化为点分十进制ip地址
    char *inet_ntoa(struct in_addr in);

    例子:
        inet_addr("192.168.2.189");
【10】端口号(vi /etc/services 查看端口号)
    为了区分一台主机接收到的数据包应该转交给哪一个进程来进行处理,使用端口号来区别
    众所周知端口:1~1023(1~255之间为众所周知端口,256~1023端口一般由UNIX系统占用)
    已登记端口:1024~49151
    动态或私有端口:49152~65535

    通常使用:8888 9999 10000 10001
【11】字节序    
    不一样类型CPU的主机中,内存存储多字节整数序列有两种方法,称为主机字节序(HBO):
    小端序(little-endian) - 低序字节存储在低地址
    将低字节存储在起始地址,称为“Little-Endian”字节序,Intel、AMD等采用的是这种方式;
    大端序(big-endian)- 高序字节存储在低地址
    将高字节存储在起始地址,称为“Big-Endian”字节序,由ARM、Motorola等所采用
        
    如何测试主机字节序:
        方法1:使用指针
        方法2:使用file命令,file a.out 其中LSB的L表明小端存储
        方法3:共用体
    
    网络中传输的数据必须按网络字节序,即大端字节序
    
    #include <arpa/inet.h>
    
    将主机字节序转化为网络字节序
    uint32_t htonl(uint32_t hostlong);
    uint16_t htons(uint16_t hostshort);
    
    将网络字节序转化为主机字节序
    uint32_t ntohl(uint32_t netlong);
    uint16_t ntohs(uint16_t netshort);

    例子:
        htons(9999);
    
****************TCP网络编程******************
【1】流程
        举个例子:
            买个手机、买张卡
            手机和卡必须匹配
            将卡与手机绑定
            设置为非飞行模式
            进行通讯
        服务器:server.c
            建立套接字 socket( )
            填充服务器的网络信息结构体 sockaddr_in
            将套接字与服务器的网络信息结构体绑定 bind( )
            将套接字设置为监听模式 listen( )
            阻塞等待客户端的链接请求 accept( )
            进行通讯 recv( )/send( )
        客户端:client.c
            建立套接字 socket( )
            填充服务器的网络信息结构体 sockaddr_in
            发送客户端的链接请求 connect( )
            进行通讯 send( )/recv( )
【2】ctags的建立
    第一步:在/usr/include 里面执行
            sudo ctags -R,生成一个tags 的索引文件
            测试:vim -t sockaddr_in
    第二步:实现全局
            在家目录下的 .vimrc 添加
            set tags+=/usr/include/tags
    使用:ctrl ] 追代码
          ctrl t 返回上一层
    

【3】socket( )
    #include <sys/types.h>          /* See NOTES */
    #include <sys/socket.h>

    int socket(int domain, int type, int protocol);
    功能:建立一个套接字,返回一个文件描述符
    参数:
        domain:通讯域或者协议族
            AF_UNIX,AF_LOCAL 本地通讯
            AF_INET ipv4网络通讯
            AF_PACKET 底层通讯
        type:类型
            SOCK_STREAM 流式套接字 TCP
            SOCK_DGRAM 数据报套接字 UDP
            SOCK_RAW 底层通讯
        protocol:协议,通常为0
    返回值:
        成功:文件描述符
        失败:-1
    例子:
        int sockfd;
        if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
        {
            perror("fail to socket");
            //return -1;
            exit(1);
        }
【4】bind( )
    #include <sys/types.h>          /* See NOTES */
    #include <sys/socket.h>

    int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
    功能:将套接字与网络信息结构体绑定
    参数:
        sockfd:文件描述符,socket的返回值
        addr:网络信息结构体
            通用的(通常不用)
                struct sockaddr {
                    sa_family_t sa_family;
                    char        sa_data[14];
                }
            通常使用sockaddr_in
                #include <netinet/in.h>
                struct sockaddr_in
                {
                    __SOCKADDR_COMMON (sin_);
                        ==>
                            #define __SOCKADDR_COMMON(sa_prefix) \
                                    sa_family_t sa_prefix##family
                        ==>
                            在函数宏里面,##表明字符串的拼接
                            sa_family_t sin_family;  //协议族 AF_INET
                                    
                    in_port_t sin_port;  //端口号
                    
                    struct in_addr sin_addr;  
                        ==>
                            struct in_addr
                            {                                                                                                              
                                in_addr_t s_addr;  //ip地址
                            };
                                                                                                                            
                    //这个没有用。为了使得sockaddr_in与sockaddr大小一致
                    unsigned char sin_zero[sizeof (struct sockaddr) -
                            __SOCKADDR_COMMON_SIZE -
                            sizeof (in_port_t) -
                            sizeof (struct in_addr)];
                };

        addrlen:addr的大小
    返回值:
        成功:0
        失败:-1
    例子:
        struct sockaddr_in serveraddr;
        serveraddr.sin_family = AF_INET;
        serveraddr.sin_addr.s_addr = inet_addr("192.168.2.222");
        serveraddr.sin_port = htons(9999);
        
        if(bind(sockfd, (struct sockaddr *)&serveraddr, sizeof(struct sockaddr_in)) < 0)
        {
            perror("fail to bind");
            exit(1);
        }
【5】listen( )
    #include <sys/types.h>          /* See NOTES */
    #include <sys/socket.h>

    int listen(int sockfd, int backlog);
    功能:将套接字设置为监听模式
    参数:
        sockfd:文件描述符,socket的返回值
        backlog:同时响应客户端的链接的个数,通常设置为5,10
    返回值:
        成功:0
        失败:-1
    例子:
        if(listen(sockfd, 10) < 0)
        {
            perror("fail to listen");
            exit(1);
        }
【6】accept
    #include <sys/types.h>          /* See NOTES */
    #include <sys/socket.h>

    int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
    功能:阻塞等待文件描述符准备就绪
    参数:
        sockfd:文件描述符,socket的返回值
        addr:网络信息结构体(自动填充的客户端的网络信息结构体)
        addrlen:addr的大小
    返回值:
        成功:新的文件描述符(用于通讯)
        失败:-1
    例子:
        int acceptfd;
        struct sockaddr_in clientaddr;
        socklen_t addrlen = sizeof(clientaddr);
        if((acceptfd = accept(sockfd, (struct sockaddr *)&clientaddr, &addrlen)) <0 )
        {
            perror("fail to accept");
            exit(1);
        }
【7】connect
    #include <sys/types.h>          /* See NOTES */
    #include <sys/socket.h>

    int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
    功能:发送客户端的链接请求
    参数:
        sockfd:文件描述符,socket的返回值
        addr:网络信息结构体(本身填充的服务器的网络信息结构体)
        addrlen:addr的大小
    返回值:
        成功:0
        失败:-1
    例子:
        if(connect(sockfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr)) < 0)
        {
            perror("fail to connect");
            exit(1);
        }
【8】send( )
    #include <sys/types.h>
    #include <sys/socket.h>

    ssize_t send(int sockfd, const void *buf, size_t len, int flags);
    功能:发送数据
    参数:
        sockfd:文件描述符
            服务器:accept的返回值
            客户端:socket的返回值
        buf:发送的数据
        len:buf的长度
        flags:标志位
            0 阻塞
            MSG_DONTWAIT 非阻塞
    返回值:
        成功:发送的数据的长度
        失败:-1
        
【9】recv( )        
    #include <sys/types.h>
    #include <sys/socket.h>

    ssize_t recv(int sockfd, void *buf, size_t len, int flags);
    功能:接收数据
    参数:
        sockfd:文件描述符
            服务器:accept的返回值
            客户端:socket的返回值
        buf:接收的数据
        len:buf的长度
        flags:标志位
            0 阻塞
            MSG_DONTWAIT 非阻塞
    返回值:
        成功:接收的数据的长度
            0 发送端异常退出或者关闭文件描述符
        失败:-1
        
****************UDP网络编程*******************
【1】流程
    服务器:
        建立套接字 socket( )
        填充服务器的网络信息结构体 sockaddr_in
        将套接字与服务器的网络信息结构体绑定 bind( )
        进行通讯 recvfrom( )/sendto( )
    客户端:
        建立套接字 socket( )
        填充服务器的网络信息结构体 sockaddr_in
        进行通讯 sendto( )/recvfrom( )
        
【2】sendto( )
    #include <sys/socket.h>

    ssize_t sendto(int socket, const void *message, size_t length,
            int flags, const struct sockaddr *dest_addr, socklen_t dest_len);
    功能:发送数据
    参数:
        socket:文件描述符,socket的返回值
        message:发送的数据
        length:数据的长度
        flags:标志位,通常为0
        dest_addr:目的地址(给谁发送数据)
        dest_len:addr的大小
    返回值:
        成功:发送的数据的长度
        失败:-1
        
【3】recvfrom( )
    #include <sys/types.h>
    #include <sys/socket.h>

    ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,
                        struct sockaddr *src_addr, socklen_t *addrlen);
    功能:接收数据
    参数:
        sockfd:文件描述符
        buf:接收的数据
        len:数据的长度
        flags:标志位,通常为0
        src_addr:源的地址(接收谁的信息)
        addrlen:addr的长度
    返回值:
        成功:接收的数据的长度
        失败:-1    
    做业:基于tcp 的文件服务器
        功能:
            客户端能够查看服务器所在目录的文件 opendir readdir
            客户端能够下载服务器所在目录的文件
            客户端能够上传文件到服务器
    
******************day_2*******************
***********IO模型***************
【1】定义
    在UNIX/Linux下主要有4种I/O 模型:
    阻塞I/O:
        最经常使用、最简单、效率最低
    非阻塞I/O:
        可防止进程阻塞在I/O操做上,须要轮询
    I/O 多路复用:
        容许同时对多个I/O进行控制
    信号驱动I/O:
        一种异步通讯模型
【2】阻塞I/O 模式
    阻塞I/O 模式是最广泛使用的I/O 模式,大部分程序使用的都是阻塞模式的I/O 。
    缺省状况下,套接字创建后所处于的模式就是阻塞I/O 模式
    前面学习的不少读写函数在调用过程当中会发生阻塞。
        读操做中的read、recv、recvfrom
        写操做中的write、send
        其余操做:accept、connect
【3】非阻塞模式I/O
    当咱们将一个套接字设置为非阻塞模式,咱们至关于告诉了系统内核:“当我请求的I/O 操做不可以立刻完成,你想让个人进程进行休眠等待的时候,
    不要这么作,请立刻返回一个错误给我。”
    当一个应用程序使用了非阻塞模式的套接字,它须要使用一个循环来不停地测试是否一个文件描述符有数据可读(称作polling)。
    应用程序不停的polling 内核来检查是否I/O操做已经就绪。这将是一个极浪费CPU 资源的操做。
    这种模式使用中不广泛。
    
    使用fcntl函数实现非阻塞IO
    #include <unistd.h>
    #include <fcntl.h>

    int fcntl(int fd, int cmd, ... /* arg */ );
    功能:操做一个文件描述符
    参数:
        fd:文件描述符
        cmd:具体的命令或者选项
            F_GETFL 获取文件状态标志位
            F_SETFL 设置文件状态标志位
                O_NONBLOCK 非阻塞
        arg:可变参,根据cmd的后面括号决定,若是是void则不使用,若是为long,则须要
    返回值:    
        成功:
            F_GETFL 获取到的文件状态标志位
            F_SETFL 0
        失败:-1
    注意:对寄存器或者位的操做,通常执行读、改、写三步

    第一步:获取标志位
    int flags;
    if((flags = fcntl(0, F_GETFL)) < 0)
    {
        perror("fail to fcntl");
        exit(1);
    }
    
    第二步:修改标志位
    //flags = flags | O_NONBLOCK;
    flags |= O_NONBLOCK;
    
    第三步:将新的标志位写回去
    if(fcntl(0, F_SETFL, flags) < 0)
    {
        perror("fail to fcntl");
        exit(1);
    }
【4】多路复用I/O
    应用程序中同时处理多路输入输出流,若采用阻塞模式,将得不到预期的目的;
    若采用非阻塞模式,对多个输入进行轮询,但又太浪费CPU时间;
    若设置多个进程,分别处理一条数据通路,将新产生进程间的同步与通讯问题,使程序变得更加复杂;
    比较好的方法是使用I/O多路复用。其基本思想是:
        先构造一张有关描述符的表,而后调用一个函数。当这些文件描述符中的一个或多个已准备好进行I/O时函数才返回。
        函数返回时告诉进程那个描述符已就绪,能够进行I/O操做。

    使用select实现IO多路复用
    #include <sys/select.h>
    #include <sys/time.h>
    #include <sys/types.h>
    #include <unistd.h>
    
    int select(int nfds, fd_set *readfds, fd_set *writefds,
                fd_set *exceptfds, struct timeval *timeout);
    功能:容许与一个程序能够操做多个文件描述符,阻塞等待文件描述符准备就绪,
          若是有一个或者多个文件描述符准备就绪,函数当即返回,并执行相应的IO操做
    参数:
        nfds:最大的文件描述符加1
        readfds:读文件描述符集合
        writefds:写文件描述符集合
        exceptfds:其余或者异常的文件描述符集合
        timeout:超时
            NULL 阻塞
    返回值:
        成功:准备就绪的文件描述符的个数
        失败:-1
        
    void FD_ZERO(fd_set *set);
    清空一个集合
    
    void FD_SET(int fd, fd_set *set);
    将文件描述符fd添加到集合set里面
    
    void FD_CLR(int fd, fd_set *set);
    将文件描述符fd从集合set里面移除
    
    int  FD_ISSET(int fd, fd_set *set);
    判断文件描述符fd是否在集合set里面
    
***************服务器模型*********************
【1】定义
    在网络程序里面,一般都是一个服务器处理多个客户机。

    为了处理多个客户机的请求, 服务器端的程序有不一样的处理方式。

    目前最经常使用的服务器模型.
        循环服务器:
            循环服务器在同一个时刻只能响应一个客户端的请求
            TCP循环服务器
            UDP循环服务器
        并发服务器:
            并发服务器在同一个时刻能够响应多个客户端的请求
            TCP并发服务器
            UDP并发服务器
【2】如何实现tcp并发服务器
    方法1:使用父子进程实现tcp并发服务器
    socket()
    sockaddr_in
    bind()
    listen()
    //如何处理僵尸进程
    while(1)
    {
        accept()
        pid = fork;
        if(pid > 0)  //父进程负责链接
        {
            
        }
        else if(pid == 0) //子进程负责通讯
        {
            while(1)
            {
                recv()/send()
            }
        }
    }
    
    方法2:使用select函数实现tcp并发服务器
    #include <sys/select.h>
    #include <sys/time.h>
    #include <sys/types.h>
    #include <unistd.h>
    
    int select(int nfds, fd_set *readfds, fd_set *writefds,
                fd_set *exceptfds, struct timeval *timeout);
    功能:容许与一个程序能够操做多个文件描述符,阻塞等待文件描述符准备就绪,
          若是有一个或者多个文件描述符准备就绪,函数当即返回,并执行相应的IO操做
    参数:
        nfds:最大的文件描述符加1
        readfds:读文件描述符集合
        writefds:写文件描述符集合
        exceptfds:其余或者异常的文件描述符集合
        timeout:超时
            NULL 阻塞
    返回值:
        成功:准备就绪的文件描述符的个数
        失败:-1
        
    void FD_ZERO(fd_set *set);
    清空一个集合
    
    void FD_SET(int fd, fd_set *set);
    将文件描述符fd添加到集合set里面
    
    void FD_CLR(int fd, fd_set *set);
    将文件描述符fd从集合set里面移除
    
    int  FD_ISSET(int fd, fd_set *set);
    判断文件描述符fd是否在集合set里面
    
    推荐书籍:
        tcp/ip 详解 卷一 卷二 卷三
        UNIX环境高级编程
        UNIX网络编程 卷一 卷二
        unix/linux系统编程手册
    
    做业:
        使用poll实现io多路复用,fgets、accept
        基于udp的网络聊天室
*********************day_3********************
【1】网络信息检索函数
    getsockopt( ) 获取一个套接口选项
    #include <sys/socket.h>

    int getsockopt(int socket, int level, int option_name,
            void *restrict option_value, socklen_t *restrict option_len);
    功能:获取一个套接字的选项
    参数:
        socket:文件描述符
        level:层次
            SOL_SOCKET 套接字层次
            IPPROTO_IP IP层次
            IPPROTO_TCP TCP层次
        option_name:选项的名称(SOL_SOCKET)
            SO_BROADCAST 是否容许发送广播信息
            SO_REUSEADDR 是否容许重复使用本地地址
            SO_SNDBUF 获取发送缓冲区的大小
            SO_RCVBUF 获取接收缓冲区的大小
            SO_RCVTIMEO 设置接收超时时间
            SO_SNDTIMEO 设置发送超时时间
        option_value:获取到的值
        option_len:option_value的大小
    返回值:
        成功:0
        失败:-1
**************网络超时检测***************
【1】必要性
    在网络通讯中,不少操做会使得进程阻塞
    TCP套接字中的recv/accept/connect
    UDP套接字中的recvfrom
    超时检测的必要性
        避免进程在没有数据时无限制地阻塞
        当设定的时间到时,进程从原操做返回继续运行
【2】本质
    阻塞函数若是没有数据到来时会一直等待
    非阻塞即便没有数据到来,函数也会当即返回
    网络超时检测的本质是设定必定的时间,在时间到达以前若是没有数据到来,
    则一直阻塞等待,若是时间到达是尚未数据到来,则函数当即返回
【3】方法1:使用setsockopt设置网络超时检测
    #include <sys/socket.h>

    int setsockopt(int socket, int level, int option_name,
            const void *option_value, socklen_t option_len);
    功能:设置一个套接字的选项
    参数:
        socket:文件描述符
        level:层次
            SOL_SOCKET 套接字层次
        option_name:选项的名称
            SO_RCVTIMEO 设置接收超时时间
        option_value:设置的值
            struct timeval
            {
                __time_t tv_sec;        秒
                __suseconds_t tv_usec;  微秒                                                                
            };
        option_len:option_value的大小
    返回值:
        成功:0
        失败:-1
【4】方法2:使用select设置网络超时检测
    #include <sys/select.h>
    #include <sys/time.h>
    #include <sys/types.h>
    #include <unistd.h>
    
    int select(int nfds, fd_set *readfds, fd_set *writefds,
                fd_set *exceptfds, struct timeval *timeout);
    功能:容许与一个程序能够操做多个文件描述符,阻塞等待文件描述符准备就绪,
          若是有一个或者多个文件描述符准备就绪,函数当即返回,并执行相应的IO操做
    参数:
        nfds:最大的文件描述符加1
        readfds:读文件描述符集合
        writefds:写文件描述符集合
        exceptfds:其余或者异常的文件描述符集合
        timeout:超时
            struct timeval {
               long    tv_sec;         /* seconds */
               long    tv_usec;        /* microseconds */
            };
            
            >0 超时时间
            0  当即返回
            NULL 一直阻塞
    返回值:
        成功:准备就绪的文件描述符的个数
        失败:-1
        
    void FD_ZERO(fd_set *set);
    清空一个集合
    
    void FD_SET(int fd, fd_set *set);
    将文件描述符fd添加到集合set里面
    
    void FD_CLR(int fd, fd_set *set);
    将文件描述符fd从集合set里面移除
    
    int  FD_ISSET(int fd, fd_set *set);
    判断文件描述符fd是否在集合set里面
【5】使用alarm闹钟实现网络超时检测
    若是直接使用alarm,当时间到达时,会退出整个进程
    若是结合信号,当时间到达时,触发SIGALRM信号,执行信号处理函数
    当信号处理函数执行完毕以后,会接着刚才的位置继续执行,这一属性
    称之为自重启属性,若是想实现超时,须要关闭这一属性,
    
    使用sigaction函数设置信号的行为
    #include <signal.h>

    int sigaction(int signum, const struct sigaction *act,
                     struct sigaction *oldact);
    功能:设置一个信号的行为
    参数:    
        signum:信号
        act:新的行为
        oldact:旧的行为
            struct sigaction {
                void     (*sa_handler)(int);  信号处理函数
                void     (*sa_sigaction)(int, siginfo_t *, void *); 信号处理函数
                sigset_t   sa_mask; 掩码(有关阻塞)
                int        sa_flags; 标志位
                    SA_RESTART 自重启属性
                void     (*sa_restorer)(void);  没有用
            };
    返回值:
        成功:0
        失败:-1

    注意:对寄存器或者位的操做,通常执行读、改、写三步
    
********************广播*************
【1】定义
    前面介绍的数据包发送方式只有一个接受方,称为单播
    若是同时发给局域网中的全部主机,称为广播
    只有用户数据报(使用UDP协议)套接字才能广播
    
    广播地址
    以192.168.3.0 (255.255.255.0) 网段为例,最大的主机地址192.168.3.255表明该网段的广播地址
    发到该地址的数据包被全部的主机接收
    255.255.255.255在全部网段中都表明广播地址
    
【2】流程(基于udp)
    发送者:    
        建立套接字 socket( )
        填充广播信息结构体 sockaddr_in
        设置为容许发送广播权限 setsockopt( )
        接收数据 sendto( )
        
    接收者:
        建立套接字 socket( )
        填充广播信息结构体 sockaddr_in
        将套接字与广播信息结构体绑定 bind( )
        接收数据 recvfrom( )
【3】设置为容许发送广播权限
    #include <sys/socket.h>

    int setsockopt(int socket, int level, int option_name,
            const void *option_value, socklen_t option_len);
    功能:设置一个套接字的选项
    参数:
        socket:文件描述符
        level:层次
            SOL_SOCKET 套接字层次
        option_name:选项的名称
            SO_BROADCAST 设置接收超时时间
        option_value:设置的值
            0 不容许
            1 容许
        option_len:option_value的大小
    返回值:
        成功:0
        失败:-1

***************组播***************
【1】定义
    单播方式只能发给一个接收方。
    广播方式发给全部的主机。过多的广播会大量占用网络带宽,形成广播风暴,影响正常的通讯。
    组播(又称为多播)是一种折中的方式。只有加入某个多播组的主机才能收到数据。
    多播方式既能够发给多个主机,又能避免象广播那样带来过多的负载(每台主机要到传输层才能判断广播包是否要处理)

    D类地址(组播地址)
        不分网络地址和主机地址,第1字节的前4位固定为1110
        224.0.0.1 – 239.255.255.255

    
【2】流程
    发送者:
        建立套接字 socket( )
        填充组播信息结构体 sockaddr_in
        发送数据 sendto( )
    接收者:
        建立套接字 socket( )
        填充组播信息结构体 sockaddr_in
        将套接字与组播信息结构体绑定 bind( )
        设置为加入多播组 setsockopt( )
        接收数据 recvfrom( )
        
【3】加入多播组
    #include <sys/socket.h>

    int setsockopt(int socket, int level, int option_name,
            const void *option_value, socklen_t option_len);
    功能:设置一个套接字的选项
    参数:
        socket:文件描述符
        level:层次
            IPPROTO_IP     IP层次
        option_name:选项的名称
            IP_ADD_MEMBERSHIP 加入多播组
        option_value:设置的值
            struct ip_mreq  {
                struct in_addr imr_multiaddr;   组播地址
                struct in_addr imr_interface;   本地地址
                        INADDR_ANY 任意主机地址
            };       
        option_len:option_value的大小
    返回值:
        成功:0
        失败:-1
    
    例子:
        struct ip_mreq mreq;
        mreq.imr_multiaddr.s_addr = inet_addr(argv[1]);
        mreq.imr_interface.s_addr = htonl(INADDR_ANY);
        
        if(setsockopt(sockfd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) < 0)
        {
            perror("fail to setsockopt");
            exit(1);
        }
    
*************使用poll实现IO多路复用****************
    实现fgets、accept两个阻塞函数
    
    #include <poll.h>

    int poll(struct pollfd *fds, nfds_t nfds, int timeout);
    功能:同select
    参数:
        fds:结构体数组
             struct pollfd {
                int   fd;         文件描述符
                
                short events;  请求的事件
                    POLLIN        普通或优先级带数据可读
                    POLLRDNORM    普通数据可读
                    POLLRDBAND    优先级带数据可读
                    POLLPRI        高优先级数据可读
                    POLLOUT        普通数据可写
                    POLLWRNORM    普通数据可写
                    POLLWRBAND    优先级带数据可写
                    POLLERR        发生错误
                    POLLHUP        发生挂起
                    POLLNVAL    描述字不是一个打开的文件
                    
                short revents;    返回的事件
            };
        nfds:文件描述符的个数
        timeout:超时
            <0    永远等待
            0    当即返回,不阻塞进程
            >0    等待指定数目的毫秒数
    返回值:
        成功:准备就绪的文件描述符的个数
        失败:-1    
    
******************本地通讯********************
【1】定义
    socket一样能够用于本地通讯
    建立套接字时使用本地协议PF_UNIX(或PF_LOCAL)。
    分为流式套接字和用户数据报套接字
    和其余进程间通讯方式相比使用方便、效率更高
    经常使用于先后台进程通讯

【2】本地信息结构体  sockaddr_un
    #include <sys/un.h>
    
    struct sockaddr_un                                                                                               
    {
        __SOCKADDR_COMMON (sun_);
            ==>
                #define __SOCKADDR_COMMON(sa_prefix) \                                                                           
                        sa_family_t sa_prefix##family
            ==>
                sa_family_t sun_family;  //地址族 AF_UNIX

        char sun_path[108];  路径名
    };
    
【3】TCP本地通讯
    服务器:
        建立套接字 socket( )
        填充本地信息结构体 sockaddr_un
        将套接字与本地信息结构体绑定 bind( )
        将套接字设置为监听模式 listen( )
        阻塞等待客户端的链接请求 accept( )
        进行通讯 recv( )/send( )
    客户端:
        建立套接字 socket( )
        填充本地信息结构体 sockaddr_un
        发送客户端的链接请求 connect( )
        进行通讯 send( )/recv( )
    linux

相关文章
相关标签/搜索