美文网首页
2021-04-26

2021-04-26

作者: 伯尤特夫 | 来源:发表于2021-04-26 09:47 被阅读0次

tinyhttp学习

空闲时间学习了下tinyhttp这个微型网络库,代码逻辑其实挺简单的,这里记录一下使用的一些函数说明。

tinyhttpd是一个超轻量型Http Server,使用c语言开发,全部代码只有502行(包括注释),附带一个简单的Client,可以通过阅读这段代码理解一个 Http Server 的本质。

下载链接:http://sourceforge.net/projects/tinyhttpd/

库代码很简单,就一个httpd.c文件,这里就具体拆分成几个部分分析了,直接贴出完整代码:

/* J. David's webserver */
/* This is a simple webserver.
 * Created November 1999 by J. David Blackstone.
 * CSE 4344 (Network concepts), Prof. Zeigler
 * University of Texas at Arlington
 */
/* This program compiles for Sparc Solaris 2.6.
 * To compile for Linux:
 *  1) Comment out the #include <pthread.h> line.
 *  2) Comment out the line that defines the variable newthread.
 *  3) Comment out the two lines that run pthread_create().
 *  4) Uncomment the line that runs accept_request().
 *  5) Remove -lsocket from the Makefile.
 */
#include <stdio.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <ctype.h>
#include <strings.h>
#include <string.h>
#include <sys/stat.h>
#include <pthread.h>
#include <sys/wait.h>
#include <stdlib.h>

#define ISspace(x) isspace((int)(x))

#define SERVER_STRING "Server: jdbhttpd/0.1.0\r\n"

void accept_request(int);
void bad_request(int);
void cat(int, FILE *);
void cannot_execute(int);
void error_die(const char *);
void execute_cgi(int, const char *, const char *, const char *);
int get_line(int, char *, int);
void headers(int, const char *);
void not_found(int);
void serve_file(int, const char *);
int startup(u_short *);
void unimplemented(int);

/**********************************************************************/
/* A request has caused a call to accept() on the server port to
 * return.  Process the request appropriately.
 * Parameters: the socket connected to the client */
/**********************************************************************/
void accept_request(int client)
{
  char buf[1024];
  int numchars;
  char method[255];
  char url[255];
  char path[512];
  size_t i, j;
  struct stat st;
  int cgi = 0; /* becomes true if server decides this is a CGI
                    * program */
  char *query_string = NULL;

  // 获取一行HTTP报文数据
  numchars = get_line(client, buf, sizeof(buf));
  i = 0;
  j = 0;

  // 对于HTTP报文来说,第一行的内容即为报文的起始行,格式为<method> <request-URL> <version>,
  // 每个字段用空白字符相连
  // 按字符解析,遇到空格结束,这里是获取method的内容
  while (!ISspace(buf[j]) && (i < sizeof(method) - 1))
  {
    method[i] = buf[j];
    i++;
    j++;
  }
  method[i] = '\0';

  // 既不是GET请求,也不是POST请求,则向客户端返回响应结果
  // #include <string.h>
  // int strcasecmp (const char *s1, const char *s2);
  // strcasecmp()用来比较参数s1 和s2 字符串,比较时会自动忽略大小写的差异
  // 若参数s1和s2字符串相同则返回0,s1大于s2则返回大于0的值,s1若小于s2则返回小于0的值
  if (strcasecmp(method, "GET") && strcasecmp(method, "POST"))
  {
    unimplemented(client);
    return;
  }

  // 若是POST请求,使用CGI解析
  if (strcasecmp(method, "POST") == 0)
    cgi = 1;

  i = 0;
  // 跳过method后的空格
  while (ISspace(buf[j]) && (j < sizeof(buf)))
    j++;

  // 获取request-URL
  while (!ISspace(buf[j]) && (i < sizeof(url) - 1) && (j < sizeof(buf)))
  {
    url[i] = buf[j];
    i++;
    j++;
  }
  url[i] = '\0';

  // 如果是GET请求,url可能带有'?'字符,有查询参数
  if (strcasecmp(method, "GET") == 0)
  {
    query_string = url;
    while ((*query_string != '?') && (*query_string != '\0'))
      query_string++;
    if (*query_string == '?')
    {
      // 如果带有查询参数,需要执行cgi,解析参数,设置标志位为1
      cgi = 1;

      // 将解析参数截取下来
      *query_string = '\0';
      query_string++;
    }
  }

  // 将url中的路径格式化到path中
  sprintf(path, "htdocs%s", url);

  // 如果path是一个目录,则默认设置首页为"index.html"
  if (path[strlen(path) - 1] == '/')
    strcat(path, "index.html");

  // 如果访问的网页不存在,不断读取剩下的请求头信息,并丢弃
  // 然后返回给客户端,访问网页不存在
  if (stat(path, &st) == -1)
  {
    while ((numchars > 0) && strcmp("\n", buf)) /* read & discard headers */
      numchars = get_line(client, buf, sizeof(buf));
    not_found(client);
  }
  else
  {
    // 同上,路径是目录则默认设置首页为"index.html"
    // S_IFDIR代表目录
    if ((st.st_mode & S_IFMT) == S_IFDIR)
      strcat(path, "/index.html");

    // S_IXUSR:文件所有者具可执行权限
    // S_IXGRP:用户组具可执行权限
    // S_IXOTH:其他用户具可读取权限
    if ((st.st_mode & S_IXUSR) ||
        (st.st_mode & S_IXGRP) ||
        (st.st_mode & S_IXOTH))
      cgi = 1;

    if (!cgi)
      // 返回静态文件
      serve_file(client, path);
    else
      // 执行CGI动态解析
      execute_cgi(client, path, method, query_string);
  }

  close(client);
}

