并发 MiniHttp 服务器

项目需求

实现一个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;//选择协议族IPV4server_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 requestlen = get_line(client_socket, buf, sizeof(buf));if(len>0){  //read col of requestsint 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 requestsif(debug){cout<<"method = GET" <<endl;}// get urlwhile(isspace(buf[j++])); //ignore spacei=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 httpdo{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 existfprintf(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 requestbad_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 headerret = headers(client_sock, resource);// 2.send http bodyif(!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){//响应httpconst 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_headerint 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-Lengthchar 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){    // normalif('\r' == ch){continue;}else if('\n' == ch){buf[count] = '\0';break;}buf[count] = ch;count++;}else if(-1 == len){    //  abnormalperror("read failed");count = -1;break;}else{  //read return 0: client has closed socketfprintf(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这个函数的库否则会报错。
在这里插入图片描述
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/817768.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

SQLite数据库文件格式(十五)

返回&#xff1a;SQLite—系列文章目录 上一篇:SQLite 4.9的虚拟表机制(十四) 下一篇&#xff1a;SQLite超详细的编译时选项&#xff08;十六&#xff09; ► 目录 本文档描述和定义磁盘上的数据库文件 自 SQLite 以来所有版本使用的格式 版本 3.0.0 &#xff08;2004-06-18…

HCIP的学习(8)

OSPF数据报文 OSPF头部信息&#xff08;公共固定&#xff09; 版本&#xff1a;OSPF版本&#xff0c;在IPv4网络中版本字段恒定为数值2&#xff08;v1属于实验室版本&#xff0c;v3属于IPv6&#xff09;类型&#xff1a;代表具体是哪一种报文&#xff0c;按照1~5排序&#xff…

AIGC 探究:人工智能生成内容的技术原理、广泛应用、创新应用、版权问题与未来挑战

AIGC&#xff08;Artificial Intelligence Generated Content&#xff09;即人工智能生成内容&#xff0c;其核心在于利用深度学习技术&#xff0c;尤其是基于神经网络的模型&#xff0c;来模拟人类创作过程&#xff0c;自主生成高质量的文本、图像、音频、视频等各类内容。神经…

GDPU Java 天码行空7

实验目的&#xff1a; 熟悉awt组件和三种基本布局&#xff0c;掌握按钮、文本框、标签、等基本控件的使用和事件响应编程。 熟悉布局管理器 1. 窗体关闭事件 &#x1f496; WindowCloseEvent.java import java.awt.event.WindowAdapter; // 导入WindowAdapter类&#xff0c;…

江新安教授受邀金佰利进行《研发创新与创新思维》培训

近日&#xff0c;科济管线创始人江新安教授受邀金佰利为金佰利全国研发团队进行《研发创新与创新思维》培训授课。创新思维的两种思维形式——非逻辑思维和逻辑思维及其训练的主要方法。创新思维的培养&#xff0c;对于克服思维定势&#xff0c;提供创新方法&#xff0c;可以发…

FreGS:具有渐进频率正则化的3D高斯溅射

FreGS: 3D Gaussian Splatting with Progressive Frequency Regularization FreGS&#xff1a;具有渐进频率正则化的3D高斯溅射 Jiahui Zhang1  Fangneng Zhan2  Muyu Xu1  Shijian Lu1  Eric Xing3, 4 张家慧 1 詹方能 2 许慕玉 1 卢世坚 1 邢志伟 3, 4 1Nanyang Technolo…

19岁第一份“贺礼”,美创蝉联“2023年度数字化赋能服务商”

在即将到来的19岁&#xff0c;美创科技迎来第一份生日“贺礼”&#xff0c;再次蝉联“2023年度数字化赋能服务商”&#xff01; 4月13日&#xff0c;以“数聚浙里 智领未来”为主题&#xff0c;由浙江省首席信息官协会主办的数字化转型与升级论坛暨CIO年度峰会于杭州隆重召开。…

[MySQL]数据库原理8——喵喵期末不挂科

希望你开心&#xff0c;希望你健康&#xff0c;希望你幸福&#xff0c;希望你点赞&#xff01; 最后的最后&#xff0c;关注喵&#xff0c;关注喵&#xff0c;关注喵&#xff0c;大大会看到更多有趣的博客哦&#xff01;&#xff01;&#xff01; 喵喵喵&#xff0c;你对我真的…

数字化仪:为何成为示波器的理想替代品?——PCIe8910M

在现代科技领域&#xff0c;数字化仪逐渐成为示波器的理想替代品。数字化仪具备诸多特点&#xff0c;使其在多个应用场景下表现出色&#xff0c;逐渐取代传统的示波器。本期文章将探讨数字化仪相对于示波器的优势&#xff0c;以及其哪些特点使其成为示波器的理想替代品。 简介…

【Datawhale LLM学习笔记】一、什么是大型语言模型(LLM)

文章目录 1. 什么是大模型2. 检索增强生成 RAG一、什么是 RAG二、RAG 的工作流程 3. langChain介绍一、什么是 LangChain二、LangChain 的核心组件 4. 开发 LLM 应用的整体流程一、何为大模型开发二、大模型开发的一般流程三、搭建 LLM 项目的流程简析&#xff08;以知识库助手…

产品推荐 | 基于Lattice CrossLink-NX FPGA语音和视觉机器学习板

01 产品概述 Lattice Semiconductor CrossLink-NX语音和视觉机器学习板是高性能 – 专为低功耗机器学习应用而设计的电路板&#xff0c;采用CrossLink-NX 40K&#xff0c;这是一款具有AI加速器的强大FPGA。 基于视觉和语音的机器学习应用 – 板载低功耗图像传感器和麦克风以及…

Linux数据库自动备份 - 定时任务发到百度云盘、坚果云、邮箱附件

前言 1. 坚果云的webdav云盘最好&#xff01; &#xff08;免费账号每月1G上传流量&#xff09; 2. 不建议数据库备份文件发送到SMTP邮箱&#xff0c;因为对方服务器非常容易当做垃圾邮件处理&#xff0c;而且发信的SMTP账号会被封禁&#xff08;实测163发到QQ邮箱被封&…

更改docker镜像下载地址

一.简介 使用指令 sudo docker info 查看本机的docker镜像下载地址为 由于本机的var文件空间不足&#xff0c;因此&#xff0c;想更改他的存储地址&#xff0c;如下 二.开始操作 1.停止Docker服务&#xff1a; 执行命令 sudo systemctl stop docker 以及 sudo systemctl s…

220V转3.3V线性降压芯片WT5100

220V转3.3V线性降压芯片WT5100 WT5100就是那个离线式的稳压器芯片&#xff0c;没有电感的那种。里面内置了650V的大功率MOSFET&#xff0c;启动控制电路&#xff0c;电压控制电路&#xff0c;交流信号检测电路等等。通过切换SEL管脚&#xff0c;它就能输出5V或3.3V的电压。不需…

一本免费开源的电子书籍!这个 71.8k star 的项目,让你轻轻松松学会算法【文末有福利】

话说作为一名程序员&#xff0c;肯定都少不了在准备面试的时候刷 LeetCode 的算法题吧。虽然面试考察的算法题在工作中用到的非常少&#xff0c;但是确实是能让我们对常用的数据结构有更深刻的理解&#xff0c;以及对思维逻辑有很大的提升。不过枯燥的刷题可能让新手无从下手&a…

【Linux网络编程】UDP协议

UDP协议 1.再谈端口号端口号划分认识知名端口号(Well-Know Port Number)两个问题netstatpidof 2.UDP协议2.1UDP的特点2.2面向数据报2.3UDP的缓冲区2.4UDP使用注意事项2.5基于UDP的应用层协议 喜欢的点赞&#xff0c;收藏&#xff0c;关注一下把&#xff01; 1.再谈端口号 端口…

计算方法实验5:对鸢尾花数据集进行主成分分析(PCA)并可视化

任务 iris数据集包含150条数据&#xff0c;从iris.txt读取&#xff0c;每条数据有4个属性值和一个标签&#xff08;标签取值为0&#xff0c;1&#xff0c;2&#xff09;。要求对这150个4维数据进行PCA&#xff0c;可视化展示这些数据在前两个主方向上的分布&#xff0c;其中不…

笔记84:关于递归法的一些感悟

题目1&#xff1a;二叉树的前序遍历 链接&#xff1a;. - 力扣&#xff08;LeetCode&#xff09; /*** Definition for a binary tree node.* struct TreeNode {* int val;* TreeNode *left;* TreeNode *right;* TreeNode() : val(0), left(nullptr), right(…

京东详情比价接口优惠券(2)

京东详情API接口在电子商务中的应用与作用性体现在多个方面&#xff0c;对于电商平台、商家以及用户都带来了显著的价值。 首先&#xff0c;从应用的角度来看&#xff0c;京东详情API接口为开发者提供了一整套丰富的功能和工具&#xff0c;使他们能够轻松地与京东平台进行交互。…

后台运行程序时报错

问题描述&#xff1a;使用pycharm连接服务器运行程序时&#xff0c;可以正常运行。但是使用Termius终端运行时报错&#xff08;运行时切换到和pycharm相同的路径&#xff09;。 2024-04-15 14:35:01.663900: I external/local_xla/xla/stream_executor/cuda/cuda_dnn.cc:454] …