网络编程相关api

回顾:
    OSI七层模型
    
    应用层
    表示层
    会话层
    传输层
    网络层
    数据链路层
    
    物理层
    
    TCP/IP四层模型
    
    IP地址
    A    0 + 7 + 24
    B    10 + 14 + 16
    C    110 + 21 + 8
    D    1110 + 28位组播地址
    E    留待备用
    
    192.168.0.103 /24
    
    127.0.0.1:回环地址,可用于本机网络协议的测试和本地进程间通信
    
    端口号
    
    16位无符号整数,0-65535            1024被系统使用
    
    
    字节序
    
---------------------------------
socket编程:
1.网络编程又叫socket编程
    套接字是一个网络编程的接口,是网络数据传输的软设备,用于网络交互。
    
    网络编程就是编写程序使两台连网的计算机相互交换数据,这个就是网络编程的全部内容。
    
    unix/linux系统作为服务器操作系统存在至今,因此,Linux的网络功能应该是非常全面和强大。
    
    网络编程其实有很成熟的通信模型,并且windows也通用。
    
2.通信模型(基于TCP的一对一的通信模型)
    
    服务器:
        1.创建socket,使用函数socket()
        2.准备通信地址,使用结构体类型
        3.绑定socket和通信地址,使用bind()
        4.监听,使用函数listen()
        5.等待连接,使用函数accept()
        6.进行通信,使用read()、write()
        7.关闭socket,使用函数close()
        
    客户端:
        1.创建socket,使用函数socket()
        2.准备通信地址(指服务器的地址),使用结构体类型
        3.连接服务器,使用函数connect()
        4.进行通信,使用read()、write()
        5.关闭socket,使用函数close()