/**********************************************************************/
/* Inform the client that a request it has made has a problem.
 * Parameters: client socket */
/**********************************************************************/
void bad_request(int client)
{
  char buf[1024];

  sprintf(buf, "HTTP/1.0 400 BAD REQUEST\r\n");
  send(client, buf, sizeof(buf), 0);
  sprintf(buf, "Content-type: text/html\r\n");
  send(client, buf, sizeof(buf), 0);
  sprintf(buf, "\r\n");
  send(client, buf, sizeof(buf), 0);
  sprintf(buf, "<P>Your browser sent a bad request, ");
  send(client, buf, sizeof(buf), 0);
  sprintf(buf, "such as a POST without a Content-Length.\r\n");
  send(client, buf, sizeof(buf), 0);
}

/**********************************************************************/
/* Put the entire contents of a file out on a socket.  This function
 * is named after the UNIX "cat" command, because it might have been
 * easier just to do something like pipe, fork, and exec("cat").
 * Parameters: the client socket descriptor
 *             FILE pointer for the file to cat */
/**********************************************************************/
void cat(int client, FILE *resource)
{
  char buf[1024];

  fgets(buf, sizeof(buf), resource);

  // 循环读取文件内容并发送给客户端,直到文件结尾
  while (!feof(resource))
  {
    send(client, buf, strlen(buf), 0);
    fgets(buf, sizeof(buf), resource);
  }
}

/**********************************************************************/
/* Inform the client that a CGI script could not be executed.
 * Parameter: the client socket descriptor. */
/**********************************************************************/
void cannot_execute(int client)
{
  char buf[1024];

  sprintf(buf, "HTTP/1.0 500 Internal Server Error\r\n");
  send(client, buf, strlen(buf), 0);
  sprintf(buf, "Content-type: text/html\r\n");
  send(client, buf, strlen(buf), 0);
  sprintf(buf, "\r\n");
  send(client, buf, strlen(buf), 0);
  sprintf(buf, "<P>Error prohibited CGI execution.\r\n");
  send(client, buf, strlen(buf), 0);
}

/**********************************************************************/
/* Print out an error message with perror() (for system errors; based
 * on value of errno, which indicates system call errors) and exit the
 * program indicating an error. */
/**********************************************************************/
void error_die(const char *sc)
{
  perror(sc);
  exit(1);
}

/**********************************************************************/
/* Execute a CGI script.  Will need to set environment variables as
 * appropriate.
 * Parameters: client socket descriptor
 *             path to the CGI script */
