csapp unix I/0 RIO 精简 web 服务器

系统调用 read、write

#include <unistd.h>
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <fcntl.h>

void unix_error(char *message) {
    fprintf(stderr, "%s: %s\n", message, strerror(errno));
    exit(0);
}

ssize_t Read(int fd, void *buf, size_t count) {
    ssize_t read_size;
    if ((read_size = read(fd, buf, count)) < 0) {
        unix_error("Read error!");
    }
    return read_size;
}

ssize_t Write(int fd, const void *buf, size_t count) {
    ssize_t write_size;
    if ((write_size = write(fd, buf, count)) < 0) {
        unix_error("Write error!");
    }
    return write_size;
}

char buf[100];

void test_read() {
    int operator = open("./my.txt", O_RDONLY, 0);
    // read: 成功返回读到的个数(0 表示触发了 EOF,或者第三个参数是 0),并且文件位置向后移动 read_size 个位置。
    // 出现不足值,可能的原因是触发了 EOF、从 pipe 或者终端相关联的包括终端(一次传送一个文本行)
    // 或者网络套接字(内部缓冲约束,网络延迟)读的、被信号中断了。除了 EOF,读磁盘不会出现不足值。
    // 出错的时候,返回 -1,并设置了 errno。此时文件位置的改动将是未定义的。
    int read_size = Read(operator, buf, sizeof(buf));

    printf("operator = %d\n", operator);    // 返回:operator = 3
    printf("read_size = %d\n", read_size);    // my.txt 里面是 12345 。返回:read_size = 5

    close(operator);
}

void test_write() {
    char buf[] = "456789";
    int operator = open("./my.txt", O_RDWR, 0);

    // write 出现不足值的情况:物理介质不够了、超过允许进程建立文件的最大值 RLIMIT_SIZE、发生了中断。
    int write_size = Write(operator, buf, sizeof(buf));  // sizeof(buf) 是 7,包含了 \0
    printf("write_size = %d\n", write_size);    // my.txt 里面是 12345 。返回:write_size = 7
    int i;
    char tmp;
    lseek(operator, 0, SEEK_SET);
    for (i = 0; i < 8; ++i) {
        Read(operator, &tmp, 1);
        printf("%d\t", tmp);     // 52    53    54    55    56    57    0     0   注意:最后的 0 是因为
    }                            // 最后一次迭代 tmp 没有赋值,还是上一次的值

    close(operator);
}

void main(void) {
    test_read();
    test_write();
}

不带缓冲区的 RIO

#include <unistd.h>
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <fcntl.h>

// 除了 EOF,读磁盘文件时,不会遇到不足值。
ssize_t rio_readn(int fd, void *usrbuf, size_t n) {
    size_t nleft = n;
    ssize_t nread;
    char *bufp = usrbuf;
    while (nleft > 0) {
        if ((nread = read(fd, bufp, nleft)) < 0) {
            if (errno == EINTR)
                nread = 0;
            else
                return -1;
        } else if (nread == 0)
            break;
        nleft -= nread;
        bufp += nread;
    }
    return (n - nleft);
}

// 写磁盘文件时候,绝不会遇到不足值。
ssize_t rio_writen(int fd, void *usrbuf, size_t n) {
    size_t nleft = n;
    ssize_t nwritten;
    char *bufp = usrbuf;
    while (nleft > 0) {
        if ((nwritten = write(fd, bufp, nleft)) <= 0) {
            if (errno == EINTR)
                nwritten = 0;
            else
                return -1;
        }
        nleft -= nwritten;
        bufp += nwritten;
    }
    return n;
}

char buf[100];

void test_read() {
    int operator = open("./my.txt", O_RDONLY, 0);

    int read_size = rio_readn(operator, buf, sizeof(buf));

    printf("operator = %d\n", operator);    // 返回:operator = 3
    printf("read_size = %d\n", read_size);    // my.txt 里面是 1234 。返回:read_size = 5

    close(operator);
}

void test_write() {
    char buf[] = "32";
    int operator = open("./my.txt", O_RDWR, 0);

    int write_size = rio_writen(operator, buf, sizeof(buf));
    printf("write_size = %d\n", write_size);    // my.txt 里面是 1234 。返回:write_size = 3
    int i;
    char tmp;
    lseek(operator, 0, SEEK_SET);
    for (i = 0; i < 8; ++i) {
        rio_readn(operator, &tmp, 1);
        printf("%d\t", tmp);     // 51      50      0       52      10      10      10      10
    }                            // 最后一次迭代 tmp 没有赋值,还是上一次 \n

    close(operator);
}

void main(void) {
    test_read();
    test_write();
}

带缓冲区的读、带缓冲区的文本行读、处理不足值的带缓冲区的读

#include <unistd.h>
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <fcntl.h>
#include <string.h>

#define RIO_BUF_SIZE 8192

typedef struct {
    int rio_fd;
    int rio_unread_cnt;
    char *rio_buf_pointer;
    char rio_buf[RIO_BUF_SIZE];
} rio_file_with_buf;

void rio_read_with_buf_init(rio_file_with_buf *p, int fd) {
    printf("%d\n", fd);
    p->rio_fd = fd;
    p->rio_unread_cnt = 0;
    p->rio_buf_pointer = p->rio_buf;
}

// 带缓冲区的读
static ssize_t rio_read(rio_file_with_buf *rp, char *usrbuf, size_t n) {
    int this_read_cnt;
    while (rp->rio_unread_cnt <= 0) {
        rp->rio_unread_cnt = read(rp->rio_fd, rp->rio_buf, sizeof(rp->rio_buf));
        if (rp->rio_unread_cnt < 0) {
            if (errno != EINTR) {
                return -1;
            }
        } else if (rp->rio_unread_cnt == 0) {
            return 0;
        } else {
            rp->rio_buf_pointer = rp->rio_buf;
        }
    }
    this_read_cnt = rp->rio_unread_cnt < n ? rp->rio_unread_cnt : n;
    memcpy(usrbuf, rp->rio_buf_pointer, this_read_cnt);
    rp->rio_buf_pointer += this_read_cnt;
    rp->rio_unread_cnt -= this_read_cnt;
    return this_read_cnt;
}