具体函数:
    1.socket()函数
        #include          /* See NOTES */
        #include

        int socket(int domain, int type, int protocol);
        功能:主要用于创建可以实现通信的交流点,也就是socket通信载体(相当于电话机)
        第一个参数:域/协议族,决定了是本地通信还是网络通信
                    AF_UNIX/AF_LOCAL    用于实现本地通信
                    AF_INET                用于实现基于IPv4的网络通信
                    AF_INET6            用于实现基于IPv6的网络通信
                    
        第二个参数:通信类型,决定了具体的通信式
                    SOCK_STREAM        提供有序的、可靠的、双向的、面向连接的字节流通信方式,默认使用TCP协议
                    SOCK_DGRAM        提供无序的、不可靠的,非面向连接的数据报通信方式,默认使用UDP协议
                    
        第三个参数:指定具体的协议,默认为0,使用默认协议

        返回值:成功返回新的socket描述符
                失败返回-1,errno被设置
        
                    
    2.通信地址数据类型
        1.通用地址结构
        struct sockaddr
        {
            sa_family_t sa_family;
            char sa_data[14];
        };
        2.IPv4通信地址结构
        struct sockaddr_in
        {
            sa_family_t sin_family;//协议族,AF_INET
            in_port_t sin_port;//16位的端口号
            struct in_addr sin_addr;//IP地址 
        };
        struct in_addr
        {
            in_addr_t s_addr;
        };        
        
    例:
        struct sockaddr_in saddr;
        memset(&saddr,0,sizeof(saddr);
        saddr.sin_family = AF_INET;
        saddr.sin_port = htons(10086);
        saddr.sin_addr.s_addr = inet_addr("192.168.0.103");
        
        
        端口格式转换相关函数:
            #include

           uint32_t htonl(uint32_t hostlong);

           uint16_t htons(uint16_t hostshort);

           uint32_t ntohl(uint32_t netlong);

           uint16_t ntohs(uint16_t netshort);

        h:host 本地格式
        n:network    网络格式(大端模式)
        s:short        2字节整数
        l:long        4字节整数    
        功能:把本地格式转成网络格式,或者反过来。
        
        IP地址格式转换相关函数:
            in_addr_t inet_addr(const char *cp);
            功能:将字符串形式的IP地址转为整数类型
            char *inet_ntoa(struct in_addr in);
            功能:将结构类型的IP地址转为字符串形式
        
        
    3.绑定函数bind()
        include          /* See NOTES */
        #include

        int bind(int sockfd, const struct sockaddr *addr,
                socklen_t addrlen);
        功能:主要用于绑定socket和具体的通信地址
        第一个参数:socket描述符,socket函数的返回值
        第二个参数:结构体指针,不管是什么协议的地址结构,都需要强转为该类型
        第三个参数:通信地址结构的大小,使用sizeof()计算即可
        返回值:成功返回0
                失败返回-1,errno被设置
                
    4.监听listen函数
        #include          /* See NOTES */
        #include

        int listen(int sockfd, int backlog);
        功能:为套接字sockfd建立一个连接请求监听队列,在调用listen函数成功后,这个套接字便成为服务套接字,即被动套接字        
        第一个参数: socket描述符
        第二个参数:监听队列的大小
        返回值:成功返回0
                失败返回-1,errno被设置
                
    5.等待连接,accept函数
        #include          /* See NOTES */
        #include

        int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
        功能:用于接收服务套接字sockfd上的连接请求
        第一个参数:socket描述符,socket函数的返回值
        第二个参数:结构体指针,用于保存接受的客户端通信地址
        第三个参数:指针类型,用于保存客户端网络地址的信息的长度
        返回值:成功返回一个连接套接字
                失败返回-1,errno被设置
        注:如果对客户地址不感兴趣,那么第二、三两个参数写NULL即可。
            如果需要客户的地址,那么第三个参数必须由调用者初始化。

        
    6.连接函数connect
        #include          /* See NOTES */
        #include

        int connect(int sockfd, const struct sockaddr *addr,
                   socklen_t addrlen);
        功能:用于连接服务器
        第一个参数:socket描述符,socket函数的返回值
        第二个参数:服务器的通信地址
        第三个参数:通信地址结构的大小,使用sizeof计算
        返回值:成功返回0
                失败返回-1,errno被设置
        

        
        迭代服务器
        
        并发服务器
            多进程服务器
            多线程服务器
            多路复用服务器
        
        
        
        
    作业:
        用多线程服务器实现群聊
        
        ./a.out 192.168.0.129 10086 name
        
        [name] hello
        [name] hello
        
        confd用数组保存即可
        当有客户连接,保存其fd
        当有客户下线,移除其fd
        
        保存和移除的操作必须同步
    
    
    
-------------------------
IO多路复用
    假如我们要在一个文件可读的情况下,马上读到数据,我们可以在这个文件上阻塞的读
    如果有多个文件,那么就需要多个进程/线程去阻塞的读每个文件
    
    能不能用一个函数,去实现多个文件的查询是否就绪的功能?
    select()
    
    运用select()函数是最具有代表性的实现复用服务器的方法
    
        #include

       /* According to earlier standards */
       #include
       #include
       #include

       int select(int nfds, fd_set *readfds, fd_set *writefds,
                  fd_set *exceptfds, struct timeval *timeout);
                  
            参数:    
                    nfds:感兴趣的文件集合中最大的文件描述符 + 1;
                    readfds:对读感兴趣的文件描述符集合
                    writefds:对写感兴趣的文件描述符集合
                    exceptfds:对异常感兴趣的文件描述符集合
                    timeout:超时时间
                    
        select实现原理:
            select 在实现时,会轮询,每隔一段时间就会去询问文件描述符[0,nfds)中的每一个文件,查看是否就绪,如果就绪那么把相应的文件描述符集合中置1.
            ...
            直到有文件就绪或超时或出错。

       void FD_CLR(int fd, fd_set *set);    //把指定fd从集合中移除
       int  FD_ISSET(int fd, fd_set *set);    //判断指定的fd是否在这个集合中
       void FD_SET(int fd, fd_set *set);    //把指定的fd设置到集合中去
       void FD_ZERO(fd_set *set);            //初始化指定的集合,全部清零
       
        struct timeval {
               time_t         tv_sec;     /* seconds */
               suseconds_t    tv_usec;    /* microseconds */
           };

        在调用select函数之前,readfds是你对读感兴趣的文件描述符集合
        在调用select函数之后,readfds是你对读感兴趣并且可以读的文件描述符集合
        writefds,exceptfds同理。
        
        timeout 在调用select之前,是超时时间
                在调用select之后,是剩余时间
                
        select返回:
            1.有文件数据就绪,立马返回,返回就绪的文件描述符个数  >0 
            2.超时返回,返回0
            3.出错了,返回-1 ,errno被设置
                
        select调用方法与顺序:
            1.    设置文件描述符
                指定监控范围
                设置超时时间
                
            2.调用select函数
            3.查看调用结果
       
       
-----------------------------------------
1.TCP与UDP协议的比较:
    1.tcp协议:
        传输控制协议  类似打电话
        面向连接的    (建立连接-->进行通信-->断开连接)
        在通信的整个过程中必需保持连接
        该协议保证了数据的传递是可靠且有序的
        属于全双工的字节流通信方式
        服务器压力较大,资源消耗大,执行效率较低
        
    2.udp协议:
        用户数据报协议
        面向非连接的    类似发短信
        在通信的整个过程中不需要保持连接
        不保证数据的可靠性和有序性
        属于全双工的数据报通信方式
        服务器压力较小,资源消耗小,执行效率高
        
2.基于UDP协议的通信模型
    服务器:
        1.创建socket,使用socket()函数 
        2.准备通信地址,使用结构体类型
        3.绑定socket和通信地址,使用bind函数
        4.进行通信,使用sendto()/recvfrom()
        5.关闭socket,使用函数close
        
    客户端:
        1.创建socket,使用socket()函数 
        2.准备通信地址,使用结构体类型
        3.进行通信,使用sendto()/recvfrom()
        4.关闭socket,使用函数close()
        
        
    相关API:
        发送数据报函数:sendto()
        #include
        #include

        ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,
                      const struct sockaddr *dest_addr, socklen_t addrlen);
        第一个参数:socket描述符,socket函数的返回值
        第二个参数:被发送的数据首地址
        第三个参数:发送数据的大小
        第四个参数:发送标志
                    0:功能与write一样,即阻塞
                    MSG_DONTWAIT:        非阻塞
                    
        第五个参数:发送数据的目标地址
        第六个参数:目标地址的大小
        
        
        接收数据报的函数:recvfrom()            
        ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,
                        struct sockaddr *src_addr, socklen_t *addrlen);
                        
        参数:参考sendto()函数
    
    