/**********************************************************************/
void execute_cgi(int client, const char *path,
                 const char *method, const char *query_string)
{
  char buf[1024];
  int cgi_output[2];
  int cgi_input[2];
  pid_t pid;
  int status;
  int i;
  char c;
  int numchars = 1;
  int content_length = -1;

  buf[0] = 'A';
  buf[1] = '\0';
  if (strcasecmp(method, "GET") == 0)
    // GET请求的话,就将HTTP请求头读完并丢弃
    while ((numchars > 0) && strcmp("\n", buf)) /* read & discard headers */
      numchars = get_line(client, buf, sizeof(buf));
  else /* POST */
  {
    // 处理POST请求
    numchars = get_line(client, buf, sizeof(buf));
    while ((numchars > 0) && strcmp("\n", buf))
    {
      // 循环读取请求头信息找到Content-Length字段的值
      // 这里bud[15] = '\0'是为了截取"Content-Length:"
      buf[15] = '\0';
      if (strcasecmp(buf, "Content-Length:") == 0)
        // 解析出内容长度,如:"Content-Length:60"
        content_length = atoi(&(buf[16]));
      numchars = get_line(client, buf, sizeof(buf));
    }

    // 如果POST请求未携带内容,则返回错误请求给客户端
    if (content_length == -1)
    {
      bad_request(client);
      return;
    }
  }

  // 返回正确响应码200
  sprintf(buf, "HTTP/1.0 200 OK\r\n");
  send(client, buf, strlen(buf), 0);

  // #include <unistd.h>
  // int pipe(int filedes[2]);
  // 返回值:成功,返回0,否则返回-1。参数数组包含pipe使用的两个文件的描述符。fd[0]:读管道,fd[1]:写管道。
  // 必须在fork()中调用pipe(),否则子进程不会继承文件描述符。
  // 两个进程不共享祖先进程,就不能使用pipe。但是可以使用命名管道。
  // pipe(cgi_output)执行成功后,cgi_output[0]:读通道 cgi_output[1]:写通道
  if (pipe(cgi_output) < 0)
  {
    cannot_execute(client);
    return;
  }
  if (pipe(cgi_input) < 0)
  {
    cannot_execute(client);
    return;
  }

  // fork出一个子进程
  if ((pid = fork()) < 0)
  {
    cannot_execute(client);
    return;
  }

  // 子进程运行CGI脚本
  if (pid == 0) /* child: CGI script */
  {
    char meth_env[255];
    char query_env[255];
    char length_env[255];

    // 1代表着stdout,0代表着stdin,将系统标准输出重定向为cgi_output[1]
    // 将系统标准输入重定向为cgi_input[0],这一点非常关键,cgi程序中用的是标准输入输出进行交互
    dup2(cgi_output[1], 1);
    dup2(cgi_input[0], 0);

    // 子进程中关闭cgi_output中读通道,关闭cgi_input中写通道
    close(cgi_output[0]);
    close(cgi_input[1]);

    sprintf(meth_env, "REQUEST_METHOD=%s", method);
    putenv(meth_env);
    if (strcasecmp(method, "GET") == 0)
    {
      sprintf(query_env, "QUERY_STRING=%s", query_string);
      putenv(query_env);
    }
    else
    { /* POST */
      sprintf(length_env, "CONTENT_LENGTH=%d", content_length);
      putenv(length_env);
    }

    // 头文件:#include <unistd.h>
    // 函数原型:int execl(const char* path, const char* arg, ....);
    // 函数说明:execl()用来执行参数path字符串所代表的文件路径(可执行程序、脚本等)接下来的
    // 参数代表执行该文件时传递过去的argv(0)、argv[1]……,最后一个参数必须用空指针(NULL)作结束。
    // 返回值:如果执行成功则函数不会返回,执行失败则直接返回-1,失败原因存于errno中。
    execl(path, path, NULL);

    // 进程退出
    exit(0);
  }
  else
  { /* parent */
    // 父进程中关闭cgi_output中写通道,关闭cgi_input中读通道
    close(cgi_output[1]);
    close(cgi_input[0]);

    // 读取POST请求中的内容,将其发送给子进程中CGI脚本
    if (strcasecmp(method, "POST") == 0)
      for (i = 0; i < content_length; i++)
      {
        recv(client, &c, 1, 0);
        write(cgi_input[1], &c, 1);
      }

    // 循环读取子进程CGI脚本返回的数据,将其发送给客户端
    while (read(cgi_output[0], &c, 1) > 0)
      send(client, &c, 1, 0);

    close(cgi_output[0]);
    close(cgi_input[1]);

    // 头文件:#include <sys/types.h> #include <sys/wait.h>
    // 函数原型:pid_t waitpid(pid_t pid, int* status, int options);
    // 函数说明:如果在调用waitpid()时子进程已经结束,则waitpid()会立即返回子进程结束状态值。
    // 子进程的结束状态值会由参数status返回,而子进程的进程识别码也会一起返回。如果不在意结束状态值,则
    // 参数status可以设成NULL。参数pid为欲等待的子进程识别码。
    waitpid(pid, &status, 0);
  }
}

