并发 MiniHttp 服务器

服务器 0

项目需求

实现一个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 80static 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 文件
览器访问服务器成功后看到的页面效果

在当前文件夹建立文件 目录 mkdir html_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这个函数的库否则会报错。
在这里插入图片描述
在这里插入图片描述

也许您对下面的内容还感兴趣: