并发 MiniHttp 服务器

avatar
作者
筋斗云
阅读量:1

项目需求

实现一个http 服务器项目,服务器启动后监听80端口的tcp 连接,当用户通过任意一款浏览器访问我们的http服务器,http服务器会查找用户访问的html页面是否存在,如果存在则通过http 协议响应客户端的请求,把页面返回给浏览器,浏览器显示html页面;如果页面不存在,则按照http 协议的规定,通知浏览器此页面不存在(404 NOT FOUND)

在这里插入图片描述

网络协议栈中各层的功能如下:
应用层:根据特定的通信目的,对数据进行分析处理,以达到某种业务性的目的。
传输层:处理传输时遇到的问题,主要是保证数据传输的可靠性。
网络层:完成数据的转发,解决数据去哪里的问题。
链路层:负责数据真正的发生过程。

数据封装与分用的过程如下:
在这里插入图片描述
发送端在发生数据前,该数据需要先自顶向下贯穿网络协议栈完成数据的封装,在这个过程中,每一层协议都会为该数据添加上对应的报头信息。接收端在收到数据后,该数据需要先自底向上贯穿网络协议栈完成数据的解包和分用,在这个过程中,每一层协议都会将对应的报头信息提取出来。

需求分析

Html 页面

Html,全称 Hypertext Markup Language,也就是“超文本链接标示语言”。
HTML文本是由 HTML命令组成的描述性文本,HTML 命令可以说明文字、 图形、动画、声音、表格、链接等。

HTTP 协议

HTTP 协议是Hyper Text Transfer Protocol(超文本传输协议)的缩写,是用于从万维网(WWW:World Wide Web )服务器传输超文本到本地浏览器的传送协议。

HTTP的特点:
1.客户端服务器模式(CS,BS):在一条通信线路上必定有一端是客户端,另一端是服务器端,请求从客户端发出,服务器响应请求并返回。
2.简单快速:客户端向服务器请求服务时,只需传送请求方法和请求资源路径,不需要发送额外过多的数据,并且由于HTTP协议结构较为简单,使得HTTP服务器的程序规模小,因此通信速度很快。
3.灵活:HTTP协议对数据对象没有要求,允许传输任意类型的数据对象,对于正在传输的数据类型,HTTP协议将通过报头中的Content-Type属性加以标记。
4.无连接:每次连接都只会对一个请求进行处理,当服务器对客户端的请求处理完毕并收到客户端的应答后,就会直接断开连接。HTTP协议采用这种方式可以大大节省传输时间,提高传输效率。
5.无状态:HTTP协议自身不对请求和响应之间的通信状态进行保存,每个请求都是独立的,这是为了让HTTP能更快地处理大量事务,确保协议的可伸缩性而特意设计的。

URL格式

URL(Uniform Resource Lacator)叫做统一资源定位符,也就是我们通常所说的网址,是因特网的万维网服务程序上用于指定信息位置的表示方法。

一个URL大致由如下几部分构成:
在这里插入图片描述
1.http://表示的是协议名称,表示请求时需要使用的协议,通常使用的是HTTP协议或安全协议HTTPS。
2.user:pass表示的是登录认证信息,包括登录用户的用户名和密码。(可省略)
3.www.example.jp表示的是服务器地址,通常以域名的形式表示。
4.80表示的是服务器的端口号。(可省略)
5./dir/index.html表示的是要访问的资源所在的路径(/表示的是web根目录)。
6.uid=1表示的是请求时通过URL传递的参数,这些参数以键值对的形式通过&符号分隔开。(可省略)
7.ch1表示的是片段标识符,是对资源的部分补充。(可省略)
注意:
如果访问服务器时没有指定要访问的资源路径,那么浏览器会自动帮我们添加/,但此时仍然没有指明要访问web根目录下的哪一个资源文件,这时默认访问的是目标服务的首页。
大部分URL中的端口号都是省略的,因为常见协议对应的端口号都是固定的,比如HTTP、HTTPS和SSH对应的端口号分别是80、443和22,在使用这些常见协议时不必指明协议对应的端口号,浏览器会自动帮我们进行填充。

