学习日记week10

网络通信:

    底层遵循TCP/IP协议没在系统中以socket接口方式呈现


 

基于TCP协议的网络通信模型:

        服务端                                 客户端

    创建socket对象                          创建socket对象

    准备通讯地址(本机IP)+端口号                 准备通讯地址(目标公网IP)+端口号

    绑定socket和通信地址                           ...

    设置监听和排队的数量                          ...

    等待客户端连接                             连接服务端

    分配新的socket对象                                ...

    接收请求                                    发送请求

    相应请求                                    接受响应

    关闭socket                                    关闭socket

    使用到的函数:

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

        功能:创建socket对象

        domain:

            AF_UNIX, AF_LOCAL   本地通信,进程间通信

            AF_INET             基于IPv4地址通信

            AF_INET6            基于IPv6地址通信

        type:

            SOCK_STREAM         数据流协议

        protocol:

            特殊的通信协议,一般不用,写0即可

        返回值:成功返回socket描述符,失败返回-1

        #include <netinet/in.h> [vim /usr/include/linux/in.h]

        struct sockaddr_in {

            __kernel_sa_family_t  sin_family; //domain写什么就填什么

            __be16        sin_port;   //端口号(大端数据htons)

            struct in_addr    sin_addr;   //IP地址(大端数据)

        };

        struct in_addr {

            __be32  s_addr;

        };

        

        大小端转换函数:

        #include <arpa/inet.h>

        uint32_t htonl(uint32_t hostlong);

        功能:把4字节的本地字节序转换成网络字节序

        uint16_t htons(uint16_t hostshort);

        功能:把2字节的本地字节序转换成网络字节序

        uint32_t ntohl(uint32_t netlong);

        功能:把4字节的网络字节序转换成本地字节序

        uint16_t ntohs(uint16_t netshort);

        功能:把4字节的网络字节序转换成本地字节序

        IPv4地址转换:

            #include <sys/socket.h>

            #include <netinet/in.h>

            #include <arpa/inet.h>

            in_addr_t inet_addr(const char *cp);

            功能:把字符串格式的点分十进制的IP地址,转换成整数形式的IP地址(大端)

            例如:inet_addr("192.168.0.1");

            char *inet_ntoa(struct in_addr in);

            功能:把整数形式的IP地址,转换成字符串格式的点分十进制IP地址

            注意:send、recv是tcp协议通信时专用的数据发送和接受函数

                但是也可以继续使用write、read函数

基于UDP协议的网络通信模型:

        接受者                         发送者

    创建socket                        创建socket

    准备通信地址                  准备通信地址

        绑定                          ...

    接受请求                        发送请求

    响应请求                        接收响应

        关闭                          关闭

    使用到的函数:

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

        功能:创建socket对象

        domain:

            AF_UNIX, AF_LOCAL   本地通信,进程间通信

            AF_INET             基于IPv4地址通信

            AF_INET6            基于IPv6地址通信

        type:

            SOCK_DGRAM          数据报协议

        protocol:

            特殊的通信协议,一般不用,写0即可

        返回值:成功返回socket描述符,失败返回-1

        UDP专用的收发数据函数:

            ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,const struct sockaddr *dest_addr, socklen_t addrlen);

            功能:发送数据,UDP专用

            sockfd:socket描述符

            buf:待发送的数据的内存首地址

            len:要发送的字节数

            flags:是否阻塞 写0即可(阻塞)

            dest_addr:通信目标的地址

            addrlen:地址结构体的字节数

            返回值:成功发送的字节数,

                -1      出现错误

                0       通信关闭

            ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,struct sockaddr *src_addr, socklen_t *addrlen);

            功能:接收数据,UDP专用

            sockfd:socket描述符

            buf:存储读取到的数据的缓冲区首地址

            len:缓冲区的大小

            flags:是否阻塞 写0即可(阻塞)

            src_addr:用于存储发送者的地址结构体

            addrlen:既是输入也是输出

                1、告诉系统src_addr结构体的字节数

                2、反馈实际接收到的发送者的地址结构体字节数

            返回值:成功发送的字节数,

                -1      出现错误

                0       通信关闭

封装TCP/UDP通信代码:

    1、gcc -fpic -c network.c

    2、gcc -shared -fpic network.o -o libnw.so

    3、sudo cp libnw.so /usr/lib

    4、sudo cp network.h /usr/include/

多路复用:

    使用一个进程(且只有一个主线程)同时监控若干个文件描述符的读写,这种读写模式叫做多路复用

    多用于TCP客户端,用于监控若干个客户端的链接和数据的收发

    优点:不需要频繁的创建进程、销毁进程,从而达到节约内存资源、时间资源,也能避免进程之间的竞争、等待

    缺点:单个客户端的任务不能耗时太长,否则其他客户端就会感知到

    适合并发量高,任务量短小的情景,例如:Web服务器