// 读取返回值 n 个字符,包含了换行符,放入缓冲区共 n+1 个字符,包含了填充的 null
// 如果超过 maxlen-1 个字节,则截断文本行,填充 null
// 可以和 rio_readnb 交叉执行。但是不能跟非缓冲区的 rio_readn 交叉。
ssize_t rio_readlineb(rio_file_with_buf *rp, void *usrbuf, size_t maxlen) {
    int n, rc;
    char c, *bufp = usrbuf;
    for (n = 1; n < maxlen; n++) {
        if ((rc = rio_read(rp, &c, 1)) == 1) {
            *bufp++ = c;
            if (c == '\n') {
                n++;
                break;
            }
        } else if (rc == 0) {
            if (n == 1)
                return 0;
            else
                break;
        } else
            return -1;
    }
    *bufp = 0;
    return n - 1;
}


// 带缓冲区的读,无不足值
ssize_t rio_readnb(rio_file_with_buf *rp, void *usrbuf, size_t n) {
    size_t nleft = n;
    ssize_t nread;
    char *bufp = usrbuf;
    while (nleft > 0) {
        if ((nread = rio_read(rp, bufp, nleft)) < 0)
            return -1;
        else if (nread == 0) {
            break;
        }
        nleft -= nread;
        bufp += nread;
    }
    return (n - nleft);
}

rio_file_with_buf riob;

void test_rio_read() {
    ssize_t operator = open("./my.txt", O_RDONLY, 0);
    char buf[20];
    rio_read_with_buf_init(&riob, operator);
    ssize_t read_size = rio_read(&riob, buf, sizeof(buf));

    printf("operator = %d\n", operator);    // 返回:operator = 3
    printf("read_size = %d\n", read_size);    // my.txt 里面是 1234 。返回:read_size = 5

    close(operator);
}

void test_rio_readlineb() {
    ssize_t operator = open("./my.txt", O_RDONLY, 0);

    char buf[20];
    rio_read_with_buf_init(&riob, operator);
    ssize_t read_size = rio_read(&riob, buf, sizeof(buf));

    printf("operator = %d\n", operator);    // 返回:operator = 3
    printf("read_size = %d\n", read_size);    // my.txt 里面是 1234 。返回:read_size = 5

    close(operator);
}

void test_rio_readnb() {
    ssize_t operator = open("./my.txt", O_RDONLY, 0);

    char buf[20];
    rio_read_with_buf_init(&riob, operator);
    ssize_t read_size = rio_read(&riob, buf, sizeof(buf));

    printf("operator = %d\n", operator);    // 返回:operator = 3
    printf("read_size = %d\n", read_size);    // my.txt 里面是 1234 。返回:read_size = 5

    close(operator);
}


void main(void) {
    // my.txt: 就一个字符2
    test_rio_read();
    test_rio_readlineb();
    test_rio_readnb();
}
/**
输出:
3
operator = 3
read_size = 2
3
operator = 3
read_size = 2
3
operator = 3
read_size = 2
*/

getaddrinfo 和 getnameinfo 实现 nslookup

#include <unistd.h>
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <fcntl.h>
#include <string.h>
#include <sys/stat.h>
#include <netdb.h>
#include <sys/socket.h>

#define MAXLINESIZE 8192

int main(int argc, char **argv) {
    struct addrinfo *p, *listp, hints;
    char buf[MAXLINESIZE];
    int rc, flags;
    if (argc != 2) {
        printf("Please, input this style:command-name domain-name!\n");
        exit(0);
    }
    memset(&hints, 0, sizeof(struct addrinfo));
    hints.ai_family = AF_INET;
    hints.ai_socktype = SOCK_STREAM;
    if ((rc = getaddrinfo(argv[1], NULL, &hints, &listp))!=0) {
        fprintf(stderr, "Error in getaddrinfo:%s\n", gai_strerror(rc));
        exit(1);
    }

    flags = NI_NUMERICHOST;
    for (p = listp;  p ; p=p->ai_next) {
        getnameinfo(p->ai_addr, p->ai_addrlen, buf, MAXLINESIZE, NULL, 0, flags);
        printf("%s\n", buf);
    }
    freeaddrinfo(listp);
    exit(0);
}

实现 echo socket

// server
#include <unistd.h>
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <fcntl.h>
#include <string.h>
#include <sys/stat.h>
#include <netdb.h>
#include <sys/socket.h>
#include <sys/types.h>

#define MAXLINESIZE 8192

#define RIO_BUF_SIZE 8192

typedef struct sockaddr SA;

void app_error(char *msg) /* Application error */
{
    fprintf(stderr, "%s!\n", msg);
    exit(0);
}

typedef struct {
    int rio_fd;
    int rio_unread_cnt;
    char *rio_buf_pointer;
    char rio_buf[RIO_BUF_SIZE];
} rio_file_with_buf;

void gai_error(int code, char *msg) {
    fprintf(stderr, "%s: %s\n", msg, gai_strerror(code));
    exit(0);
}

void Getaddrinfo(const char *node, const char *service, const struct addrinfo *hints, struct addrinfo **res) {
    int rc;
    if ((rc = getaddrinfo(node, service, hints, res)) != 0) {
        gai_error(rc, "Getaddrinfo error: ");
    }
}

void unix_error(char *msg) /* Unix-style error */
{
    fprintf(stderr, "%s: %s\n", msg, strerror(errno));
    exit(0);
}

void Close(int fd) {
    int rc;

    if ((rc = close(fd)) < 0)
        unix_error("Close error!\n");
}

int open_clientfd(char *hostname, char *port) {
    int clientfd;
    struct addrinfo hints, *listp, *p;

    memset(&hints, 0, sizeof(struct addrinfo));
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_flags = AI_NUMERICSERV;// force the service argument in the getaddrinfo function to be a port number
    hints.ai_flags |= AI_ADDRCONFIG;
    Getaddrinfo(hostname, port, &hints, &listp);

    for (p = listp; p; p = p->ai_next) {
        if ((clientfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) < 0) continue;
        if (connect(clientfd, p->ai_addr, p->ai_addrlen) != -1) break;
        Close(clientfd);
    }

    freeaddrinfo(listp);

    if (!p) return -1;
    return clientfd;
}