URI、URL、URN

URI、URL、URN的定义如下:
URI(Uniform Resource Indentifier)统一资源标识符:用来唯一标识资源。
URL(Uniform Resource Locator)统一资源定位符:用来定位唯一的资源。
URN(Uniform Resource Name)统一资源名称:通过名字来标识资源。
URI、URL、URN三者的关系 URL是URI的一种,URL不仅能唯一标识资源,还定义了该如何访问或定位该资源,URN也是URI的一种,URN通过名字来标识资源,因此URL和URN都是URI的子集。

在这里插入图片描述
URI有绝对和相对之分:
绝对的URI:对标识符出现的环境没有依赖,比如URL就是一种绝对的URI,同一个URL无论出现在什么地方都能唯一标识同一个资源。
相对的URI:对标识符出现的环境有依赖,比如HTTP请求行中的请求资源路径就是一种相对的URI,这个资源路径出现在不同的主机上标识的就是不同的资源。

客户端请求

客户端发送一个HTTP请求到服务器的请求消息包括以下格式:请求行(request line)、请求头部(header)、空行和请求数据四个部分组成,下图给出了请求报文的一般格式:
在这里插入图片描述
请求行:[请求方法] + [URI] + [HTTP版本]
请求头部:请求的属性,这些属性都是以key: value的形式按行陈列的。
回车符换行符:遇到空行表示请求报头结束。
请求数据:请求正文允许为空字符串,如果请求数据存在,则在请求头部中会有一个Content-Length属性来标识请求正文的长度。

服务端响应

服务器响应客户端的HTTP响应也由四个部分组成,分别是:状态行、消息报头、空行和响应正文。
在这里插入图片描述
状态行:[HTTP版本] + [状态码] + [状态码描述]。
消息报头:响应的属性,这些属性都是以key: value的形式按行陈列的。
回车符换行符:遇到空行表示请响应报头结束。
响应正文:响应正文允许为空字符串,如果响应正文存在,则在响应报头中会有一个Content-Length属性来标识响应正文的长度。

响应代号代号描述
服务器上存在请求的内容,并可以响应给客户端200OK
客户端的请求有异常,方法有问题501Method Not Implemented
服务器收到请求后,因为自身的问题没法响应500Internal Server Error
请求的内容不存在404NOT FOUND
客户端发送的请求格式有问题等400BAD REQUEST

HTTP常见的Header

Content-Type:数据类型(text/html等)。
Content-Length:正文的长度。
Host:客户端告知服务器,所请求的资源是在哪个主机的哪个端口上。
User-Agent:声明用户的操作系统和浏览器的版本信息。
Referer:当前页面是哪个页面跳转过来的。
Location:搭配3XX状态码使用,告诉客户端接下来要去哪里访问。
Cookie:用户在客户端存储少量信息,通常用于实现会话(session)的功能。

实战整了一个后台服务,真香!

实现 Mini 型 http 服务器

接收http请求 解析http请求 响应http请求

在浏览器网址搜索栏输入 xxx/fei.html 登录到我的服务器端. xxx/fei.html 是我电脑的ip地址.在 linux shell 窗口 输入 ip addr 可获取本机ip
在这里插入图片描述
在这里插入图片描述

代码实现