select:

    fd_set是文件描述符的集合,是要通过以下函数进行操作

    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);

    功能:把文件描述符添加到集合中

    void FD_ZERO(fd_set *set);

    功能:清空集合

    #include <sys/select.h>

    int select(int nfds, fd_set *readfds, fd_set *writefds,fd_set *exceptfds, struct timeval *timeout);

    功能:监控多个文件描述符的读、写、异常操作

    nfds:被监控的文件描述符中最大的+1

    readfds:监控读操作的文件描述符集合

    writefds:监控写操作的文件描述符集合

    exceptfds:监控异常操作的文件描述符集合

    timeout:设置超时时间

            NULL    一直阻塞,知道某个文件描述符产生变化

            大约0 等待超时时间,超时后返回0

            0秒0毫秒   非阻塞

    返回值:监控的发生相关操作的文件描述符的个数,超时返回0,错误返回-1

    注意:readfds,writefds,exceptfd 这三个参数即是输入也是输出,也就是说调用select时这三个即合理秒需要存放着被监控的文件描述符,当函数因为监控的文件发生变化而返回时,发生变化的文件描述符会存在集合中没发生变化的就删除掉了

    select设计不合理的地方:

        1、每次调用select时都需要重新向他传递被监控者的集合

        2、调用结束后若想要知道哪些文件喵师傅发生了相关操作,需要把所有的监控的文件描述符都测试一遍

    select的优点:

        他是最早的多路复用的函数,几乎所有的操作系统都支持,程序的的兼容性高

    

pselect:[只是select的增强版,本质上没有区别,缺点没变]

    int pselect(int nfds, fd_set *readfds, fd_set *writefds,fd_set *exceptfds, const struct timespec *timeout,const sigset_t *sigmask);

    功能:与select大致相同

    区别:

        1、超时时间的类型不同,pselect的精度更高

        2、pselect的timeout,即是输入也是输出,可以返回剩余的时间,但是select只是输入,不能输出

        3、pselect监听时可以通过sigmask参数设置要想要在监听时屏蔽的信号,可以保证pselect的监听不被信号干扰

poll:

    struct pollfd {

        int   fd;         //被监听的文件描述符   输入

        short events;     //要监听的事件      输入

        short revents;    //实际监听到的事件    输出

        {

            POLLIN      普通优先级的读事件

            POLLPRI     高优先级的读事件

            POLLOUT     普通优先级的写事件

            POLLRDHUP   对方socket关闭

            POLLERR     错误事件

            POLLHUP     对方挂起事件

            POLLNVAL    非法的文件描述符

        }

    };

    #include <poll.h>

    int poll(struct pollfd *fds, nfds_t nfds, int timeout);

    fds:pollfd结构体数组

    nfds:数组的长度,也就是数组成员个数

    timeout:超时时间

    返回值:监听到发生相关操作的描述符的个数,超时返回0,错误返回-1

epoll

    #include <sys/epoll.h>

    int epoll_create(int size);

    功能:创建epoll对象,该对象可以保存被监控的描述符

    size:epoll能保存描述符的数量

    返回值:创建成功的epoll对象的描述符

    int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);

    功能:控制epoll对象,添加、删除描述符

    epfd:epoll对象的描述符,即epoll_create的返回值

    op:

        EPOLL_CTL_ADD   添加socket文件描述符

        EPOLL_CTL_MOD   删除socket文件描述符

        EPOLL_CTL_DEL   修改要监控的事件

    fd:

        想要操作的socket文件呢描述符

    event:

        想要监控的事件,可以参考poll的事件 前面加E

    返回值:成功返回0,失败返回-1

    int epoll_wait(int epfd, struct epoll_event *events,int maxevents, int timeout);

    功能:监控文件描述符,并返回产生事件的文件描述符

    epfd:epoll对象的描述符,即epoll_create的返回值

    events:输出型参数,用于获取产生事件的文件描述符

    maxevents:可以返回的最大的事件数

    timeout:设置超时时间

    返回值:事件发生的描述符的数量

    

    epoll的优点:

        1、只需要添加一次描述符,不需要重复赋值

        2、会把发生变化的文件描述符返回,因此不需要遍历全部都文件描述符即可

        3、编程结构简洁

epoll的条件触发和边缘触发:

    条件触发:当文件缓冲区中又要读取的数据时就会触发事件  [像键盘]

    边缘触发:当数据发送时只触发一次                       [像鼠标]

        1、事件监控设置为EPOLLET

        2、要循环读取缓冲区直到缓冲区读完

        3、要以非阻塞的模式读取

        4、返回-1表示读取完毕,才能进行下一次epoll_wait监控事件

        优点:减低事件触发的次数

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值