void dns_error(char *msg) /* DNS-style error */
{
    fprintf(stderr, "%s: DNS error %d!\n", msg, h_errno);
    exit(0);
}

int Open_clientfd(char *hostname, char *port) {
    int rc;
    if ((rc = open_clientfd(hostname, port)) < 0) {
        if (rc == -1)
            unix_error("Open_clientfd Unix error!\n");
        else
            dns_error("Open_clientfd DNS error!\n");
    }
    return rc;
}

int open_listenfd(char *port) {
    struct addrinfo hints, *listp, *p;
    int listenfd, optval = 1;

    memset(&hints, 0, sizeof(struct addrinfo));
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_flags = AI_NUMERICSERV;
    hints.ai_flags |= AI_PASSIVE | AI_ADDRCONFIG;
    Getaddrinfo(NULL, port, &hints, &listp);

    for (p = listp; p; p->ai_next) {
        if ((listenfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) < 0)continue;
        setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, (const void *) &optval, sizeof(int));

        if (bind(listenfd, p->ai_addr, p->ai_addrlen) == 0)break;
        Close(listenfd);
    }

    freeaddrinfo(listp);
    if (!p)return -1;
    if (listen(listenfd, 1024) < 0) {
        Close(listenfd);
        return -1;
    }

    return listenfd;
}


void rio_read_with_buf_init(rio_file_with_buf *p, int fd) {
    p->rio_fd = fd;
    p->rio_unread_cnt = 0;
    p->rio_buf_pointer = p->rio_buf;
}

// 带缓冲区的读
static ssize_t rio_read(rio_file_with_buf *rp, char *usrbuf, size_t n) {
    int this_read_cnt;
    while (rp->rio_unread_cnt <= 0) {
        rp->rio_unread_cnt = read(rp->rio_fd, rp->rio_buf, sizeof(rp->rio_buf));
        if (rp->rio_unread_cnt < 0) {
            if (errno != EINTR) {
                return -1;
            }
        } else if (rp->rio_unread_cnt == 0) {
            return 0;
        } else {
            rp->rio_buf_pointer = rp->rio_buf;
        }
    }
    this_read_cnt = rp->rio_unread_cnt < n ? rp->rio_unread_cnt : n;
    memcpy(usrbuf, rp->rio_buf_pointer, this_read_cnt);
    rp->rio_buf_pointer += this_read_cnt;
    rp->rio_unread_cnt -= this_read_cnt;
    return this_read_cnt;
}

ssize_t rio_writen(int fd, void *usrbuf, size_t n) {
    size_t nleft = n;
    ssize_t nwritten;
    char *bufp = usrbuf;
    while (nleft > 0) {
        if ((nwritten = write(fd, bufp, nleft)) <= 0) {
            if (errno == EINTR)
                nwritten = 0;
            else
                return -1;
        }
        nleft -= nwritten;
        bufp += nwritten;
    }
    return n;
}

ssize_t rio_readlineb(rio_file_with_buf *rp, void *usrbuf, size_t maxlen) {
    int n, rc;
    char c, *bufp = usrbuf;
    for (n = 1; n < maxlen; n++) {
        if ((rc = rio_read(rp, &c, 1)) == 1) {
            *bufp++ = c;
            if (c == '\n') {
                n++;
                break;
            }
        } else if (rc == 0) {
            if (n == 1)
                return 0;
            else
                break;
        } else
            return -1;
    }
    *bufp = 0;
    return n - 1;
}


char *Fgets(char *ptr, int n, FILE *stream) {
    char *rptr;

    if (((rptr = fgets(ptr, n, stream)) == NULL) && ferror(stream))
        app_error("Fgets error");

    return rptr;
}

void Rio_writen(int fd, void *usrbuf, size_t n) {
    if (rio_writen(fd, usrbuf, n) != n)
        unix_error("Rio_writen error!\n");
}

void Fputs(const char *ptr, FILE *stream) {
    if (fputs(ptr, stream) == EOF)
        unix_error("Fputs error!\n");
}


ssize_t Rio_readlineb(rio_file_with_buf *rp, void *usrbuf, size_t maxlen) {
    ssize_t rc;

    if ((rc = rio_readlineb(rp, usrbuf, maxlen)) < 0)
        unix_error("Rio_readlineb error!\n");
    return rc;
}

int Open_listenfd(char *port) {
    int rc;

    if ((rc = open_listenfd(port)) < 0)
        unix_error("Open_listenfd error!\n");
    return rc;
}

void Getnameinfo(const struct sockaddr *sa, socklen_t salen, char *host,
                 size_t hostlen, char *serv, size_t servlen, int flags) {
    int rc;

    if ((rc = getnameinfo(sa, salen, host, hostlen, serv,
                          servlen, flags)) != 0)
        gai_error(rc, "Getnameinfo error: ");
}

void echo(int connfd) {
    size_t n;
    char buf[MAXLINESIZE];
    rio_file_with_buf rio;
    rio_read_with_buf_init(&rio, connfd);
    while ((n = Rio_readlineb(&rio, buf, MAXLINESIZE)) != 0) {
        printf("Received %d bytes\n", (int) n);
        Rio_writen(connfd, buf, n);
    }
}

int Accept(int s, struct sockaddr *addr, socklen_t *addrlen) {
    int rc;

    if ((rc = accept(s, addr, addrlen)) < 0)
        unix_error("Accept error!\n");
    return rc;
}


int main(int argc, char **argv) {
    int listenfd, connfd;
    socklen_t clientlen;
    struct sockaddr_storage clientaddr;
    char client_hostname[MAXLINESIZE], client_port[MAXLINESIZE];

    if (argc != 2) {
        printf("Please input the command like this: <command-name> <port>");
        exit(0);
    }
    listenfd = Open_listenfd(argv[1]);

    while (1) {
        clientlen = sizeof(struct sockaddr_storage);
        connfd = Accept(listenfd, (SA *) &clientaddr, &clientlen);
        Getnameinfo((SA *) &clientaddr, clientlen, client_hostname, MAXLINESIZE, client_port, MAXLINESIZE, 0);
        printf("Connect to (%s, %s)\n", client_hostname, client_port);
        echo(connfd);
        Close(connfd);
    }
}
// clent
#include <unistd.h>
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <fcntl.h>
#include <string.h>
#include <sys/stat.h>
#include <netdb.h>
#include <sys/socket.h>
#include <sys/types.h>