/**********************************************************************/
/* Get a line from a socket, whether the line ends in a newline,
 * carriage return, or a CRLF combination.  Terminates the string read
 * with a null character.  If no newline indicator is found before the
 * end of the buffer, the string is terminated with a null.  If any of
 * the above three line terminators is read, the last character of the
 * string will be a linefeed and the string will be terminated with a
 * null character.
 * Parameters: the socket descriptor
 *             the buffer to save the data in
 *             the size of the buffer
 * Returns: the number of bytes stored (excluding null) */
/**********************************************************************/
int get_line(int sock, char *buf, int size)
{
  int i = 0;
  char c = '\0';
  int n;

  while ((i < size - 1) && (c != '\n'))
  {
    n = recv(sock, &c, 1, 0);
    /* DEBUG printf("%02X\n", c); */
    if (n > 0)
    {
      if (c == '\r')
      {
        n = recv(sock, &c, 1, MSG_PEEK);
        /* DEBUG printf("%02X\n", c); */
        if ((n > 0) && (c == '\n'))
          recv(sock, &c, 1, 0);
        else
          c = '\n';
      }
      buf[i] = c;
      i++;
    }
    else
      c = '\n';
  }
  buf[i] = '\0';

  return (i);
}

/**********************************************************************/
/* Return the informational HTTP headers about a file. */
/* Parameters: the socket to print the headers on
 *             the name of the file */
/**********************************************************************/
void headers(int client, const char *filename)
{
  char buf[1024];
  (void)filename; /* could use filename to determine file type */

  strcpy(buf, "HTTP/1.0 200 OK\r\n");
  send(client, buf, strlen(buf), 0);
  strcpy(buf, SERVER_STRING);
  send(client, buf, strlen(buf), 0);
  sprintf(buf, "Content-Type: text/html\r\n");
  send(client, buf, strlen(buf), 0);
  strcpy(buf, "\r\n");
  send(client, buf, strlen(buf), 0);
}

/**********************************************************************/
/* Give a client a 404 not found status message. */
/**********************************************************************/
void not_found(int client)
{
  char buf[1024];

  sprintf(buf, "HTTP/1.0 404 NOT FOUND\r\n");
  send(client, buf, strlen(buf), 0);
  sprintf(buf, SERVER_STRING);
  send(client, buf, strlen(buf), 0);
  sprintf(buf, "Content-Type: text/html\r\n");
  send(client, buf, strlen(buf), 0);
  sprintf(buf, "\r\n");
  send(client, buf, strlen(buf), 0);
  sprintf(buf, "<HTML><TITLE>Not Found</TITLE>\r\n");
  send(client, buf, strlen(buf), 0);
  sprintf(buf, "<BODY><P>The server could not fulfill\r\n");
  send(client, buf, strlen(buf), 0);
  sprintf(buf, "your request because the resource specified\r\n");
  send(client, buf, strlen(buf), 0);
  sprintf(buf, "is unavailable or nonexistent.\r\n");
  send(client, buf, strlen(buf), 0);
  sprintf(buf, "</BODY></HTML>\r\n");
  send(client, buf, strlen(buf), 0);
}

/**********************************************************************/
/* Send a regular file to the client.  Use headers, and report
 * errors to client if they occur.
 * Parameters: a pointer to a file structure produced from the socket
 *              file descriptor
 *             the name of the file to serve */
/**********************************************************************/
void serve_file(int client, const char *filename)
{
  FILE *resource = NULL;
  int numchars = 1;
  char buf[1024];

  // 不清楚干什么用,好像就是随便初始化一下,因为下面直接就开始比较了buf内容
  buf[0] = 'A';
  buf[1] = '\0';

  // 读取HTTP请求头并丢弃
  while ((numchars > 0) && strcmp("\n", buf)) /* read & discard headers */
    numchars = get_line(client, buf, sizeof(buf));

  // 打开文件,若失败则返回文件不存在给客户端
  resource = fopen(filename, "r");
  if (resource == NULL)
    not_found(client);
  else
  {
    // 打开文件成功,添加HTTP头,并发送文件内容给客户端
    headers(client, filename);
    cat(client, resource);
  }
  fclose(resource);
}

/**********************************************************************/
/* This function starts the process of listening for web connections
 * on a specified port.  If the port is 0, then dynamically allocate a
 * port and modify the original port variable to reflect the actual
 * port.
 * Parameters: pointer to variable containing the port to connect on
 * Returns: the socket */
/**********************************************************************/
int startup(u_short *port)
{
  int httpd = 0;
  struct sockaddr_in name;

  // 设置http socket
  httpd = socket(PF_INET, SOCK_STREAM, 0);
  if (httpd == -1)
    error_die("socket");

  // 设置地址族
  memset(&name, 0, sizeof(name));
  name.sin_family = AF_INET;
  name.sin_port = htons(*port);
  name.sin_addr.s_addr = htonl(INADDR_ANY);

  // socket与地址绑定
  if (bind(httpd, (struct sockaddr *)&name, sizeof(name)) < 0)
    error_die("bind");

  // 动态申请端口号
  if (*port == 0) /* if dynamically allocating a port */
  {
    int namelen = sizeof(name);

    // getsockname可以获得一个与socket相关的地址,第二个参数为存放所获取套接字名称的缓冲区
    if (getsockname(httpd, (struct sockaddr *)&name, &namelen) == -1)
      error_die("getsockname");

    // ntohs将一个16位数由网络字节顺序转换为主机字节顺序
    *port = ntohs(name.sin_port);
  }

  // 监听连接
  if (listen(httpd, 5) < 0)
    error_die("listen");
  return (httpd);
}

/**********************************************************************/
/* Inform the client that the requested web method has not been
 * implemented.
 * Parameter: the client socket */
/**********************************************************************/
void unimplemented(int client)
{
  char buf[1024];

  sprintf(buf, "HTTP/1.0 501 Method Not Implemented\r\n");
  send(client, buf, strlen(buf), 0);
  sprintf(buf, SERVER_STRING);
  send(client, buf, strlen(buf), 0);
  sprintf(buf, "Content-Type: text/html\r\n");
  send(client, buf, strlen(buf), 0);
  sprintf(buf, "\r\n");
  send(client, buf, strlen(buf), 0);
  sprintf(buf, "<HTML><HEAD><TITLE>Method Not Implemented\r\n");
  send(client, buf, strlen(buf), 0);
  sprintf(buf, "</TITLE></HEAD>\r\n");
  send(client, buf, strlen(buf), 0);
  sprintf(buf, "<BODY><P>HTTP request method not supported.\r\n");
  send(client, buf, strlen(buf), 0);
  sprintf(buf, "</BODY></HTML>\r\n");
  send(client, buf, strlen(buf), 0);
}

/**********************************************************************/

int main(void)
{
  int server_sock = -1;
  u_short port = 0;
  int client_sock = -1;
  struct sockaddr_in client_name;
  int client_name_len = sizeof(client_name);
  pthread_t newthread;

  // 实现socket的创建、绑定、监听
  server_sock = startup(&port);
  printf("httpd running on port %d\n", port);

  // 这里一个循环接收客户端的连接,每个客户端连接之后就启动一个线程
  // 去执行accept_request函数
  while (1)
  {
    // 接收客户端的连接,没有客户端连接的时候会阻塞在这
    client_sock = accept(server_sock,
                         (struct sockaddr *)&client_name,
                         &client_name_len);
    if (client_sock == -1)
      error_die("accept");

    // 创建线程去响应客户端的操作,支持多客户端连接
    /* accept_request(client_sock); */
    if (pthread_create(&newthread, NULL, accept_request, client_sock) != 0)
      perror("pthread_create");
  }

  close(server_sock);

  return (0);
}

在学习网络编程的时候,看到一些名词:阻塞/非阻塞IO、多路复用、边缘触发、事件驱动。咱也不知道啥意思,后续学习中再补充。

下面列出tinyhttp中使用到的部分函数说明:
【strcasecmp】

头文件:#include <string.h>
函数原型:int strcasecmp(const char* str1, const char* str2);
函数解释:比较两个字符串是否相同,忽略大小写。
返回值:若str1等于str2返回0,若str1大于str2返回大于0的值,若str1小于str2则返回小于0的值

【socket】

头文件;#include <sys/socket.h>
函数原型:int socket(int af, int type, int protocol);
函数解释:用来创建套接字。第一个参数af(Address Family)为地址族,即IP地址类型,常用的有AF_INET和AF_INET6,前者为IPV4地址,如127.0.0.1(是一个特殊的IP地址,表示本机地址);后者表示IPV6地址,如1030:C9B4:FF12:48AA:1A2B。也可以使用PF前缀(Protocol Family)和AF一样,PF_INET等价AF_INET,PF_INET6等价AF_INET6。第二个参数type为数据传输方式,常用的有SOCK_STREAM(流格式套接字/面向连接的套接字)和SOCK_DGRAM(数据报格式套接字/无连接的套接字)。第三个参数protocol表示传输协议,常用的有IPPROTO_TCP和IPPROTO_UDP,分别表示TCP传输协议和UDP传输协议。(问题:有了地址类型和传输方式,还不足以决定采用哪种协议吗?还需要第三个参数?答:有了地址类型和传输方式,操作系统会自动推演出协议类型。但是,若有两个不同的协议对应相同的地址类型和传输方式时,操作系统就无法选择使用哪种协议了),IP地址为IPV4,如果传输方式使用SOCK_STREAM(或者SOCK_DGRAM)时,能满足这两个条件的协议只有TCP(或者UDP)协议,所以protocol的值可以设置为0,让操作系统自己推演使用什么协议,示例:
int tcp_socket = socket(AF_INET, SOCK_STREAM, 0);  //创建TCP套接字
int udp_socket = socket(AF_INET, SOCK_DGRAM, 0);  //创建UDP套接字
返回值:返回创建的套接字(Linux下一切皆文件,这里就好像返回一个文件描述符一样)

【大端模式和小端模式】

大端模式和小端模式是实际的字节和存储的地址对应关系的模式
大端模式:低地址存储高字节
小端模式:低地址存储低字节
不管是大端模式还是小端模式,我们存储或者读取数据的时候都是从低地址往高地址存储或读取

字节序转换函数
1、htons 把unsigned short类型从主机序转换到网络序(host to network short)
2、htonl 把unsigned long类型从主机序转换到网络序(host to network long)
3、ntohs 把unsigned short类型从网络序转换到主机序(network to host short)
4、ntohl 把unsigned long类型从网络序转换到主机序(network to host long)
主机序一般都是小端(小部分是大端),网络序是大端存储

实际核心问题就是一个是字节顺序,一个是存储顺序

【seekg】【seekp】

seekg()是对输入流的操作 g是get缩写
seekp()是对输出流的操作 p是put缩写
头文件:#include <istream>/#include <ostream>
函数原型:
basic_istream<Elem, Tr>& seekg(  //一个参数
    pos_type pos
);
basic_istream<Elem, Tr>& seekg(
    off_type off,                                     //两个参数
    ios_base::seekdir way
);
函数解释:移动在流中读/写位置,参数pos表示读取/写入指针的绝对位置;off为偏移量;way表示基地址,有三个取值:ios::beg(表示输入/输出流的起始位置)、ios::cur(表示输入/输出流的当前位置)、ios::end(表示输入/输出流的结束位置)

【tellg】【tellp】

tellg()是对输入流的操作 g是get缩写
tellp()是对输出流的操作 p是put缩写
头文件:#include <istream>/#include <ostream>
函数原型:int tellg();
函数说明:返回流中读取/写入的当前位置
返回值:返回一个整型,表示读/写指针位置

