socket 02

作者: 81bad73e9053 | 来源:发表于2017-02-23 21:01 被阅读40次

    shutdown文件夹代码

    为什么要做成三次握手:相互确认身份,tcpip是全双工双通道协议

    1.僵死进程的产生

    一个进程在调用exit命令结束自己生命的时候其实并没有真正被销毁,而是留下一个称为僵死进程的数据结构,在linux进程的状态中,僵死进程是非常特殊的一种,它已经放弃了几乎所有的内存空间,没有任何执行代码,也不能被调度,仅仅是在进程列表中保留 一个位置,记录该进程退出状态等信息供其他进程收集,除此之外,僵死进程不再占用任何内存空间,它需要它的父进程来为他收尸,如果它的父进程没有安装sigchld信号处理函数调用wait或者waitpid等待子进程结束,又没有显示的忽略这个信号,那么它就一直保持僵死状态,如果父进程结束了,那么init进程会接收这个子进程,为他收尸,它就可以被清除。

    2.waitpid

    Paste_Image.png Paste_Image.png

    3.避免僵死进程

    Paste_Image.png Paste_Image.png

    客户端断开连接,同时发送五个SIGCHLD信号到服务端,但是sigchild是不可靠信号,父进程不一定能收到5个sigchild信号

    避免僵尸进程的方法

    Paste_Image.png

    4.tcp/ip协议的十一种状态

    Paste_Image.png Paste_Image.png
    • 服务端调用socket初始化并通过bind绑定接口然后再调用listen函数使得服务端进入LISTEN状态
    • 服务端调用accept函数,进入SYN_RCVD状态,accept阻塞等待客户端连接
    • 客户端调用connect函数,客户端进入SYN_SENT状态
    • connect发送一个syn a到服务端,服务端回一个syn b ack a+1,客户端通过判断a+1确认后进入ESTABLISHED状态,同时发给服务端一个ack b+1
    • 服务端接收到b+1确认后进入ESTABLISHED状态
    • 为什么会有三次握手?因为tcpip是全双工协议,需要双方都确认身份。
    • 客户端和服务端都可以先close,先调用close的那一端他的socket的状态最终会成为TIME_WAIT状态
    • 为什么主动关闭套接字的那一方需要进入TIME_WAIT状态?先关闭的那一端并不是进入closed状态是因为需要发送一个ack y+1到对等方让对等方完全关闭,在网络环境下并不能保证这个ack发送成功,在TIME_WAIT状态下可以多次重发,让最后一个确认包LAST_ACK得到真正的确认
    • 为什么双方都需要显示的close?
    • 半链接状态FIN_WAIT_2,调用close之后,进入FIN_WAIT_1状态,在对等方读到0的时候,tcpip协议会回一个ack,然后进入半链接状态。

    6.sigpipe

    如果对方socket已关闭,对等方再发写数据,则会产生SIGPIPE信号

    • SIGPIPE信号会让进程终止(man 7 signal,阅读SIGPIPE默认ACT)
    • 往一个已经接收FIN的套接中写是允许的,接收到FIN仅仅代表对方不再发送数据。
    • 在收到RST段之后,如果再调用write就会产生SIGPIPE信号,对于这个信号的处理我们通常忽略即可。
      signal(SIGPIPE, SIG_IGN);
    Paste_Image.png

    结论:对SIGPIPE处理方法:
    1)忽略该信号即可signal(SIGPIPE, SIG_IGN);
    2)捕捉。改变默认行为

    7.close和shutdown的区别

    Paste_Image.png
    ssize_t readn(int fd, void *buf, size_t count)
    {
        size_t nleft = count;
        ssize_t nread;
        char *bufp = (char*)buf;
    
        while (nleft > 0)
        {
            if ((nread = read(fd, bufp, nleft)) < 0)
            {
                if (errno == EINTR)
                    continue;
                return -1;
            }
            else if (nread == 0)
                return count - nleft;
    
            bufp += nread;
            nleft -= nread;
        }
    
        return count;
    }
    
    ssize_t writen(int fd, const void *buf, size_t count)
    {
        size_t nleft = count;
        ssize_t nwritten;
        char *bufp = (char*)buf;
    
        while (nleft > 0)
        {
            if ((nwritten = write(fd, bufp, nleft)) < 0)
            {
                if (errno == EINTR)
                    continue;
                return -1;
            }
            else if (nwritten == 0)
                continue;
    
            bufp += nwritten;
            nleft -= nwritten;
        }
    
        return count;
    }
    
    ssize_t recv_peek(int sockfd, void *buf, size_t len)
    {
        while (1)
        {
            int ret = recv(sockfd, buf, len, MSG_PEEK);
            if (ret == -1 && errno == EINTR)
                continue;
            return ret;
        }
    }
    
    ssize_t readline(int sockfd, void *buf, size_t maxline)
    {
        int ret;
        int nread;
        char *bufp = buf;
        int nleft = maxline;
        while (1)
        {
            ret = recv_peek(sockfd, bufp, nleft);
            if (ret < 0)
                return ret;
            else if (ret == 0)
                return ret;
    
            nread = ret;
            int i;
            for (i=0; i<nread; i++)
            {
                if (bufp[i] == '\n')
                {
                    ret = readn(sockfd, bufp, i+1);
                    if (ret != i+1)
                        exit(EXIT_FAILURE);
    
                    return ret;
                }
            }
    
            if (nread > nleft)
                exit(EXIT_FAILURE);
    
            nleft -= nread;
            ret = readn(sockfd, bufp, nread);
            if (ret != nread)
                exit(EXIT_FAILURE);
    
            bufp += nread;
        }
    
        return -1;
    }
    
    void echo_srv(int conn)
    {
        char recvbuf[1024];
            while (1)
            {
                    memset(recvbuf, 0, sizeof(recvbuf));
                    int ret = readline(conn, recvbuf, 1024); 
                    if (ret == -1)
                        ERR_EXIT("readline");
                    if (ret == 0)
                    {
                        printf("client close\n");
                        break;
                    }
                    
                    if (recvbuf[0] == '2')  //注意2 一共2处。。。。
                    {
                        //close(conn); //11111111111
                        //close(conn); //11111111111
                        shutdown(conn, SHUT_WR);
                    }
                    
            
                    fputs(recvbuf, stdout);
                    writen(conn, recvbuf, strlen(recvbuf));
                    
                 
            }
    }
    
    void handle_sigchld(int sig)
    {   
        int mypid = 0;
        
        while ( (mypid=waitpid(-1, NULL, WNOHANG)) >0 )
        {
            printf("孩子退出,父进程要收尸:%d \n", mypid);
        }
    }
    
    
    int main(void)
    {  
        signal(SIGCHLD, handle_sigchld);
        int listenfd;
        if ((listenfd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) 
            ERR_EXIT("socket");
    
        struct sockaddr_in servaddr;
        memset(&servaddr, 0, sizeof(servaddr));
        servaddr.sin_family = AF_INET;
        servaddr.sin_port = htons(8001); 
        servaddr.sin_addr.s_addr = inet_addr("127.0.0.1"); 
    
        int on = 1;
        if (setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0)
            ERR_EXIT("setsockopt");
    
        if (bind(listenfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0)
            ERR_EXIT("bind");
            
        if (listen(listenfd, SOMAXCONN) < 0)
            ERR_EXIT("listen");
    
        struct sockaddr_in peeraddr;
        socklen_t peerlen = sizeof(peeraddr);
        int conn;
    
        pid_t pid;
        while (1)
        {
            if ((conn = accept(listenfd, (struct sockaddr*)&peeraddr, &peerlen)) < 0)
                ERR_EXIT("accept");
    
            printf("ip=%s port=%d\n", inet_ntoa(peeraddr.sin_addr), ntohs(peeraddr.sin_port));
    
            pid = fork();
            if (pid == -1)
                ERR_EXIT("fork");
            if (pid == 0)
            {
                close(listenfd);
                echo_srv(conn);
                exit(EXIT_SUCCESS);
            }
            else
            {
                //close(conn);  //注意1 
            } 
        } 
        return 0;
    }
    
    ssize_t readn(int fd, void *buf, size_t count)
    {
            size_t nleft = count;
            ssize_t nread;
            char *bufp = (char*)buf;
    
            while (nleft > 0)
            {
                    if ((nread = read(fd, bufp, nleft)) < 0)
                    {
                            if (errno == EINTR)
                                    continue;
                            return -1;
                    }
                    else if (nread == 0)
                            return count - nleft;
    
                    bufp += nread;
                    nleft -= nread;
            }
    
            return count;
    }
    
    ssize_t writen(int fd, const void *buf, size_t count)
    {
            size_t nleft = count;
            ssize_t nwritten;
            char *bufp = (char*)buf;
    
            while (nleft > 0)
            {
                    if ((nwritten = write(fd, bufp, nleft)) < 0)
                    {
                            if (errno == EINTR)
                                    continue;
                            return -1;
                    }
                    else if (nwritten == 0)
                            continue;
    
                    bufp += nwritten;
                    nleft -= nwritten;
            }
    
            return count;
    }
    
    ssize_t recv_peek(int sockfd, void *buf, size_t len)
    {
            while (1)
            {
                    int ret = recv(sockfd, buf, len, MSG_PEEK);
                    if (ret == -1 && errno == EINTR)
                            continue;
                    return ret;
            }
    }
    
    
    ssize_t readline(int sockfd, void *buf, size_t maxline)
    {
            int ret;
            int nread;
            char *bufp = buf;
            int nleft = maxline;
            while (1)
            {
                    ret = recv_peek(sockfd, bufp, nleft);
                    if (ret < 0)
                            return ret;
                    else if (ret == 0)
                            return ret;
    
                    nread = ret;
                    int i;
                    for (i=0; i<nread; i++)
                    {
                            if (bufp[i] == '\n')
                            {
                                    ret = readn(sockfd, bufp, i+1);
                                    if (ret != i+1)
                                            exit(EXIT_FAILURE);
    
                                    return ret;
                            }
                    }
    
                    if (nread > nleft)
                            exit(EXIT_FAILURE);
    
                    nleft -= nread;
                    ret = readn(sockfd, bufp, nread);
                    if (ret != nread)
                            exit(EXIT_FAILURE);
    
                    bufp += nread;
            }
    
            return -1;
    }
    
    void echo_cli(int sock)
    {
        char sendbuf[1024] = {0};
            char recvbuf[1024] = {0};
            while (fgets(sendbuf, sizeof(sendbuf), stdin) != NULL)
            {
                    writen(sock, sendbuf, strlen(sendbuf));
    
                    int ret = readline(sock, recvbuf, sizeof(recvbuf));
                    if (ret == -1)
                            ERR_EXIT("readline");
                    else if (ret == 0)
                    {
                            printf("client close\n");
                            break;
                    }
    
                    fputs(recvbuf, stdout);
                    memset(sendbuf, 0, sizeof(sendbuf));
                    memset(recvbuf, 0, sizeof(recvbuf));
            }
    
            close(sock);
    }
    
    int main(void)
    {
        int sock;
        if ((sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)
            ERR_EXIT("socket");
    
        struct sockaddr_in servaddr;
        memset(&servaddr, 0, sizeof(servaddr));
        servaddr.sin_family = AF_INET;
        servaddr.sin_port = htons(8001);
        servaddr.sin_addr.s_addr = inet_addr("127.0.0.1");
    
        if (connect(sock, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0)
            ERR_EXIT("connect");
    
        struct sockaddr_in localaddr;
        socklen_t addrlen = sizeof(localaddr);
        if (getsockname(sock, (struct sockaddr*)&localaddr, &addrlen) < 0)
            ERR_EXIT("getsockname");
    
        printf("ip=%s port=%d\n", inet_ntoa(localaddr.sin_addr), ntohs(localaddr.sin_port));
    
    
        echo_cli(sock);
    
        return 0;
    }
    

    8.io模型

    8.1 阻塞io模型

    Paste_Image.png
    • 1.当上层应用app1调用recv系统调用时,如果对等方没有发送数据(缓冲区没有数据),上层应用app1将阻塞(默认行为,被linux内核阻塞);
    • 2.当对等方发送了数据,linux内核recv端缓冲区,有数据后,内核会把数据copy给用户空间。然后上层应用app1解除阻塞,执行下一步操作。

    8.2 非阻塞io

    Paste_Image.png
    • 1: 上层应用程序app2将套接字设置成非阻塞模式。
    • 2: 上层应用程序app2轮询调用recv函数,接受数据。若缓冲区没有数据,上层程序app2不会阻塞,recv返回值为-1,错误码是EWOULDBLOCK。
    • 3:上层应用程序不断轮询有没有数据到来。会造成上层应用忙等待。大量消耗CPU。很少直接用。应用范围小,一般和selectIO复用配合使用

    8.3 io复用

    Paste_Image.png
    • 1: 上层应用程序app3调用select机制(该机制有linux内核支持,避免了app3忙等待。),进行轮询文件描述符的状态变化。
    • 2:当select管理的文件描述符没有数据(或者状态没有变化时),上层应用程序app3也会阻塞。
    • 3:好处select机制可以管理多个文件描述符
    • 4:select可以看成一个管理者,用select来管理多个IO。
      一旦检测到的一个I/O或者多个IO,有我们感兴事件,发生,select函数将返回,返回值为检测到的事件个数。进而可以利用select相关api函数,操作具体事件。
    • 5:select函数可以设置等待时间,避免了上层应用程序app3,长期僵死。
    • 6: 和阻塞IO模型相比,selectI/O复用模型相当于提前阻塞了。等到有数据到来时,再调用recv就不会发生阻塞。

    8.4 信号驱动io模型

    Paste_Image.png
    • 1: 上层应用程序app4建立SIGIO信号处理程序。当缓冲区有数据到来,内核会发送信号告诉上层应用程序app4。
    • 2:上层应用程序app4接收到信号后,调用recv函数,因缓冲区有数据,recv函数一般不会阻塞。
    • 3:这种用于模型用的比较少,属于典型的“拉模式”。即:上层应用app4,需要调用recv函数把数据拉进来。
    • 4:这种方式的缺点是信号可能会有延时,信号处理的延时期间数据可能会发生变化

    8.5 异步io

    Paste_Image.png
    • 1:上层应用程序app5调用aio_read函数,同时提交一个应用层的缓冲区buf;调用完毕后,不会阻塞。上层应用程序app5可以继续其他任务。
    • 2:当tcpip协议缓冲区有数据时,linux主动的把内核数据copy到用户空间。然后再给上层应用app5发送信号;告诉app5数据有了,赶快处理吧!
    • 3:典型的“推模式”
    • 4: 效率最高的一种形式,上层应用程序app5有异步处理的能力(在linux内核的支持下,言外之意:处理其他任务的同时,也可支持IO通讯)。
    • 5:异步I/O指的是什么?
      上层应用程序app5,在也可以干别的活的时,可以接收数据(接受异步通信事件。===)异步命令来源)。与信号驱动IO模型,上层应用程序app5不需要调用recv函数。

    9.select

    allow a program to monitor multiple file descriptors, waiting until one or more of
    the file descriptors become "ready" for some class of I/O operation (e.g., input possible). A file
    descriptor is considered ready if it is possible to perform the corresponding I/O operation (e.g., read(2))
    without blocking.
    允许程序监控多个文件描述符,直到有一个或者多个文件描述符可用,所谓可用是指,这个文件描述符在执行io操作的时候不会被阻塞

           /* According to POSIX.1-2001 */
           #include <sys/select.h> 
           /* According to earlier standards */
           #include <sys/time.h>
           #include <sys/types.h>
           #include <unistd.h>
           //nfds,最大的文件描述符加1(比如集合里有1,2,7,那么n就是8)
           int select(int nfds, fd_set *readfds, fd_set *writefds,
                      fd_set *exceptfds, struct timeval *timeout);
    
     •int FD_ISSET(int fd, fd_set *fdset);
    –测试某个描述符是否在集合内
    •void FD_CLR(int fd, fd_set *fdset);
    –从集合内把一个描述符移除
    •void FD_SET(int fd, fd_set *fdset);
    –把一个描述符加入集合
    •void FD_ZERO(fd_set *fdset);
    –清空描述符集合。
    
    Paste_Image.png

    当那个文件描述符可用的时候,那一位就变成1

    Paste_Image.png Paste_Image.png

    10.实现说明

    select()实现说明

    • 调用select时通过参数告诉内核用户感兴趣的IO描述符
    • 关心的IO状态: 输入,输出或错误
    • 返回之后内核告诉调用者多个描述符准备好了
    • 哪些描述符发生了变化
    • 调用返回后对准备好的描述符调用读写操作
    • 不关心的描述符集合传NULL

    11. select实例:监控输入

    int main(void) 
    {
           fd_set rfds;
           struct timeval tv;
           int retval; 
           /* Watch stdin (fd 0) to see when it has input. */
           FD_ZERO(&rfds);
           
           while (1)
           {
                FD_SET(0, &rfds);
        
               /* Wait up to five seconds. */
               tv.tv_sec = 2;
               tv.tv_usec = 0; 
               
               retval = select(1, &rfds, NULL, NULL, &tv);
               /* Don't rely on the value of tv now! */ 
               if (retval == -1)
                   perror("select() -1");
               else if (retval)
                {
                     printf("Data is available now.\n");
                }
                  
                   /* FD_ISSET(0, &rfds) will be true. */   
               else
                   printf("No data within five seconds.\n");
            } 
            return 0;
    }
    

    12. 封装

    void activate_nonblock(int fd);
    void deactivate_nonblock(int fd);
    
    int read_timeout(int fd, unsigned int wait_seconds);
    int write_timeout(int fd, unsigned int wait_seconds);
    int accept_timeout(int fd, struct sockaddr_in *addr, unsigned int wait_seconds);
    int connect_timeout(int fd, struct sockaddr_in *addr, unsigned int wait_seconds);
    
    
    ssize_t readn(int fd, void *buf, size_t count);
    ssize_t writen(int fd, const void *buf, size_t count);
    ssize_t recv_peek(int sockfd, void *buf, size_t len);
    ssize_t readline(int sockfd, void *buf, size_t maxline);
    

    实现

    /**
     * read_timeout - 读超时检测函数,不含读操作
     * @fd: 文件描述符
     * @wait_seconds: 等待超时秒数,如果为0表示不检测超时
     * 成功(未超时)返回0,失败返回-1,超时返回-1并且errno = ETIMEDOUT
     */
    int read_timeout(int fd, unsigned int wait_seconds)
    {
        int ret = 0;
        if (wait_seconds > 0)
        {
            fd_set read_fdset;
            struct timeval timeout;
    
            FD_ZERO(&read_fdset);
            FD_SET(fd, &read_fdset);
    
            timeout.tv_sec = wait_seconds;
            timeout.tv_usec = 0;
            
            //select返回值三态
            //1 若timeout时间到(超时),没有检测到读事件 ret返回=0
            //2 若ret返回<0 &&  errno == EINTR 说明select的过程中被别的信号中断(可中断睡眠原理)
            //2-1 若返回-1,select出错
            //3 若ret返回值>0 表示有read事件发生,返回事件发生的个数
            
            do
            {
                ret = select(fd + 1, &read_fdset, NULL, NULL, &timeout);
            } while (ret < 0 && errno == EINTR);
    
            if (ret == 0)
            {
                ret = -1;
                errno = ETIMEDOUT;
            }
            else if (ret == 1)
                ret = 0;
        }
    
        return ret;
    }
    
    /**
     * write_timeout - 写超时检测函数,不含写操作
     * @fd: 文件描述符
     * @wait_seconds: 等待超时秒数,如果为0表示不检测超时
     * 成功(未超时)返回0,失败返回-1,超时返回-1并且errno = ETIMEDOUT
     */
    int write_timeout(int fd, unsigned int wait_seconds)
    {
        int ret = 0;
        if (wait_seconds > 0)
        {
            fd_set write_fdset;
            struct timeval timeout;
    
            FD_ZERO(&write_fdset);
            FD_SET(fd, &write_fdset);
    
            timeout.tv_sec = wait_seconds;
            timeout.tv_usec = 0;
            do
            {
                ret = select(fd + 1, NULL, &write_fdset, NULL, &timeout);
            } while (ret < 0 && errno == EINTR);
    
            if (ret == 0)
            {
                ret = -1;
                errno = ETIMEDOUT;
            }
            else if (ret == 1)
                ret = 0;
        }
    
        return ret;
    }
    
    /**
     * accept_timeout - 带超时的accept
     * @fd: 套接字
     * @addr: 输出参数,返回对方地址
     * @wait_seconds: 等待超时秒数,如果为0表示正常模式
     * 成功(未超时)返回已连接套接字,超时返回-1并且errno = ETIMEDOUT
     */
    int accept_timeout(int fd, struct sockaddr_in *addr, unsigned int wait_seconds)
    {
        int ret;
        socklen_t addrlen = sizeof(struct sockaddr_in);
    
        if (wait_seconds > 0)
        {
            fd_set accept_fdset;
            struct timeval timeout;
            FD_ZERO(&accept_fdset);
            FD_SET(fd, &accept_fdset);
            timeout.tv_sec = wait_seconds;
            timeout.tv_usec = 0;
            do
            {
                ret = select(fd + 1, &accept_fdset, NULL, NULL, &timeout);
            } while (ret < 0 && errno == EINTR);
            if (ret == -1)
                return -1;
            else if (ret == 0)
            {
                errno = ETIMEDOUT;
                return -1;
            }
        }
    
        //一但检测出 有select事件发生,表示对等方完成了三次握手,客户端有新连接建立
        //此时再调用accept将不会堵塞
        if (addr != NULL)
            ret = accept(fd, (struct sockaddr*)addr, &addrlen); //返回已连接套接字
        else
            ret = accept(fd, NULL, NULL);
        if (ret == -1)
            ERR_EXIT("accept");
    
        return ret;
    }
    
    /**
     * activate_noblock - 设置I/O为非阻塞模式
     * @fd: 文件描符符
     */
    void activate_nonblock(int fd)
    {
        int ret;
        int flags = fcntl(fd, F_GETFL);
        if (flags == -1)
            ERR_EXIT("fcntl");
    
        flags |= O_NONBLOCK;
        ret = fcntl(fd, F_SETFL, flags);
        if (ret == -1)
            ERR_EXIT("fcntl");
    }
    
    /**
     * deactivate_nonblock - 设置I/O为阻塞模式
     * @fd: 文件描符符
     */
    void deactivate_nonblock(int fd)
    {
        int ret;
        int flags = fcntl(fd, F_GETFL);
        if (flags == -1)
            ERR_EXIT("fcntl");
    
        flags &= ~O_NONBLOCK;
        ret = fcntl(fd, F_SETFL, flags);
        if (ret == -1)
            ERR_EXIT("fcntl");
    }
    
    
    /**
     * connect_timeout - connect
     * @fd: 套接字
     * @addr: 要连接的对方地址
     * @wait_seconds: 等待超时秒数,如果为0表示正常模式
     * 成功(未超时)返回0,失败返回-1,超时返回-1并且errno = ETIMEDOUT
     */
    int connect_timeout(int fd, struct sockaddr_in *addr, unsigned int wait_seconds)
    {
        int ret;
        socklen_t addrlen = sizeof(struct sockaddr_in);
    
        if (wait_seconds > 0)
            activate_nonblock(fd);
    
        ret = connect(fd, (struct sockaddr*)addr, addrlen);
        if (ret < 0 && errno == EINPROGRESS)
        {
            //printf("11111111111111111111\n");
            fd_set connect_fdset;
            struct timeval timeout;
            FD_ZERO(&connect_fdset);
            FD_SET(fd, &connect_fdset);
            timeout.tv_sec = wait_seconds;
            timeout.tv_usec = 0;
            do
            {
                // 一但连接建立,则套接字就可写  所以connect_fdset放在了写集合中
                ret = select(fd + 1, NULL, &connect_fdset, NULL, &timeout);
            } while (ret < 0 && errno == EINTR);
            if (ret == 0)
            {
                ret = -1;
                errno = ETIMEDOUT;
            }
            else if (ret < 0)
                return -1;
            else if (ret == 1)
            {
                //printf("22222222222222222\n");
                /* ret返回为1(表示套接字可写),可能有两种情况,一种是连接建立成功,一种是套接字产生错误,*/
                /* 此时错误信息不会保存至errno变量中,因此,需要调用getsockopt来获取。 */
                int err;
                socklen_t socklen = sizeof(err);
                int sockoptret = getsockopt(fd, SOL_SOCKET, SO_ERROR, &err, &socklen);
                if (sockoptret == -1)
                {
                    return -1;
                }
                if (err == 0)
                {
                    //printf("3333333333333\n");
                    ret = 0;
                }
                else
                {
                    //printf("4444444444444444:%d\n", err);
                    errno = err;
                    ret = -1;
                }
            }
        }
        if (wait_seconds > 0)
        {
            deactivate_nonblock(fd);
        }
        return ret;
    }
    
    /**
     * readn - 读取固定字节数
     * @fd: 文件描述符
     * @buf: 接收缓冲区
     * @count: 要读取的字节数
     * 成功返回count,失败返回-1,读到EOF返回<count
     */
    ssize_t readn(int fd, void *buf, size_t count)
    {
        size_t nleft = count;
        ssize_t nread;
        char *bufp = (char*)buf;
    
        while (nleft > 0)
        {
            if ((nread = read(fd, bufp, nleft)) < 0)
            {
                if (errno == EINTR)
                    continue;
                return -1;
            }
            else if (nread == 0)
                return count - nleft;
    
            bufp += nread;
            nleft -= nread;
        }
    
        return count;
    }
    
    /**
     * writen - 发送固定字节数
     * @fd: 文件描述符
     * @buf: 发送缓冲区
     * @count: 要读取的字节数
     * 成功返回count,失败返回-1
     */
    ssize_t writen(int fd, const void *buf, size_t count)
    {
        size_t nleft = count;
        ssize_t nwritten;
        char *bufp = (char*)buf;
    
        while (nleft > 0)
        {
            if ((nwritten = write(fd, bufp, nleft)) < 0)
            {
                if (errno == EINTR)
                    continue;
                return -1;
            }
            else if (nwritten == 0)
                continue;
    
            bufp += nwritten;
            nleft -= nwritten;
        }
    
        return count;
    }
    
    /**
     * recv_peek - 仅仅查看套接字缓冲区数据,但不移除数据
     * @sockfd: 套接字
     * @buf: 接收缓冲区
     * @len: 长度
     * 成功返回>=0,失败返回-1
     */
    ssize_t recv_peek(int sockfd, void *buf, size_t len)
    {
        while (1)
        {
            int ret = recv(sockfd, buf, len, MSG_PEEK);
            if (ret == -1 && errno == EINTR)
                continue;
            return ret;
        }
    }
    
    /**
     * readline - 按行读取数据
     * @sockfd: 套接字
     * @buf: 接收缓冲区
     * @maxline: 每行最大长度
     * 成功返回>=0,失败返回-1
     */
    ssize_t readline(int sockfd, void *buf, size_t maxline)
    {
        int ret;
        int nread;
        char *bufp = buf;
        int nleft = maxline;
        while (1)
        {
            ret = recv_peek(sockfd, bufp, nleft);
            if (ret < 0)
                return ret;
            else if (ret == 0)
                return ret;
    
            nread = ret;
            int i;
            for (i=0; i<nread; i++)
            {
                if (bufp[i] == '\n')
                {
                    ret = readn(sockfd, bufp, i+1);
                    if (ret != i+1)
                        exit(EXIT_FAILURE);
    
                    return ret;
                }
            }
    
            if (nread > nleft)
                exit(EXIT_FAILURE);
    
            nleft -= nread;
            ret = readn(sockfd, bufp, nread);
            if (ret != nread)
                exit(EXIT_FAILURE);
    
            bufp += nread;
        }
    
        return -1;
    }
    

    相关文章

      网友评论

          本文标题:socket 02

          本文链接:https://www.haomeiwen.com/subject/rctiwttx.html