#define MAXLINESIZE 8192

#define RIO_BUF_SIZE 8192

void app_error(char *msg) /* Application error */
{
    fprintf(stderr, "%s!\n", msg);
    exit(0);
}

typedef struct {
    int rio_fd;
    int rio_unread_cnt;
    char *rio_buf_pointer;
    char rio_buf[RIO_BUF_SIZE];
} rio_file_with_buf;

void gai_error(int code, char *msg) {
    fprintf(stderr, "%s: %s\n", msg, gai_strerror(code));
    exit(0);
}

void Getaddrinfo(const char *node, const char *service, const struct addrinfo *hints, struct addrinfo **res) {
    int rc;
    if ((rc = getaddrinfo(node, service, hints, res)) != 0) {
        gai_error(rc, "Getaddrinfo error!\n");
    }
}

void unix_error(char *msg) /* Unix-style error */
{
    fprintf(stderr, "%s: %s\n", msg, strerror(errno));
    exit(0);
}

void Close(int fd) {
    int rc;

    if ((rc = close(fd)) < 0)
        unix_error("Close error!\n");
}

int open_clientfd(char *hostname, char *port) {
    int clientfd;
    struct addrinfo hints, *listp, *p;

    memset(&hints, 0, sizeof(struct addrinfo));
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_flags = AI_NUMERICSERV;// force the service argument in the getaddrinfo function to be a port number
    hints.ai_flags |= AI_ADDRCONFIG;
    Getaddrinfo(hostname, port, &hints, &listp);

    for (p = listp; p; p = p->ai_next) {
        if ((clientfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) < 0) continue;
        if (connect(clientfd, p->ai_addr, p->ai_addrlen) != -1) break;
        Close(clientfd);
    }

    freeaddrinfo(listp);

    if (!p) return -1;
    return clientfd;
}

void dns_error(char *msg) /* DNS-style error */
{
    fprintf(stderr, "%s: DNS error %d!\n", msg, h_errno);
    exit(0);
}

int Open_clientfd(char *hostname, char* port) {
    int rc;
    if ((rc = open_clientfd(hostname, port)) < 0) {
        if (rc == -1)
            unix_error("Open_clientfd Unix error!\n");
        else
            dns_error("Open_clientfd DNS error!\n");
    }
    return rc;
}

int open_listenfd(char *port) {
    struct addrinfo hints, *listp, *p;
    int listenfd, optval = 1;

    memset(&hints, 0, sizeof(struct addrinfo));
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_flags = AI_NUMERICSERV;
    hints.ai_flags |= AI_PASSIVE | AI_ADDRCONFIG;
    Getaddrinfo(NULL, port, &hints, &listp);

    for (p = listp; p; p->ai_next) {
        if ((listenfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) < 0)continue;
        setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, (const void *) &optval, sizeof(int));

        if (bind(listenfd, p->ai_addr, p->ai_addrlen) == 0)break;
        Close(listenfd);
    }

    freeaddrinfo(listp);
    if (!p)return -1;
    if (listen(listenfd, 1024) < 0) {
        Close(listenfd);
        return -1;
    }

    return listenfd;
}


void rio_read_with_buf_init(rio_file_with_buf *p, int fd) {
    printf("%d\n", fd);
    p->rio_fd = fd;
    p->rio_unread_cnt = 0;
    p->rio_buf_pointer = p->rio_buf;
}

// 带缓冲区的读
static ssize_t rio_read(rio_file_with_buf *rp, char *usrbuf, size_t n) {
    int this_read_cnt;
    while (rp->rio_unread_cnt <= 0) {
        rp->rio_unread_cnt = read(rp->rio_fd, rp->rio_buf, sizeof(rp->rio_buf));
        if (rp->rio_unread_cnt < 0) {
            if (errno != EINTR) {
                return -1;
            }
        } else if (rp->rio_unread_cnt == 0) {
            return 0;
        } else {
            rp->rio_buf_pointer = rp->rio_buf;
        }
    }
    this_read_cnt = rp->rio_unread_cnt < n ? rp->rio_unread_cnt : n;
    memcpy(usrbuf, rp->rio_buf_pointer, this_read_cnt);
    rp->rio_buf_pointer += this_read_cnt;
    rp->rio_unread_cnt -= this_read_cnt;
    return this_read_cnt;
}

ssize_t rio_writen(int fd, void *usrbuf, size_t n) {
    size_t nleft = n;
    ssize_t nwritten;
    char *bufp = usrbuf;
    while (nleft > 0) {
        if ((nwritten = write(fd, bufp, nleft)) <= 0) {
            if (errno == EINTR)
                nwritten = 0;
            else
                return -1;
        }
        nleft -= nwritten;
        bufp += nwritten;
    }
    return n;
}

ssize_t rio_readlineb(rio_file_with_buf *rp, void *usrbuf, size_t maxlen) {
    int n, rc;
    char c, *bufp = usrbuf;
    for (n = 1; n < maxlen; n++) {
        if ((rc = rio_read(rp, &c, 1)) == 1) {
            *bufp++ = c;
            if (c == '\n') {
                n++;
                break;
            }
        } else if (rc == 0) {
            if (n == 1)
                return 0;
            else
                break;
        } else
            return -1;
    }
    *bufp = 0;
    return n - 1;
}


char *Fgets(char *ptr, int n, FILE *stream) {
    char *rptr;

    if (((rptr = fgets(ptr, n, stream)) == NULL) && ferror(stream))
        app_error("Fgets error");

    return rptr;
}

void Rio_writen(int fd, void *usrbuf, size_t n) {
    if (rio_writen(fd, usrbuf, n) != n)
        unix_error("Rio_writen error!\n");
}

void Fputs(const char *ptr, FILE *stream) {
    if (fputs(ptr, stream) == EOF)
        unix_error("Fputs error!\n");
}