#include <iostream> #include <unistd.h> #include <stdio.h> #include <sys/types.h> #include <sys/socket.h> #include <ctype.h> #include <arpa/inet.h> #include <string.h> #include <netinet/in.h> #include <errno.h> #include <sys/stat.h> #include <pthread.h>  #define SERVER_PORT 80  static int debug = 1;  using namespace std;  int get_line(int socket, char *buf, int size); void*  do_http_request(void* pclient_socket); void do_http_response(int client_sock, const char *path); void do_http_response1(int client_sock);  int headers(int client_sock, FILE *resource); void cat(int client_sock, FILE *resource); void inner_error(int client_sock); void unimplemented(int client_sock); void bad_request(int client_sock); void not_found(int client_sock);  int main(void){      int sock;//代表信箱     struct sockaddr_in server_addr;       //1.美女创建信箱     sock = socket(AF_INET, SOCK_STREAM, 0);      //2.清空标签,写上地址和端口号     bzero(&server_addr, sizeof(server_addr));      server_addr.sin_family = AF_INET;//选择协议族IPV4     server_addr.sin_addr.s_addr = htonl(INADDR_ANY);//监听本地所有IP地址     server_addr.sin_port = htons(SERVER_PORT);//绑定端口号      //实现标签贴到收信得信箱上     bind(sock, (struct sockaddr *)&server_addr,  sizeof(server_addr));      //把信箱挂置到传达室,这样,就可以接收信件了     listen(sock, 128);      //万事俱备,只等来信     printf("等待客户端的连接\n");       int done =1;      while(done){         struct sockaddr_in client;         int client_sock, len, i;         char client_ip[64];         char buf[256];          pthread_t id;         int* pclient_sock = NULL;          socklen_t  client_addr_len;         client_addr_len = sizeof(client); 	    printf("come here!\n");         client_sock = accept(sock, (struct sockaddr *)&client, &client_addr_len);          //打印客服端IP地址和端口号         printf("client ip: %s\t port : %d\n",                  inet_ntop(AF_INET, &client.sin_addr.s_addr,client_ip,sizeof(client_ip)),                  ntohs(client.sin_port));         /*处理http请求,读取客户端的数据*/ 		// do_http_request(client_sock);          //启动线程处理 http 请求         pclient_sock = (int*)malloc(sizeof(int));         *pclient_sock = client_sock;         pthread_create(&id, NULL, do_http_request, (void *)pclient_sock);  		// close(client_sock);     }  	close(sock); 	return 0; }  void* do_http_request(void* pclient_socket){     /*read client http request*/     int len=0;     char buf[256];     char method[64];     char url[256];     char path[512];     struct stat st;      int client_socket = *(int*)pclient_socket;       //1.read col of request     len = get_line(client_socket, buf, sizeof(buf));      if(len>0){  //read col of requests         int i=0, j=0;         while(!isspace(buf[j]) && (i < sizeof(method)-1)){             method[i] = buf[j];             i++;             j++;         }          method[i] = '\0';         if(debug){             cout << "request method: " << method << endl;         }          if(strncasecmp(method, "GET", i) == 0){   //Only handle get requests             if(debug){                 cout<<"method = GET" <<endl;             }              // get url             while(isspace(buf[j++])); //ignore space              i=0;              while(!isspace(buf[j]) && (sizeof(url)-1)){                 url[i] = buf[j];                 i++;                 j++;             }              url[i] = '\0';              if(debug){                 cout<<"url: "<<url<<endl;             }              // continue to read the header of http             do{                 len = get_line(client_socket, buf, sizeof(buf));                 if(debug){                     cout<<"read: "<< buf<<endl;                 }             }while(len>0);              // *** Locate the local HTML address of the server***             // Handle the "?" in the URL             {                 char *pos = strchr(url, '?');                 if(pos){                     *pos = '\0';                     cout<<"real url: "<< url <<endl;                 }              }              sprintf(path, "./html_docs/%s", url);             if(debug){                 cout<<"path: "<< path <<endl;             }              //  Execute HTTP response             //  判断文件是否存在,如果存在就响应200 OK,同时发送相应的html文件,如果不存在,就响应404 NOT FOUND.             if(-1 == stat(path, &st)){     // if file not exist                 fprintf(stderr, "stat %s failed. reason: %s\n", path, strerror(errno));                 not_found(client_socket);             }else{                 if(S_ISDIR(st.st_mode)){                     strcat(path, "/index.html");    // is directory?                 }                 //do_http_response1(client_socket);                 do_http_response(client_socket, path);                 }           }else{  // no get requests, read all http headers and Response server 501 "Method Not Implemented"             fprintf(stderr, "warning! other request [%s]\n", method);             do{                 len = get_line(client_socket, buf, sizeof(buf));                 if(debug){                     cout<<"read: "<< buf<<endl;                 }             }while(len>0);              unimplemented(client_socket);  //         }         }else{  // fialed to request         // bad request         bad_request(client_socket);      }       close(client_socket);     if(pclient_socket) free(pclient_socket);    //释放动态分配的内存      return NULL;      }  void unimplemented(int client_sock){ 	const char*reply= "HTTP/1.0 501 unimplemented\r\n\ 	Content-Type: text/html\r\n\ 	\r\n\ 	<HTML lang=\"zh-CN\">\r\n\ 	<meta content=\"text/html; charset=utf-8\" http-equiv=\"Content-Type\">\r\n\ 	<HEAD>\r\n\ 	<TITLE>Mehod Not Implemented</TITLE>\r\n\ 	</HEAD>\r\n\ 	<BODY>\r\n\ 	<P>unimplemented\r\n\     <P>The server could not fulfill your request because the resource specified is unavailable or nonexistent.\r\n\ 	</BODY>\r\n\ 	</HTML>";  	int len = write(client_sock,reply,strlen(reply)); 	if(debug) fprintf(stdout,reply); 	 	if(len<=0) fprintf(stderr,"send reply failed.reason:%s\n",strerror(errno)); 	 }  void bad_request(int client_sock){ 	const char*reply= "HTTP/1.0 400 bad_request\r\n\ 	Content-Type: text/html\r\n\ 	\r\n\ 	<HTML lang=\"zh-CN\">\r\n\ 	<meta content=\"text/html; charset=utf-8\" http-equiv=\"Content-Type\">\r\n\ 	<HEAD>\r\n\ 	<TITLE>Bad Requst</TITLE>\r\n\ 	</HEAD>\r\n\ 	<BODY>\r\n\ 	<P>Bad Request!\r\n\     <P>The server could not fulfill your request because the resource specified is unavailable or nonexistent.\r\n\ 	</BODY>\r\n\ 	</HTML>";  	int len = write(client_sock,reply,strlen(reply)); 	if(debug) fprintf(stdout,reply); 	 	if(len<=0) fprintf(stderr,"inner reply failed.reason:%s\n",strerror(errno)); 	 }  void do_http_response(int client_sock, const char *path){     FILE *resource = NULL;     int ret = 0;      resource = fopen(path,"r");      if(resource == NULL){         not_found(client_sock);         return;     }      // 1.send http header     ret = headers(client_sock, resource);      // 2.send http body     if(!ret){         cat(client_sock, resource);     }      fclose(resource);  }  /* 返回关于响应文件信息的 http 头部 输入: client_sock:客服端socket的句柄 resource:文件的句柄 返回值:success->0 fail->-1 */  int headers(int client_sock, FILE *resource){     struct stat st;     int fileid = 0;     char buf[1024]={0};     char tmp[64];     strcpy(buf,"HTTP/1.0 200 OK\r\n"); 	strcat(buf,"Server: Martin Server\r\n"); 	strcat(buf,"Content-Type: text/html\r\n"); 	strcat(buf,"Connection: Close\r\n");      fileid = fileno(resource);      if(fstat(fileid, &st) == -1){         inner_error(client_sock);         return -1;     }      snprintf(tmp, 64, "Content-Length: %d\r\n\r\n", st.st_size);     strcat(buf,tmp);      if(debug){         fprintf(stdout, "header: %s\n", buf);     }      if(send(client_sock, buf, strlen(buf), 0)<0){         fprintf(stderr, "send failed. data: %s, reason: %s\n", buf, strerror(errno));         return -1;     }      return 0;  }   // 说明:实现将html文件的内容按照行读取并发送客户端 void cat(int client_sock, FILE *resource){     char buf[1024];      fgets(buf, sizeof(buf), resource);      while(!feof(resource)){         int len = write(client_sock, buf, strlen(buf));          if(len<0){             fprintf(stderr, "send body error. reason: %s\n",strerror(errno));             break;         }          if(debug) fprintf(stdout, "%s", buf);         fgets(buf, sizeof(buf), resource);      }   }  void do_http_response1(int client_sock){     	//响应http 	const char *main_header = "HTTP/1.0 200 OK\r\nServer: Martin Server\r\nContent-Type: text/html\r\nConnection: Close\r\n"; 	const char *welcome_content = "\ 	<html lang=\"zh-CN\">\n\ 	<head>\n\ 	<meta content=\"text/html; charset=utf-8\" http-equiv=\"Content-Type\">\n\ 	<title>This is a test</title>\n\ 	</head>\n\ 	<body>\n\ 	<div align=center height=\"500px\" >\n\ 	<br/><br/><br/>\n\ 	<h2>大家好!</h2><br/><br/>\n\ 	<form action=\"commit\" method=\"post\">\n\ 	尊姓大名: <input type=\"text\" name=\"name\" />\n\ 	<br/>芳龄几何: <input type=\"password\" name=\"age\" />\n\ 	<br/><br/><br/><input type=\"submit\" value=\"提交\" />\n\ 	<input type=\"reset\" value=\"重置\" />\n\ 	</form>\n\ 	</div>\n\ 	</body>\n\ 	</html>"; 	 	//1.送main_header  	int len = write(client_sock, main_header, sizeof(main_header));      if(debug) fprintf(stdout, "...do http response...\n");     if(debug) fprintf(stdout, "write[%d]:%s", len, main_header);      //2.生成Content-Length 	char send_buf[64];     int wc_len = strlen(welcome_content); 	len = snprintf(send_buf,64,"Content-Length: %d\r\n\r\n",wc_len); 	len = write(client_sock,send_buf,len);      if(debug) fprintf(stdout, "write[%d]: %s", len, send_buf);   	len = write(client_sock,welcome_content,wc_len);     if(debug) fprintf(stdout, "write[%d]: %s", len, welcome_content);  }    int get_line(int socket, char *buf, int size){     int count = 0;     char ch = '\0';     int len = 0;      while ((count < size -1)&& ch != '\n'){         len = read(socket, &ch, 1);          if(1 == len){    // normal                          if('\r' == ch){                 continue;             }else if('\n' == ch){                 buf[count] = '\0';                 break;             }              buf[count] = ch;             count++;          }else if(-1 == len){    //  abnormal             perror("read failed");             count = -1;             break;         }else{  //read return 0: client has closed socket             fprintf(stderr, "client close\n");             count = -1;             break;         }      }      if(count>=0) buf[count] = '\0';      return count;   //count=-1:error!! count=0:read a Blank row; count > 0: read a row success!      }   void not_found(int client_sock){ 	const char*reply= "HTTP/1.0 404 NOT FOUND\r\n\ 	Content-Type: text/html\r\n\ 	\r\n\ 	<HTML lang=\"zh-CN\">\r\n\ 	<meta content=\"text/html; charset=utf-8\" http-equiv=\"Content-Type\">\r\n\ 	<HEAD>\r\n\ 	<TITLE>NOT FOUND</TITLE>\r\n\ 	</HEAD>\r\n\ 	<BODY>\r\n\ 	<P>文件不存在!\r\n\     <P>The server could not fulfill your request because the resource specified is unavailable or nonexistent.\r\n\ 	</BODY>\r\n\ 	</HTML>";  	int len = write(client_sock, reply, strlen(reply)); 	if(debug) fprintf(stdout, reply); 	 	if(len<=0) fprintf(stderr,"send reply failed.reason:%s\n",strerror(errno)); }  void inner_error(int client_sock){ 	const char*reply= "HTTP/1.0 500 Internal Sever Error\r\n\ 	Content-Type: text/html\r\n\ 	\r\n\ 	<HTML lang=\"zh-CN\">\r\n\ 	<meta content=\"text/html; charset=utf-8\" http-equiv=\"Content-Type\">\r\n\ 	<HEAD>\r\n\ 	<TITLE>NOT FOUND</TITLE>\r\n\ 	</HEAD>\r\n\ 	<BODY>\r\n\ 	<P>服务器内部出错!\r\n\     <P>The server could not fulfill your request because the resource specified is unavailable or nonexistent.\r\n\ 	</BODY>\r\n\ 	</HTML>";  	int len = write(client_sock,reply,strlen(reply)); 	if(debug) fprintf(stdout,reply); 	 	if(len<=0) fprintf(stderr,"inner reply failed.reason:%s\n",strerror(errno)); 	 } 