-----------------
UDP应用:
    广播
    组播
    
套接选项:
    我们正常使用套接字编程时,一般只关注数据通信,而忽略套接字的不同特性
    但有时需要设置地址复用,允许发送广播消息,将主机加入多播组,设置发送与接收缓冲区的大小等
    这些都需要对套接字选项进行设置。
    
    每个套接字在不同的协议层次上有不同的行为属性,那么这个行为属性就称为套接字选项
    
    getsockopt
    setsockopt
    
        #include          /* See NOTES */
        #include

        int getsockopt(int sockfd, int level, int optname,
                      void *optval, socklen_t *optlen);
        注:optlen要初始化
        
        int setsockopt(int sockfd, int level, int optname,
                      const void *optval, socklen_t optlen);
                      
        第一个参数:socket描述符,socket函数的返回值
        第二个参数:要设置的套接字选项所在的协议层次
        第三个参数:套接字选项的名称
        第四个参数:保存要设置的选项值 的 缓冲区首地址
        第五个参数:第四个参数的长度
        

        SOL_SOCKET    
            SO_BROADCAST        允许发送广播数据报    int
            SO_REUSEADDR        允许重用本地地址    int 
        
            SO_RCVBUF            接收缓冲区大小        int
            SO_SNDBUF            发送缓冲区大小        int
            
        IPPROTO_IP
            IP_ADD_MEMBERSHIP    加入多播组            ip_mreq{}


广播:
    广播是向同一网络中的所有主机传输数据的方法,是基于UDP的
    

    广播地址:
        192.168.0.255        直接广播/子网内广播
        255.255.255.255     本地广播/全网广播

多播/组播:
    多播/组播的数据传输也是基于UDP的,是同时传递到加入特定多播组的大量主机。
    多播组是D类IP地址(224.0.0.0~239.255.255.255)
    
    加入多播组的方式:
        struct ip_mreq
        {
            struct in_addr imr_multiaddr;//多播组的IP
            struct in_addr imr_interface;//加入多播组的那台主机的IP
        };
        
        struct ip_mreq join_addr;
        join_addr.imr_multiaddr.s_addr = inet_addr("224.1.2.3");
        join_addr.imr_interface.s_addr = inet_addr("192.168.1.110");        
        setsockopt(sockfd,IPPROTO_IP,IP_ADD_MEMBERSHIP,(void*)&join_addr,sizeof(join_addr));
    

UNIX协议域    unix domain socket
    AF_UNIX/
    AF_LOCAL
        #include

        #define UNIX_PATH_MAX    108
        struct sockaddr_un 
        {
           sa_family_t sun_family;               /* AF_UNIX */
           char        sun_path[UNIX_PATH_MAX];  /* pathname */
        };

    利用socket编程接口,来实现本地进程间通信
    UDS提供两类套接字:
        字节流套接字(类似于TCP)
        数据报套接字(类似于UDP)
        
    练习:
        利用网络传输文件
        1.文件名
        2.文件大小    stat()
        3.文件内容
        
        
    
 

你可能感兴趣的:(网络编程)