ssize_t Rio_readlineb(rio_file_with_buf *rp, void *usrbuf, size_t maxlen) {
    ssize_t rc;

    if ((rc = rio_readlineb(rp, usrbuf, maxlen)) < 0)
        unix_error("Rio_readlineb error!\n");
    return rc;
}

int main(int argc, char **argv) {
    int clientfd;
    char *host, *port, buf[MAXLINESIZE];
    rio_file_with_buf rio;
    if (argc != 3) {
        printf("Please input the command like this: <command-name> <host> <port>");
        exit(0);
    }

    host = argv[1];
    port = argv[2];

    clientfd = Open_clientfd(host, port);
    rio_read_with_buf_init(&rio, clientfd);

    while (Fgets(buf, MAXLINESIZE, stdin) != NULL) {
        Rio_writen(clientfd, buf, strlen(buf));
        Rio_readlineb(&rio, buf, MAXLINESIZE);
        Fputs(buf, stdout);
    }
    Close(clientfd);
    exit(0);

}

Tiny Web Server

#include <stdio.h>

#define __USE_GNU

#include <unistd.h>
#include <errno.h>
#include <stdlib.h>
#include <fcntl.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/stat.h>
#include <netdb.h>
#include <sys/socket.h>
#include <sys/types.h>


#define MAXLINESIZE 8192

#define RIO_BUF_SIZE 8192
extern char **environ;
typedef struct sockaddr SA;


void app_error(char *msg) /* Application error */
{
    fprintf(stderr, "%s!\n", msg);
    exit(0);
}

typedef struct {
    int rio_fd;
    int rio_unread_cnt;
    char *rio_buf_pointer;
    char rio_buf[RIO_BUF_SIZE];
} rio_file_with_buf;

//void gai_error(int code, char *msg) {
//    fprintf(stderr, "%s: %s\n", msg, gai_strerror(code));
//    exit(0);
//}

void Getaddrinfo(const char *node, const char *service, const struct addrinfo *hints, struct addrinfo **res) {
    int rc;
    if ((rc = getaddrinfo(node, service, hints, res)) != 0) {
        printf("Getaddrinfo error: ");
    }
}

void unix_error(char *msg) /* Unix-style error */
{
    fprintf(stderr, "%s: %s\n", msg, strerror(errno));
    exit(0);
}

void Close(int fd) {
    int rc;

    if ((rc = close(fd)) < 0)
        unix_error("Close error!\n");
}

int open_clientfd(char *hostname, char *port) {
    int clientfd;
    struct addrinfo hints, *listp, *p;

    memset(&hints, 0, sizeof(struct addrinfo));
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_flags = AI_NUMERICSERV;// force the service argument in the getaddrinfo function to be a port number
    hints.ai_flags |= AI_ADDRCONFIG;
    Getaddrinfo(hostname, port, &hints, &listp);

    for (p = listp; p; p = p->ai_next) {
        if ((clientfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) < 0) continue;
        if (connect(clientfd, p->ai_addr, p->ai_addrlen) != -1) break;
        Close(clientfd);
    }

    freeaddrinfo(listp);

    if (!p) return -1;
    return clientfd;
}

void dns_error(char *msg) /* DNS-style error */
{
    fprintf(stderr, "%s: DNS error %d!\n", msg, h_errno);
    exit(0);
}

int Open_clientfd(char *hostname, char *port) {
    int rc;
    if ((rc = open_clientfd(hostname, port)) < 0) {
        if (rc == -1)
            unix_error("Open_clientfd Unix error!\n");
        else
            dns_error("Open_clientfd DNS error!\n");
    }
    return rc;
}

int open_listenfd(char *port) {
    struct addrinfo hints, *listp, *p;
    int listenfd, optval = 1;

    memset(&hints, 0, sizeof(struct addrinfo));
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_flags = AI_NUMERICSERV;
    hints.ai_flags |= AI_PASSIVE | AI_ADDRCONFIG;
    Getaddrinfo(NULL, port, &hints, &listp);

    for (p = listp; p; p->ai_next) {
        if ((listenfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) < 0)continue;
        setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, (const void *) &optval, sizeof(int));

        if (bind(listenfd, p->ai_addr, p->ai_addrlen) == 0)break;
        Close(listenfd);
    }

    freeaddrinfo(listp);
    if (!p)return -1;
    if (listen(listenfd, 1024) < 0) {
        Close(listenfd);
        return -1;
    }

    return listenfd;
}


void rio_read_with_buf_init(rio_file_with_buf *p, int fd) {
    p->rio_fd = fd;
    p->rio_unread_cnt = 0;
    p->rio_buf_pointer = p->rio_buf;
}

// 带缓冲区的读
static ssize_t rio_read(rio_file_with_buf *rp, char *usrbuf, size_t n) {
    int this_read_cnt;
    while (rp->rio_unread_cnt <= 0) {
        rp->rio_unread_cnt = read(rp->rio_fd, rp->rio_buf, sizeof(rp->rio_buf));
        if (rp->rio_unread_cnt < 0) {
            if (errno != EINTR) {
                return -1;
            }
        } else if (rp->rio_unread_cnt == 0) {
            return 0;
        } else {
            rp->rio_buf_pointer = rp->rio_buf;
        }
    }
    this_read_cnt = rp->rio_unread_cnt < n ? rp->rio_unread_cnt : n;
    memcpy(usrbuf, rp->rio_buf_pointer, this_read_cnt);
    rp->rio_buf_pointer += this_read_cnt;
    rp->rio_unread_cnt -= this_read_cnt;
    return this_read_cnt;
}

ssize_t rio_writen(int fd, void *usrbuf, size_t n) {
    size_t nleft = n;
    ssize_t nwritten;
    char *bufp = usrbuf;
    while (nleft > 0) {
        if ((nwritten = write(fd, bufp, nleft)) <= 0) {
            if (errno == EINTR)
                nwritten = 0;
            else
                return -1;
        }
        nleft -= nwritten;
        bufp += nwritten;
    }
    return n;
}

