网络编程学习笔记1

慈云数据 6个月前 (05-13) 技术支持 35 0

文章目录

  • 一、socket
    • 1、创建socket
    • 2、网络通信流程
    • 3、accept()函数
    • 4、signal()函数
    • 5、recv()函数
    • 6、connect()函数
    • 二、I/O多路复用
      • 1.select模型
      • 2.poll模型
      • 3.epoll模型
      • 一、socket

        1、创建socket

        int socket(int domain,int type,int protocol);
        //返回值:一个有效的socket,失败时返回-1,errno被设置。
        
        (1)domain:
        PF_INET       IPv4互联网协议族
        PF_INET6      IPv6互联网协议族
        PF_LOCAL      本地通信的协议族
        PF_PACKET     内核底层协议族
        PF_IPX        IPX Novell协议族
        (2)type数据传输的类型
        2.1 SOCK_STREAM 
        面向连接的socket:
        1)数据不会丢失
        2)数据顺序不会错乱
        3)双向通道
        2.2 SOCK_DGRAM
        无连接的socket:
        1)数据可能会丢失
        2)数据顺序可能错乱
        3)传输的效率更高
        (3)protocol最终使用的协议
        在IPv4中,数据传输方式为SOCK_STREAM的协议只有IPPROTO_TCP,数据传输方式为SOCK_DGRAM的协议只有IPPROTO_UDP,也可填0.
        

        2、网络通信流程

        在这里插入图片描述

        3、accept()函数

        accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
        //是在一个套接口接受的一个连接。用于在服务器端接受客户端连接的系统调用
        //参数:
        sockfd:套接字描述符,该套接口在listen()后监听连接。
        addr:(可选)指针,指向一缓冲区,其中接收为通讯层所知的连接实体的地址。Addr参数的实际格式由套接口创建时所产生的地址族确定。
        addrlen:(可选)指针,输入参数,配合addr一起使用,指向存有addr地址长度的整型数。
        

        4、signal()函数

        //设置某一信号的对应动作
        #include
        void ( *signal( int signum,void(* handler)(int)) )  (int);
        或者:typedef void (*sig_t)( int );
        sig_t signal(int signum,sig_t handler);
        
        //参数:
        1.signum:指明了所要处理的信号类型,它可以取除了SIGKILL和SIGSTOP外的任何一种信号。
        2.handler:描述了与信号关联的动作,它可以取以下三种值:
        (1)一个无返回值的函数地址
        此函数必须在signal()被调用前申明,handler中为这个函数的名字。当接收到一个类型为signum的信号时,就执行handler 所指定的函数。这个函数应有如下形式的定义:
        void func(int sig);
        (2)SIG_IGN
        这个符号表示忽略该信号,执行了相应的signal()调用后,进程会忽略类型为sig的信号。
        (3)SIG_DFL
        这个符号表示恢复系统对信号的默认处理。
        

        5、recv()函数

        int recv(SOCKET s, char FAR*buf, int len, int flags);
        //是一个用于从套接字接收数据的函数。
        //返回值:如果接收到的字节数大于0,表示实际复制到缓冲区的数据量。如果返回0,表示对方已关闭连接。如果返回SOCKET_ERROR,表示出现了错误。
        
        1.参数:
        s:套接字的描述符,指定接收数据的端点。
        buf:一个指向缓冲区的指针,用于存放接收到的数据。
        len:缓冲区buf的长度。
        flags:通常设置为0,但可以用于控制函数的行为,如使用MSG_PEEK查看数据或使用MSG_OOB处理带外数据。
        2.函数行为:
        (1)如果套接字处于阻塞模式,recv将一直等待直到有数据可读或发生错误。
        (2)如果套接字处于非阻塞模式,并且没有数据可读,recv将返回SOCKET_ERROR并设置WSAEWOULDBLOCK错误。
        (3)在数据接收过程中,如果网络出现错误,recv将返回SOCKET_ERROR。
        (4)如果远程端正常关闭连接,对于面向连接的套接字(如SOCK_STREAM),recv将立即返回,接收0个字节。
        (5)如果连接被重置,对于面向连接的套接字,recv将失败并显示错误WSAECONNRESET。
        

        6、connect()函数

        #include <sys/socket.h>
        int connect(int sockfd, struct sockaddr * serv_addr, int addrlen);
        //用于建立与指定socket的连接。
        //返回值:成功则返回0, 失败返回-1, 错误原因存于errno 中.
        
        参数:
        sockfd:标识一个套接字。
        serv_addr:套接字s想要连接的主机地址和端口号。
        addrlen:name缓冲区的长度。
        

        二、I/O多路复用

        1.select模型

         int select(int nfds, fd_set* readfds, fd_set* writefds, fd_set* errorfds, struct timeval* timeout);
        //select函数用于检测一组socket中是否有事件就绪,它能够监视我们需要监视的文件描述符的变化情况——读写或是异常 。
        //返回值:准备就绪的描述符数,若超时则返回0,若出错则返回-1。
        
        1.nfds:select监视的文件句柄数,视进程中打开的文件数而定,一般设为你要监视各文件中的最大文件号加一。
        2.readfds:select监视的可读文件句柄集合。
        3.writefds: select监视的可写文件句柄集合。
        4.exceptfds:select监视的异常文件句柄集合。
        5.timeout:本次select()的超时结束时间。(见/usr/sys/select.h,可精确至百万分之一秒!)
        
        FD_ZERO(fd_set *fdset):清空fdset与所有文件句柄的联系。
        FD_SET(int fd, fd_set *fdset):建立文件句柄fd与fdset的联系。
        FD_CLR(int fd, fd_set *fdset):清除文件句柄fd与fdset的联系。
        FD_ISSET(int fd, fd_set *fdset):检查fdset联系的文件句柄fd是否可读写,当>0表示可读写。
        

        存在的问题:

        (1)采用轮询的方式扫描bitmap,性能会随着socket数量增多而下降。

        (2)每次调用select(),需要拷贝bitmap。

        (3)bitmap的大小(单个进/线程打开的socket数量)由FD_SETSIZE宏设置,默认是1024个,可以修改,但是效率将降低。

        2.poll模型

        int poll(struct pollfd fds[], nfds_t nfds, int timeout);
        //用于监视多个文件描述符的状态变化。
        /*返回值:
        如果函数调用成功,则返回所有事件就绪的文件描述符个数。
        如果timeout时间耗尽,返回0。
        如果函数调用失败,返回-1,同时错误码会被设置。
        */
        struct pollfd {
        int fd; /*文件描述符*/
        short events; /* 等待的需要测试事件 */
        short revents; /* 实际发生了的事件,也就是返回结果 */
        };
        
        1.fds:指向一个结构体数组的指针,每个数组元素都是一个struct pollfd结构,用于指定测试某个给定的fd的条件。struct pollfd包含三个成员:fd(文件描述符),events(等待的事件),revents(实际发生的事件)。
        2.nfds:表示fds数组的长度。
        3.timeout:表示poll函数的超时时间,单位是毫秒(ms)。如果timeout的取值为-1,则poll调用后进行阻塞等待,直到被监视的某个文件描述符上的某个事件就绪;如果timeout的取值为0,则poll调用之后进行非阻塞等待,无论被监视的文件描述符上的事件是否就绪,poll检测之后都会立即返回;如果timeout的取值为特定的时间值,则poll调用后在指定的时间内进行阻塞等待,如果被监视的文件描述符上一直没有事件就绪,则在该时间后poll进行超时返回。
        

        存在的问题:

        (1)poll的数据结构是数组,转入内核后转换成了链表。

        (2)每调用一次poll拷贝一次结构体数组(select()需要拷贝两次bitmap)。

        (3)监视的连接数没有1024的限制。但是使用遍历的方法,监视越多,效率越低。

        3.epoll模型

        int epoll_create(int size)
        //创建一个epoll的句柄,size用来告诉内核这个监听的数目一共有多大。成功时返回epoll文件描述符,失败时返回-1。
        int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event)
        //epoll的事件注册函数,它不同与select()是在监听事件时告诉内核要监听什么类型的事件,而是在这里先注册要监听的事件类型。第一个参数是epoll_create()的返回值,第二个参数表示动作,用三个宏来表示:EPOLL_CTL_ADD:注册新的fd到epfd中;EPOLL_CTL_MOD:修改已经注册的fd的监听事件;EPOLL_CTL_DEL:从epfd中删除一个fd;第三个参数是需要监听的fd,第四个参数是告诉内核需要监听什么事。
        int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout)
        //等待事件的产生,类似于select()调用。参数events用来存储内核得到的事件的集合,maxevents告知内核events的大小,timeout是超时时间。成功时返回有多少文件描述符就绪,时间到时返回0,出错时返回-1。
        

        内容来自b站C++网络编程,从Socket基础到Epoll,百度。

微信扫一扫加客服

微信扫一扫加客服

点击启动AI问答
Draggable Icon