socket 02

shutdown文件夹代码

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

1.僵死进程的产生

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

2.waitpid

Paste_Image.png
Paste_Image.png

3.避免僵死进程

socket 02_第1张图片
Paste_Image.png
socket 02_第2张图片
Paste_Image.png

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

避免僵尸进程的方法

socket 02_第3张图片
Paste_Image.png

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

socket 02_第4张图片
Paste_Image.png
socket 02_第5张图片
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);
socket 02_第6张图片
Paste_Image.png

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

7.close和shutdown的区别

socket 02_第7张图片
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 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 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模型

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

8.2 非阻塞io

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

8.3 io复用

socket 02_第10张图片
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模型

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

8.5 异步io

socket 02_第12张图片
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  
       /* According to earlier standards */
       #include 
       #include 
       #include 
       //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);
–清空描述符集合。
socket 02_第13张图片
Paste_Image.png

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

socket 02_第14张图片
Paste_Image.png
socket 02_第15张图片
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返回 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 nleft)
            exit(EXIT_FAILURE);

        nleft -= nread;
        ret = readn(sockfd, bufp, nread);
        if (ret != nread)
            exit(EXIT_FAILURE);

        bufp += nread;
    }

    return -1;
}

你可能感兴趣的:(socket 02)