ssize_t rio_readlineb(rio_file_with_buf *rp, void *usrbuf, size_t maxlen) {
    int n, rc;
    char c, *bufp = usrbuf;
    for (n = 1; n < maxlen; n++) {
        if ((rc = rio_read(rp, &c, 1)) == 1) {
            *bufp++ = c;
            if (c == '\n') {
                n++;
                break;
            }
        } else if (rc == 0) {
            if (n == 1)
                return 0;
            else
                break;
        } else
            return -1;
    }
    *bufp = 0;
    return n - 1;
}


char *Fgets(char *ptr, int n, FILE *stream) {
    char *rptr;

    if (((rptr = fgets(ptr, n, stream)) == NULL) && ferror(stream))
        app_error("Fgets error");

    return rptr;
}

void Rio_writen(int fd, void *usrbuf, size_t n) {
    if (rio_writen(fd, usrbuf, n) != n)
        unix_error("Rio_writen error!\n");
}

void clienterror(int fd, char *cause, char *errnum, char *shortmsg, char *longmsg) {
    char buf[MAXLINESIZE], body[MAXLINESIZE];
    sprintf(body, "<html><title>Tiny Error</title>");
    sprintf(body, "%s<body bgcolor=""ffffff"">\r\n", body);
    sprintf(body, "%s%s: %s\r\n", body, errnum, shortmsg);
    sprintf(body, "%s<p>%s: %s\r\n", body, longmsg, cause);
    sprintf(body, "%s<hr><em>The Tiny Web server</em>\r\n", body);

    sprintf(buf, "HTTP/1.0 %s %s\r\n", errnum, shortmsg);
    Rio_writen(fd, buf, strlen(buf));
    sprintf(buf, "Content-type: text/html\r\n");
    Rio_writen(fd, buf, strlen(buf));
    sprintf(buf, "Content-length: %d\r\n\r\n", (int) strlen(body));
    Rio_writen(fd, buf, strlen(buf));
    Rio_writen(fd, body, strlen(body));
}

void Fputs(const char *ptr, FILE *stream) {
    if (fputs(ptr, stream) == EOF)
        unix_error("Fputs error!\n");
}


ssize_t Rio_readlineb(rio_file_with_buf *rp, void *usrbuf, size_t maxlen) {
    ssize_t rc;

    if ((rc = rio_readlineb(rp, usrbuf, maxlen)) < 0)
        unix_error("Rio_readlineb error!\n");
    return rc;
}

int Open_listenfd(char *port) {
    int rc;

    if ((rc = open_listenfd(port)) < 0)
        unix_error("Open_listenfd error!\n");
    return rc;
}

void Getnameinfo(const struct sockaddr *sa, socklen_t salen, char *host,
                 size_t hostlen, char *serv, size_t servlen, int flags) {
    int rc;

    if ((rc = getnameinfo(sa, salen, host, hostlen, serv,
                          servlen, flags)) != 0)
        printf("Getnameinfo error: ");
}

void echo(int connfd) {
    size_t n;
    char buf[MAXLINESIZE];
    rio_file_with_buf rio;
    rio_read_with_buf_init(&rio, connfd);
    while ((n = Rio_readlineb(&rio, buf, MAXLINESIZE)) != 0) {
        printf("Received %d bytes\n", (int) n);
        Rio_writen(connfd, buf, n);
    }
}

int Accept(int s, struct sockaddr *addr, socklen_t *addrlen) {
    int rc;

    if ((rc = accept(s, addr, addrlen)) < 0)
        unix_error("Accept error!\n");
    return rc;
}

void read_requesthdrs(rio_file_with_buf *rp) {
    char buf[MAXLINESIZE];

    Rio_readlineb(rp, buf, MAXLINESIZE);
    while (strcmp(buf, "\r\n")) {
        Rio_readlineb(rp, buf, MAXLINESIZE);
        printf("%s", buf);
    }
    return;
}

void Execve(const char *filename, char *const argv[], char *const envp[]) {
    if (execve(filename, argv, envp) < 0)
        unix_error("Execve error");
}

pid_t Wait(int *status) {
    pid_t pid;

    if ((pid = wait(status)) < 0)
        unix_error("Wait error");
    return pid;
}

int Dup2(int fd1, int fd2) {
    int rc;

    if ((rc = dup2(fd1, fd2)) < 0)
        unix_error("Dup2 error");
    return rc;
}

void serve_dynamic(int fd, char *filename, char *cgiargs) {
    char buf[MAXLINESIZE], *emptylist[] = {NULL};
    sprintf(buf, "HTTP/1.0 200 OK\r\n");
    Rio_writen(fd, buf, strlen(buf));
    sprintf(buf, "Server: Tiny Web Server.\r\n");
    Rio_writen(fd, buf, strlen(buf));
    if (Fork() == 0) {
        setenv("QUERY_STRING", cgiargs, 1);
        Dup2(fd, STDOUT_FILENO);
        Execve(filename, emptylist, environ);
    }
    Wait(NULL);
}



int parse_uri(char *uri, char *filename, char *cgiargs) {
    char *ptr;
    if (!strstr(uri, "cgi-bin")) {
        strcpy(cgiargs, "");
        strcpy(filename, ".");
        strcat(filename, uri);
        if (uri[strlen(uri) - 1] == '/') {
            strcat(filename, "home.html");
        }
        return 1;
    }

    ptr = index(uri, '?');
    if (ptr) {
        strcpy(cgiargs, ptr + 1);
        *ptr = '\0';
    } else {
        strcpy(cgiargs, "");
    }
    strcpy(filename, ".");
    strcat(filename, uri);
    return 0;
}

pid_t Fork(void) {
    pid_t pid;

    if ((pid = fork()) < 0)
        unix_error("Fork error");
    return pid;
}


void get_filetype(char *filename, char *filetype) {
    if (strstr(filename, ".html"))
        strcpy(filetype, "text/html");
    else if (strstr(filename, ".gif"))
        strcpy(filetype, "image/gif");
    else if (strstr(filename, ".png"))
        strcpy(filetype, "text/png");
    else if (strstr(filename, ".jpg"))
        strcpy(filetype, "text/jpeg");
    else
        strcpy(filetype, "text/plain");
}

int Open(const char *pathname, int flags, mode_t mode) {
    int rc;

    if ((rc = open(pathname, flags, mode)) < 0)
        unix_error("Open error");
    return rc;
}