html 文件
览器访问服务器成功后看到的页面效果

在当前文件夹建立文件 目录 mkdirhtml_docs 进入这个目录 建立一个名为 fei.html 的文件将下面的 html 文本放入

<html lang=\"zh-CN\"> <head> <meta content=\"text/html; charset=utf-8 bom\" http-equiv=\"Content-Type\"> <title>This is a test</title> </head> <body> <div align=center height=\"500px\" > <br/><br/><br/> <h2>Good guys! Welcome to China Service!</h2><br/><br/> <form action="commit" method="post"> 尊姓大名: <input type="text" name="name" /> <br/>芳龄几何: <input type="password" name="age" /> <br/><br/><br/><input type="submit" value="提交" /> <input type="reset" value="重置" /> </form> </div> </body> </html> 

通过c/c++在linux系统实现Mini型http服务器的并发访问

相关函数

bezro()

函数原型extern void bzero(void *s, int n)
头文件<string.h>
功能:置字符串s的前n个字节为零且包括‘\0
说明:无返回值

memset()

函数原型extern void *memset(void *buffer, int c, int count)
头文件<string.h>
功能:把buffer所指内存区域的前count个字节设置成c的值。

stat 函数与 struct stat 结构体

#include <sys/types.h> #include <sys/stat.h> #include <unistd.h>  int stat(const char *path, struct stat *buf); int fstat(int fd, struct stat *buf); int lstat(const char *path, struct stat *buf); 

