zoukankan      html  css  js  c++  java
  • 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( )
       

  • 相关阅读:
    Hello iOS
    钝化程序、逻辑冻结、冻结程序的延续、瞬间转移
    对字符串操作的各种笔试题
    .NET各大平台数据列表控件绑定原理及比较(WebForm、Winform、WPF)
    多线程计算----pthread
    Xcode的Hello World(简单易懂)
    two sets of Qt binaries into the same process的解决办法
    微软新一代输入法框架 TSF
    Starting the application on Mac does not work(拷贝platforms到不同的位置,才能解决问题),还可设置DYLD_PRINT_LIBRARIES=1 观察动态库
    dddd
  • 原文地址:https://www.cnblogs.com/wanghuaijun/p/7439661.html
Copyright © 2011-2022 走看看