void *Mmap(void *addr, size_t len, int prot, int flags, int fd, off_t offset) {
    void *ptr;

    if ((ptr = mmap(addr, len, prot, flags, fd, offset)) == ((void *) -1))
        unix_error("mmap error");
    return (ptr);
}


void Munmap(void *start, size_t length) {
    if (munmap(start, length) < 0)
        unix_error("munmap error");
}

void serve_static(int fd, char *filename, int filesize) {
    int srcfd;
    char *srcp, filetype[MAXLINESIZE], buf[RIO_BUF_SIZE];
    get_filetype(filename, filetype);
    sprintf(buf, "HTTP/1.0 200 OK\r\n");
    sprintf(buf, "%sServer: Tiny Web Server\r\n", buf);
    sprintf(buf, "%sConnection: close\r\n", buf);
    sprintf(buf, "%sContent-length: %d\r\n", buf, filesize);
    sprintf(buf, "%sContent-type: %s\r\n\r\n", buf, filetype);
    Rio_writen(fd, buf, strlen(buf));
    printf("Response headers:\n");
    printf("%s", buf);

    srcfd = Open(filename, O_RDONLY, 0);
    srcp = Mmap(0, filesize, PROT_READ, MAP_PRIVATE, srcfd, 0);
    Close(srcfd);
    Rio_writen(fd, srcp, filesize);
    Munmap(srcp, filesize);
}

void doit(int fd) {
    int is_static;
    struct stat sbuf;
    char buf[MAXLINESIZE], method[MAXLINESIZE], uri[MAXLINESIZE], version[MAXLINESIZE];
    char filename[MAXLINESIZE], cgiargs[MAXLINESIZE];
    rio_file_with_buf rio;

    rio_read_with_buf_init(&rio, fd);
    Rio_readlineb(&rio, buf, MAXLINESIZE);
    printf("Request headers:\n");
    printf("%s", buf);
    sscanf(buf, "%s %s %s", method, uri, version);

    if (strcasecmp(method, "GET")) {
        clienterror(fd, method, "501", "Not implemented!", "Tiny does not implement this.");
        return;
    }

    read_requesthdrs(&rio);

    is_static = parse_uri(uri, filename, cgiargs);
//    exit(1);
    if (stat(filename, &sbuf) < 0) {
        clienterror(fd, filename, "404", "Not found!", "Tiny could not find this file.");
        return;

    }
//    printf("%d", is_static);
//    printf("%d", S_ISREG(sbuf.st_mode));
//    printf("%d", S_IRUSR & sbuf.st_mode);
//    printf("%d", S_IXUSR & sbuf.st_mode);
//    exit(1);


    if (is_static) {
        if (!S_ISREG(sbuf.st_mode) || !(S_IRUSR & sbuf.st_mode)) {
            clienterror(fd, filename, "403", "Forbidden!", "Tiny could not read this file.");
            return;
        }
        serve_static(fd, filename, sbuf.st_size);
    } else {
        if (!S_ISREG(sbuf.st_mode) || !(S_IXUSR & sbuf.st_mode)) {
            clienterror(fd, filename, "403", "Forbidden!", "Tiny could not read this file.");
            return;
        }
        serve_dynamic(fd, filename, cgiargs);
    }
}


int main(int argc, char **argv) {
    int listenfd, connfd;
    char hostname[MAXLINESIZE], port[MAXLINESIZE];
    socklen_t clientlen;
    struct sockaddr_storage clientaddr;

    if (argc != 2) {
        printf("Please input the command like this: <command-name> <port>");
        exit(0);
    }

    listenfd = Open_listenfd(argv[1]);

    while (1) {
        clientlen = sizeof(clientaddr);
        connfd = Accept(listenfd, (SA *) &clientaddr, &clientlen);
        Getnameinfo((SA *) &clientaddr, clientlen, hostname, MAXLINESIZE, port, MAXLINESIZE, 0);
        printf("Connect from (%s, %s)\n", hostname, port);
        doit(connfd);
        Close(connfd);
    }
}

adder

#include <unistd.h>
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <fcntl.h>
#include <string.h>
#include <sys/stat.h>
#include <netdb.h>
#include <sys/socket.h>
#include <sys/types.h>

#define MAXLINESIZE 8192

#define RIO_BUF_SIZE 8192

typedef struct sockaddr SA;

void app_error(char *msg) /* Application error */
{
    fprintf(stderr, "%s!\n", msg);
    exit(0);
}

typedef struct {
    int rio_fd;
    int rio_unread_cnt;
    char *rio_buf_pointer;
    char rio_buf[RIO_BUF_SIZE];
} rio_file_with_buf;

void gai_error(int code, char *msg) {
    fprintf(stderr, "%s: %s\n", msg, gai_strerror(code));
    exit(0);
}

void Getaddrinfo(const char *node, const char *service, const struct addrinfo *hints, struct addrinfo **res) {
    int rc;
    if ((rc = getaddrinfo(node, service, hints, res)) != 0) {
        gai_error(rc, "Getaddrinfo error: ");
    }
}

void unix_error(char *msg) /* Unix-style error */
{
    fprintf(stderr, "%s: %s\n", msg, strerror(errno));
    exit(0);
}

void Close(int fd) {
    int rc;

    if ((rc = close(fd)) < 0)
        unix_error("Close error!\n");
}

int open_clientfd(char *hostname, char *port) {
    int clientfd;
    struct addrinfo hints, *listp, *p;

    memset(&hints, 0, sizeof(struct addrinfo));
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_flags = AI_NUMERICSERV;// force the service argument in the getaddrinfo function to be a port number
    hints.ai_flags |= AI_ADDRCONFIG;
    Getaddrinfo(hostname, port, &hints, &listp);

    for (p = listp; p; p = p->ai_next) {
        if ((clientfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) < 0) continue;
        if (connect(clientfd, p->ai_addr, p->ai_addrlen) != -1) break;
        Close(clientfd);
    }

    freeaddrinfo(listp);

    if (!p) return -1;
    return clientfd;
}

void dns_error(char *msg) /* DNS-style error */
{
    fprintf(stderr, "%s: DNS error %d!\n", msg, h_errno);
    exit(0);
}