作用:获取文件信息
path:文件的路径;
buf:传入的保存文件状态的指针,用于保存文件的状态;
返回值:成功返回0,失败返回-1,设置errno

struct stat {     dev_t     st_dev;     /* ID of device containing file */文件使用的设备号     ino_t     st_ino;     /* inode number */    索引节点号      mode_t    st_mode;    /* protection */  文件对应的模式,文件,目录等     nlink_t   st_nlink;   /* number of hard links */    文件的硬连接数       uid_t     st_uid;     /* user ID of owner */    所有者用户识别号     gid_t     st_gid;     /* group ID of owner */   组识别号       dev_t     st_rdev;    /* device ID (if special file) */ 设备文件的设备号     off_t     st_size;    /* total size, in bytes */ 以字节为单位的文件容量        blksize_t st_blksize; /* blocksize for file system I/O */ 包含该文件的磁盘块的大小        blkcnt_t  st_blocks;  /* number of 512B blocks allocated */ 该文件所占的磁盘块       time_t    st_atime;   /* time of last access */ 最后一次访问该文件的时间        time_t    st_mtime;   /* time of last modification */ /最后一次修改该文件的时间        time_t    st_ctime;   /* time of last status change */ 最后一次改变该文件状态的时间    }; 
    S_IFMT   0170000    文件类型的位遮罩     S_IFSOCK 0140000    套接字     S_IFLNK 0120000     符号连接     S_IFREG 0100000     一般文件     S_IFBLK 0060000     区块装置     S_IFDIR 0040000     目录     S_IFCHR 0020000     字符装置     S_IFIFO 0010000     先进先出 ​     S_ISUID 04000     文件的(set user-id on execution)位     S_ISGID 02000     文件的(set group-id on execution)位     S_ISVTX 01000     文件的sticky位 ​     S_IRUSR(S_IREAD) 00400     文件所有者具可读取权限     S_IWUSR(S_IWRITE)00200     文件所有者具可写入权限     S_IXUSR(S_IEXEC) 00100     文件所有者具可执行权限 ​     S_IRGRP 00040             用户组具可读取权限     S_IWGRP 00020             用户组具可写入权限     S_IXGRP 00010             用户组具可执行权限 ​     S_IROTH 00004             其他用户具可读取权限     S_IWOTH 00002             其他用户具可写入权限     S_IXOTH 00001             其他用户具可执行权限 ​     上述的文件类型在POSIX中定义了检查这些类型的宏定义:     S_ISLNK (st_mode)    判断是否为符号连接     S_ISREG (st_mode)    是否为一般文件     S_ISDIR (st_mode)    是否为目录     S_ISCHR (st_mode)    是否为字符装置文件     S_ISBLK (s3e)        是否为先进先出     S_ISSOCK (st_mode)   是否为socket     若一目录具有sticky位(S_ISVTX),则表示在此目录下的文件只能被该文件所有者、此目录所有者或root来删除或改名,在linux中,最典型的就是这个/tmp目录啦。 ​ 

st_mode 主要包含了 3 部分信息:
15-12 位保存文件类型
11-9 位保存执行文件时设置的信息
8-0 位保存文件访问权限

pthread

每个线程都有一个在进程中唯一的线程标识符,用一个数据类型 pthread_t 表示,该数据类型在 Linux 中就是一个无符号长整型数据。

int pthread_create (pthread_t *thread,pthread_attr_t *attr,void *(*start_routine)(void *),void *arg); 

作用:创建新的线程
返回值:若创建成功,返回0;若出错,则返回错误编号。
参数
thread 是线程标识符,但这个参数不是由用户指定的,而是由 pthread_create 函数在创建时将新的线程的标识符放到这个变量中。
attr 指定线程的属性,可以用 NULL 表示默认属性。
start_routine 指定线程开始运行的函数。
argstart_routine 所需要的参数,是一个无类型指针。

结束线程
当发生以下情形之一时,线程就会结束:
1.线程运行的函数return了,也就是线程的任务已经完成;
2.线程调用了 pthread_exit 函数;
3.其他线程调用 pthread_cancel 结束这个线程;
4.进程调用 exec()exit(),结束了;
5.main() 函数先结束了,而且 main() 自己没有调用 pthread_exit 来等所有线程完成任务。

当然,一个线程结束,并不意味着它的所有信息都已经消失------僵尸线程的问题。