【read】

头文件:#include <istream>
函数原型:istream& read(char* buffer, int size);
函数说明:读取文件内容。第一个参数buffer用于保存文件内容,第二个参数size表示需要读取多少个字节

【getline】

头文件:#include <istream>
函数原型:
istream& getline(char* buffer, int size); 
istream& getline (char* s, streamsize n, char delim);
函数说明:读取最多size个字符保存到buffer中,即使大小不够size。遇到delim,或者读完一行,或字数达到限制则终止
头文件:#include <string>
函数原型:
(1) 
istream& getline (istream& is, string& str, char delim);
istream& getline (istream&& is, string& str, char delim);
(2) 
istream& getline (istream& is, string& str);
istream& getline (istream&& is, string& str);
函数说明:(1)和(2)就是是否设置终止符的差异,调用两个参数的函数内部会默认以'\n'为结束符,第一个参数is是流,第二个参数str用于保存读取的内容,第三个参数delim表示终止符,默认是'\n'。

【write】

头文件:#include <ostream>
函数原型:ostream& write (const char* s, streamsize n);
函数说明:将s中前n个字符写入流中

【putenv】

头文件:#include <stdlib.h>
函数原型:int putenv(const char* string);
函数说明:putenv()用来改变或增加环境变量的内容。参数string 的格式为name=value,如果该环境变量原先存在,则变量内容会依参数string改变,否则此参数内容会成为新的环境变量。
返回值:执行成功则返回0,有错误发生则返回-1。

【getenv】

头文件:#include <stdlib.h>
函数原型:char* getenv(const char* name);
函数说明:getenv()用来取得参数name 环境变量的内容。参数name 为环境变量的名称,如果该变量存在则会返回指向该内容的指针,环境变量的格式为name=value。
返回值:执行成功则返回指向该内容的指针,找不到符合的环境变量名称则返回NULL。

如下这些,后面学习完再补充:
【fgets】

代码块

【feof】

代码块

【pipe】

代码块

【fork】

代码块

【dup2】

代码块

【execl】

代码块

【waitpid】

代码块

【recv】

代码块

【send】

代码块

【select/poll/epoll】

代码块

【mmap】

代码块

【sem_init/sem_destroy/sem_wait/sem_post】

代码块

相关文章

  • 2021-04-26

    2021-04-26

  • bitshares比特股数据20210426

    2021-04-26比特股BTS大额转账的记录 时间转出转入BTS数量13:50:57zbbts001zbsend...

  • 态度

    我怎么如此幸运-99将帅挑战赛51天-重生233-戴红霞(2021-04-26) 我怎么如此幸运-态度 1.我怎么...

  • 20210421《寻路中国》

    缘起 忘记啥时候开始的了,只记得是2021年4月,计划15个番茄钟,2021-04-26全书读完,用时13个。 副...

  • 单片机学习笔记 (一)

    单片机学习笔记 (一) 2021-04-26 1.初始单片机(STC 89C52) 一共有40个引脚,其中:40号...

  • #1000/5完成数#镜子练习第一天

    2021-04-26讲我喜欢你,我爱你,感觉真的太让人害羞了,浑身颤栗,对自己不信任,感觉这个行为有点蠢。 我想要...

  • 幸福其实没那么难

    2021-04-26 前一阵子跟老公闹别扭,日子总是过得磕磕绊绊,别别扭扭,搞得自己心情也非常不好。一直生气不想理...

  • 跟璇子一起打造个人IP(13)

    跟璇子一起打造个人IP(13) 2021-04-26 第13天 昨天无意间看到了一个有趣的段子,开心之余,也引起了...

  • 2021-04-26

    早梦日记 梦见自己和同学一起玩,到商场挑到了非常好看的衣服,清新浅色的防晒衣。 后来去朋友家睡觉。他们房子是欧式建...

  • 2021-04-26

    睁着眼睛时,目光所及之处全是你的影子 闭着眼睛时,思维所过之处全是你的样子 这颗想你的心啊何处安放 你对我呼唤 你...

网友评论

      本文标题:2021-04-26

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