int Open_clientfd(char *hostname, char *port) {
    int rc;
    if ((rc = open_clientfd(hostname, port)) < 0) {
        if (rc == -1)
            unix_error("Open_clientfd Unix error!\n");
        else
            dns_error("Open_clientfd DNS error!\n");
    }
    return rc;
}

int open_listenfd(char *port) {
    struct addrinfo hints, *listp, *p;
    int listenfd, optval = 1;

    memset(&hints, 0, sizeof(struct addrinfo));
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_flags = AI_NUMERICSERV;
    hints.ai_flags |= AI_PASSIVE | AI_ADDRCONFIG;
    Getaddrinfo(NULL, port, &hints, &listp);

    for (p = listp; p; p->ai_next) {
        if ((listenfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) < 0)continue;
        setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, (const void *) &optval, sizeof(int));

        if (bind(listenfd, p->ai_addr, p->ai_addrlen) == 0)break;
        Close(listenfd);
    }

    freeaddrinfo(listp);
    if (!p)return -1;
    if (listen(listenfd, 1024) < 0) {
        Close(listenfd);
        return -1;
    }

    return listenfd;
}


void rio_read_with_buf_init(rio_file_with_buf *p, int fd) {
    p->rio_fd = fd;
    p->rio_unread_cnt = 0;
    p->rio_buf_pointer = p->rio_buf;
}

// 带缓冲区的读
static ssize_t rio_read(rio_file_with_buf *rp, char *usrbuf, size_t n) {
    int this_read_cnt;
    while (rp->rio_unread_cnt <= 0) {
        rp->rio_unread_cnt = read(rp->rio_fd, rp->rio_buf, sizeof(rp->rio_buf));
        if (rp->rio_unread_cnt < 0) {
            if (errno != EINTR) {
                return -1;
            }
        } else if (rp->rio_unread_cnt == 0) {
            return 0;
        } else {
            rp->rio_buf_pointer = rp->rio_buf;
        }
    }
    this_read_cnt = rp->rio_unread_cnt < n ? rp->rio_unread_cnt : n;
    memcpy(usrbuf, rp->rio_buf_pointer, this_read_cnt);
    rp->rio_buf_pointer += this_read_cnt;
    rp->rio_unread_cnt -= this_read_cnt;
    return this_read_cnt;
}

ssize_t rio_writen(int fd, void *usrbuf, size_t n) {
    size_t nleft = n;
    ssize_t nwritten;
    char *bufp = usrbuf;
    while (nleft > 0) {
        if ((nwritten = write(fd, bufp, nleft)) <= 0) {
            if (errno == EINTR)
                nwritten = 0;
            else
                return -1;
        }
        nleft -= nwritten;
        bufp += nwritten;
    }
    return n;
}

ssize_t rio_readlineb(rio_file_with_buf *rp, void *usrbuf, size_t maxlen) {
    int n, rc;
    char c, *bufp = usrbuf;
    for (n = 1; n < maxlen; n++) {
        if ((rc = rio_read(rp, &c, 1)) == 1) {
            *bufp++ = c;
            if (c == '\n') {
                n++;
                break;
            }
        } else if (rc == 0) {
            if (n == 1)
                return 0;
            else
                break;
        } else
            return -1;
    }
    *bufp = 0;
    return n - 1;
}


char *Fgets(char *ptr, int n, FILE *stream) {
    char *rptr;

    if (((rptr = fgets(ptr, n, stream)) == NULL) && ferror(stream))
        app_error("Fgets error");

    return rptr;
}

void Rio_writen(int fd, void *usrbuf, size_t n) {
    if (rio_writen(fd, usrbuf, n) != n)
        unix_error("Rio_writen error!\n");
}

void Fputs(const char *ptr, FILE *stream) {
    if (fputs(ptr, stream) == EOF)
        unix_error("Fputs error!\n");
}


ssize_t Rio_readlineb(rio_file_with_buf *rp, void *usrbuf, size_t maxlen) {
    ssize_t rc;

    if ((rc = rio_readlineb(rp, usrbuf, maxlen)) < 0)
        unix_error("Rio_readlineb error!\n");
    return rc;
}

int Open_listenfd(char *port) {
    int rc;

    if ((rc = open_listenfd(port)) < 0)
        unix_error("Open_listenfd error!\n");
    return rc;
}

void Getnameinfo(const struct sockaddr *sa, socklen_t salen, char *host,
                 size_t hostlen, char *serv, size_t servlen, int flags) {
    int rc;

    if ((rc = getnameinfo(sa, salen, host, hostlen, serv,
                          servlen, flags)) != 0)
        gai_error(rc, "Getnameinfo error: ");
}

void echo(int connfd) {
    size_t n;
    char buf[MAXLINESIZE];
    rio_file_with_buf rio;
    rio_read_with_buf_init(&rio, connfd);
    while ((n = Rio_readlineb(&rio, buf, MAXLINESIZE)) != 0) {
        printf("Received %d bytes\n", (int) n);
        Rio_writen(connfd, buf, n);
    }
}

int Accept(int s, struct sockaddr *addr, socklen_t *addrlen) {
    int rc;

    if ((rc = accept(s, addr, addrlen)) < 0)
        unix_error("Accept error!\n");
    return rc;
}


int main(void) {
    char *buf, *p;
    char arg1[MAXLINESIZE], arg2[MAXLINESIZE], content[MAXLINESIZE];
    int n1 = 0, n2 = 0;
    if ((buf=getenv("QUERY_STRING"))!=NULL){
        p = strchr(buf, '&');
        *p = '\0';
        strcpy(arg1, buf);
        strcpy(arg2, p + 1);
        n1 = atoi(arg1);
        n2 = atoi(arg2);
    }
    sprintf(content, "QUERY_STRING=%s", buf);
    sprintf(content, "%sThe answer is: %d + %d = %d\r\n", content, n1, n2, n1+n2);

    printf("Connection: close\r\n");
    printf("Content-length: %d\r\n", (int)strlen(content));
    printf("Content-type: text/html\r\n\r\n");
    printf("%s", content);
    fflush(stdout);
    exit(0);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值