malloc

#include<stdlib.h>  //或者 #include<malloc.h> 
extern void * malloc(unsigned int num_byte);       

功能:分配长度为num_byte字节的内存块
参数:需要分配的内存字节数,如果内存池中的可用内存可以满足这个需求,malloc就返回一个指向被分配的内存块起始位置的指针
返回值:如果分配成功则返回指向被分配内存的指针,否则返回空指针NULL

注意:
1.malloc函数返回的是void *类型,如果写成:p = malloc(sizeof(int));则程序无法通过编译,报错:“不能将void *赋值给int *类型变量”。所以必须通过(int *)来强制类型转换;
2.函数的实参为sizeof(int),用于指明一个整形数据需要的大小,如果写成: int * p =(int *)malloc(1);代码也能通过编译,但事实上只分配了1个字节大小的内存空间

malloc所分配的是一块连续的内存。

当内存不使用时,应使用free()函数将内存块释放:void free( void * pointer);
功能:释放内存
参数free 函数的参数要么是NULL,要么是一个先前从malloccallocrealloc返回的值。向free传递一个NULL参数不会产生任何效果。
返回值:无

常见的动态内存错误
1.对NULL指针进行解引用操作(忘记检查所请求的内存是否分配成功)
2.对分配的内存进行操作时越过了分配内存的边界
3.释放并非动态分配的内存(传递给free的指针必须是一个从malloc、calloc或realloc函数返回的指针)
4.试图释放一块动态分配的内存的一部分(释放一块内存的一部分是不允许的,动态分配的内存必须整块一起释放)
5.一块动态内存被释放之后被继续使用等。(不要访问已经被free函数释放了的内存,注意指针的复制)
C语言——malloc函数详解

转义字符

转义字符转义功能ASCII 码值
\0空字符0
\a响铃7
\b退格(Backspace)8
\t水平制表符(即横向跳格)9
\n换行(Enter)10
\v竖向跳格11
\f换页12
\r回车13

isspace()

int isspace(int c); 

作用:检查所传的字符是否是空白字符
返回值:如果 c 是一个空白字符,则该函数返回非零值(true),否则返回 0false

strncasecmp()

strcasecmp() 函数对字符串s1和s2执行逐字节比较,忽略字符的大小写

#include <strings.h> int strcasecmp(const char *s1, const char *s2); int strncasecmp(const char *s1, const char *s2, size_t n); 

作用:函数对字符串s1s2执行逐字节比较,忽略字符的大小写。如果发现s1分别小于、匹配或大于s2,则返回一个小于、等于或大于0的整数
函数输入值
s1 / s2 :对比字符串
n:对比个数
函数返回值
strcasecmp()函数在忽略大小写后
如果发现s1分别小于、匹配或大于s2,则返回一个小于、等于或大于零的整数。
(1)若参数s1s2字符串相同则返回0
(2)若参数s1大于s2,则返回大于0的值
(3)若参数s1小于s2,则返回小于0的值

strcat

#include <string.h> char *strcat(char *_Destination,const char *_Source) 

strcat函数是字符串追加函数,也就是在字符串后面追加另一个字符串。

在自己实现的时候就需要提前找到被追加的字符串的尾部,也就是找到'\0'

char* my_strcat(char* dest, const char* str) { 	char* ret = dest; 	assert(*dest != NULL); 	assert(*str); 	//找到目的字符串里的'\0' 	while (*dest != '\0') 	{ 		dest++; 	} 	//追加 	while (*dest++ = *str++) 	{ 		; 	} 	return ret; } 

fileno()

#include <stdio.h> int fileno(FILE *stream) 

函数说明:fileno()用来取得参数stream指定的文件流所使用的文件描述词
返回值:返回文件描述词

fgets()

# include <stdio.h> char *fgets(char *s, int size, FILE *stream); 

功能:是从 stream 流中读取 size 个字符存储到字符指针变量 s 所指向的内存空间。它的返回值是一个指针,指向字符串中第一个字符的地址。
参数
s 代表要保存到的内存空间的首地址,可以是字符数组名,也可以是指向字符数组的字符指针变量名。
size 代表的是读取字符串的长度。
stream 表示从何种流中读取,可以是标准输入流 stdin,也可以是文件流,即从某个文件中读取。

feof()

作用:用于判断文件指针是否指向文件末尾
返回值:当指向文件尾,返回1;否则返回0;

在文件读取过程中,不能用feof函数的返回值直接用来判断文件的是否结束!!!
feof的正确使用场景:feof应当用于,在文件读取已经结束的时候,判断是读取失败导致的结束还是遇到文件尾正常的结束(与ferror搭配)

编译文件

实现并发访问使用到线程函数 pthread_create 因此编译时要在 -o 的前面加上 pthread这个函数的库否则会报错。
在这里插入图片描述
在这里插入图片描述

广告一刻

为您即时展示最新活动产品广告消息,让您随时掌握产品活动新动态!