热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Linux下是实现的 HTTP 服务器

项目功能:

项目功能:

(1)能接收客户端的GET请求;

(2)能够解析客户端的请求报文,根据客户端要求找到相应的资源;

(2)能够回复http应答报文;

(3)能够读取服务器中存储的文件,并返回给请求客户端,实现对外发布静态资源;

(4)使用I/O复用来提高处理请求的并发度;

(5)服务器端支持错误处理,如要访问的资源不存在时回复404错误等。

1.http协议

1.客户端请求消息

客户端有get请求和post请求

过程:浏览器->发给->服务器,客户端(浏览器)发送一个HTTP请求到服务器的请求消息包括以下格式:请求行(request line)、请求头部(header)、空行和请求数据四个部分组成

步骤:

  • 请求行:说明请求类型,要访问的资源,以及使用的 http 版本

  • 请求头:说明服务器要使用的附加信息,每一行都需要 \r\n 表示某一个属性结束

  • 空 行:必须!,即使没有请求数据,其实就是  \r\n

  • 请求数据:也叫主体,可以添加任意的其他数据,是客户端需要的数据,由服务器发送

注意:在连续读取http请求头部时,如果碰到两个连续的回车换行,即表示请求头部结束

浏览器请求的内容样例

浏览器请求:
GET /demo.html HTTP/1.1
Host: 47.100.162.191
Connection: keep-alive
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.26 Safari/537.36 Core/1.63.6788.400 QQBrowser/10.3.2767.400
Upgrade-Insecure-Requests: 1
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
COOKIE:cna=BT0+EoVi1FACAXH3Nv5I7h6k;isg=BIOD99I03BNYvZDfE2FJUOsMB0ftUBZcBFi4E7VgYOJZdKOWPcvRinAl6kSfVG8y

2.服务器响应消息

服务器获取浏览器的状态消息

过程:服务器->发给->浏览器

步骤:

  • 状态行: 包括 http 协议版本号,状态码,状态信息

  • 消息报头: 说明客户端要使用的一些附加信息

  • 空 行:必须!

  • 响应正文:服务器返回给客户端的文本信息

服务器响应的内容样例

服务器响应:
HTTP/1.0 200 OK
Server: Martin Server
Content-Type: text/html
Connection: Close
Content-Length: 526



一些响应代号及代号描述

响应代号

代号描述

服务器上存在请求的内容,并可以响应给客户端

200

OK

客户端的请求有异常,方法有问题

501

Method Not Implemented

服务器收到请求后,因为自生的问题没法响应

500

Internal Server Error

请求的内容不存在

404

NOT FOUND

客户端发送的请求格式有问题等

400

BAD REQUEST

3.使用到的一些函数及结构体

1.stat和fstat函数

函数原型:int stat(const char *pathname, struct stat *statbuf);

   int fstat(int fd, struct stat *statbuf);

int stat(const char *pathname, struct stat *statbuf);作用:获取文件信息包含在头文件:include #include #include 返回值:成功返回0,失败返回-1参数解释:参数pathname:表示文件路径参数statbuf:struct stat类型的结构体int fstat(int fd, struct stat *statbuf);作用:由文件描述符获取文件的状态包含的头文件:#include    #include 参数解释:参数fd:表示是已经打开的文件描述符参数statbuf:是struct stat类型的结构体返回值:执行成功返回0,失败返回-1结构体原型:
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 */ 最后一次改变该文件状态的时间
};st_mode包含了三部分的信息:1. 15-12位保存了文件类型2. 11-9位保存了执行文件时设置的信息3. 8-0位保存文件访问权限在c库中定义的S_ISDIR函数:S_ISDIR(statbuf.st_mode)作用:判断一个路径是否是一个目录返回值:不是路径返回非零,是路径返回0

2.isspace函数

函数原型:int isspace(int c);

int isspace(int c);  包含在头文件:#include 作用:判断字符c是否为空白符(空白符指空格、水平制表、垂直制表、换页、回车和换行符。)返回值:成功返回非0,失败返回0

3.strncasecmp函数

函数原型:int strncasecmp (const char *s1, const char *s2, size_t count);

int strncasecmp (const char *s1, const char *s2, size_t count);作用:判断字符串指定长度的字符是否相等,忽略大小写包含的头文件:#include返回值:小于0:表示s1大于s2等于0:表示s1等于s2大于0:表示s1大于s2

4.strchr函数

函数原型:char *strstr( const char *string, const char *strCharSet );

char *strstr( const char *string, const char *strCharSet );作用:用于判断字符串strCharSet是否是string的子串返回值:如果是子串,则返回字串strCharSet在字符串string中第一个出现的位置到结尾的字符串,否则返回NULL,返回值是一个指针,返回的是子串在原字符串中第一个出现的位置,如果没有找到返回NULL

5.snprintf函数

函数原型:int snprintf(char *str, size_t size, const char *format, ...)

作用:如果格式化后的字符串长度 = size,则只将其中的(size-1)个字符复制到str中,并给其后添加一个字符串结束符('\0'),返回值为欲写入的字符串长度。

6.fprintf函数

函数原型:intfprintf( FILE *stream, constchar *format, ... );

包含的头文件:#include int fprintf( FILE *stream, const char *format, ... );作用:根据指定的format(格式)发送信息(参数)到由stream(流)指定的文件.因此fprintf()可以使得信息输出到指定的文件fprintf()和printf()一样工作. printf是打印输出到屏幕,fprintf是打印输出到文件。 fprintf()的返回值是输出的字符数,发生错误时返回一个负值.

7.fileno函数

函数原型:int fileno(FILE *stream);

int fileno(FILE *stream);  包含在头文件 作用:把文件流指针转换成文件描述符参数stream:是指定的文件路径返回值:成功返回的是stream对应的文件描述符id,失败返回-1

8.pthread_create函数

函数原型:int pthread_create(pthread_t *thread, const pthread_attr_t *attr,

void *(*start_routine) (void *), void *arg);

int pthread_create(pthread_t *restrict tidp,const pthread_attr_t *restrict attr,void *(*start_rtn)(void), void *restrict arg);作用:创建一个线程包含的头文件:在linux下编译时,如果程序使用到pthread.h头文件中的函数,需要加  -lpthread 选项例如:gcc Minihttp.c -o minihttp -lpthread参数解释:第一个参数为指向线程标识符的指针。第二个参数用来设置线程属性。第三个参数是线程运行函数的起始地址。最后一个参数是运行函数的参数

4.实现一个简单http服务器的步骤

1.创建用于连接的服务器socket套接字

2.处理客户端连接请求的消息,把请求行和请求头部的内容读取出来,并且判断客户端实现的是get请求还是post请求

3.服务器响应客户端的请求,如果客户端申请的是一个网页,那就需要在服务器编写出符合http协议的请求行和请求头,并且发送给客户端,再把客户端请求的内容发送给浏览器;如果客户端申请的不是网页,那就不需要发送请求头和请求行,直接发送内容即可。

5.实现了简单的http服务器的代码

1.使用了epoll实现的http服务器

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#includeint init_listen_fd(int port, int epfd)
{//创建监听的套接字int lfd = socket(AF_INET, SOCK_STREAM, 0);if (lfd == -1){perror("socket error\n");exit(1);}struct sockaddr_in srv_addr;bzero(&srv_addr, sizeof(srv_addr));srv_addr.sin_family = AF_INET;srv_addr.sin_port = htons(port);srv_addr.sin_addr.s_addr = htonl(INADDR_ANY);//端口复用int opt = -1;setsockopt(lfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));//给lfd绑定结构体int ret = bind(lfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr));if (ret == -1){perror("bind error\n");exit(1);}//设置监听上限ret = listen(lfd, 128);if (ret == -1){perror("listen error\n");}//将lfd 添加到epoll树上struct epoll_event ev;ev.events = EPOLLIN;ev.data.fd = lfd;ret = epoll_ctl(epfd, EPOLL_CTL_ADD, lfd, &ev);if (ret == -1){perror("epoll_ctl error\n");exit(1);}return lfd;
}void do_accept(int lfd, int epfd)
{struct sockaddr_in clt_addr;socklen_t clt_addr_len = sizeof(clt_addr);int cfd = accept(lfd, (struct sockaddr*)&clt_addr, &clt_addr_len);if (cfd == -1){perror("accept error\n");exit(1);}//打印客户端IP+PORTchar client_ip[64] = { 0 };printf("New Client IP:%s, Port:%d, cfd = %d\n",inet_ntop(AF_INET, &clt_addr.sin_addr.s_addr, client_ip, sizeof(client_ip)), ntohs(clt_addr.sin_port), cfd);//设置cfd非阻塞int flag = fcntl(cfd, F_GETFL);flag |= O_NONBLOCK;fcntl(cfd, F_SETFL, flag);//将新节点cfd挂上epoll上struct epoll_event ev;ev.data.fd = cfd;//边缘非阻塞模式ev.events = EPOLLIN | EPOLLET;int ret = epoll_ctl(epfd, EPOLL_CTL_ADD, cfd, &ev);if (ret == -1){perror("epoll_ctl add cfd error\n");exit(1);}
}//获取一行\r\n结尾的数据
int get_line(int cfd, char* buf, int size)
{int i = 0;char c = '\0';int n;while ((i 0){if (c == '\r'){n = recv(cfd, &c, 1, MSG_PEEK);if ((n > 0) && (c == '\n')){recv(cfd, &c, 1, 0);}else{c = '\n';}}buf[i] = c;i++;}else{c = '\n';}}buf[i] = '\0';if (n == -1){i = n;}return i;
}//断开链接
void disconnect(int cfd, int epfd)
{int ret = epoll_ctl(epfd, EPOLL_CTL_DEL, cfd, NULL);if (ret != 0){perror("epoll_ctl error\n");exit(1);}close(cfd);
}void send_respond(int cfd, //客户端的socketint no, //错误号char* disp, //错误描述char* type, //回发文件类型int len) //文件长度
{//拼写出http协议char buf[1024] = { 0 };sprintf(buf, "HTTP/1.1 %d %s\r\n", no, disp);sprintf(buf+strlen(buf), "%s\r\n", type);sprintf(buf + strlen(buf), "Content_Length:%d\r\n", len);send(cfd, buf, strlen(buf), 0);send(cfd, "\r\n", 2, 0);
}//把本地文件内容发生给浏览器
void send_file(int cfd, const char* file)
{int n = 0;char buf[1024];int fd = open(file, O_RDONLY);//打开的服务器文件if (fd == -1){//404错误页面perror("oepn error");exit(1);}while ((n = read(fd, buf, sizeof(buf))) > 0)//获取文件中的数据{send(cfd, buf, n, 0);//}close(fd);
}//处理http请求,判断文件是否存在,存在则把内容发给浏览器
void http_request(int cfd, const char* file)
{struct stat sbuf;//判断文件是否存在int ret = stat(file, &sbuf);if (ret != 0){perror("stat");exit(1);}if (S_ISREG(sbuf.st_mode))//判断是一个普通文件{// 回发 http协议应答send_respond(cfd, 200, "OK", "Content-Type: text/plain; charset=iso-8859-1", sbuf.st_size);//回发 给客户端请求数据内容send_file(cfd, file);}
}void do_read(int cfd, int epfd)
{//读取一行http协议,拆分,获取get文件名 协议号char line[1024] = { 0 };int len = get_line(cfd, line, sizeof(line));//读取 请求协议首行 if (len == 0){printf("服务器,检查到客户端关闭.....\n");disconnect(cfd, epfd);}else{char method[16] = { 0 };char path[256] = { 0 };char protocol[16] = { 0 };sscanf(line, "%[^ ] %[^ ] %[^ ]", method, path, protocol);//分割首行printf("method=%s, path=%s, protocol=%s\n", method, path, protocol);while (1){char buf[1024] = { 0 };len = get_line(cfd, buf, sizeof(buf));//把缓冲区的剩下内容读走if (len == '\n' || len == -1){break;}}if (strncasecmp(method, "GET", 3) == 0)//比较字符串,参数3表示比较几个{char* file = path + 1; //取出 客户端要访问的文件名http_request(cfd, file);//读出文件内容}}
}void epoll_run(int port)
{int i = 0;struct epoll_event all_events[128];//创建一个epoll监听树根int epfd = epoll_create(128);if (epfd == -1){perror("epoll_create error\n");exit(1);}//创建lfd,并添加到监听树根int lfd = init_listen_fd(port, epfd);while (1){//监听节点对应事件int ret = epoll_wait(epfd, all_events, 128, -1);if (ret == -1){perror("epoll_wait error\n");exit(1);}for (i = 0; i events & EPOLLIN)){continue;}if (pev->data.fd == lfd){//接受链接请求do_accept(lfd, epfd);}else{//读事件处理do_read(pev->data.fd, epfd);}}}
}int main(int argc, char* argv[])
{//命令行参数获取 端口 和 server提供的目录if (argc <3){printf("./server port path\n");}//获取端口号int port &#61; atoi(argv[1]);//改变进程工作目录int ret &#61; chdir(argv[2]);if (ret !&#61; 0){perror("chdir error");exit(1);}//启动epoll监听epoll_run(port);return 0;
}

2.使用线程实现的http服务器

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include static int debug &#61; 1;int get_line(int sock, char* buf, int size);//获取客户端请求的内容
void* do_http_request(void* pclient_sock);//作为线程的回调函数
void do_http_response(int client_sock, const char* path);//响应客户端请求
int headers(int client_sock, FILE* resource);//给客户端发送请求头数据
void cat(int client_sock, FILE* resource);//给客户端发送html数据
void unimplemented(int client_sock);//请求没有被实现
void not_found(int client_sock);//如果没有找到就执行这个
void inner_error(int client_sock);//服务器内部出错
void bad_request(int client_sock);//请求出错int main()
{int sock;//代表信箱struct sockaddr_in server_addr;//1.美女创建信箱sock &#61; socket(AF_INET, SOCK_STREAM, 0);//2.清空标签&#xff0c;写上地址和端口号bzero(&server_addr, sizeof(server_addr));server_addr.sin_family &#61; AF_INET;//选择协议族IPV4server_addr.sin_addr.s_addr &#61; htonl(INADDR_ANY);//监听本地所有IP地址server_addr.sin_port &#61; htons(8888);//绑定端口号//实现标签贴到收信得信箱上int ret &#61; bind(sock, (struct sockaddr*)&server_addr, sizeof(server_addr));if(ret &#61;&#61; -1){perror("bind error");exit(1);}//把信箱挂置到传达室&#xff0c;这样&#xff0c;就可以接收信件了ret &#61; listen(sock, 128);if(ret &#61;&#61; -1){perror("bind error");exit(1);}//设置端口复用int opt &#61; 1;setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void*)&opt, sizeof(opt));//万事俱备&#xff0c;只等来信printf("等待客户端连接\n");while (1) {struct sockaddr_in client;bzero(&client, sizeof(client));int client_sock, len, i;char client_ip[128];char buf[256];pthread_t id;//线程idint* pclient_sock &#61; NULL;socklen_t client_addr_len;client_addr_len &#61; sizeof(client);client_sock &#61; 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 请求,读取客户端发送的数据*/pclient_sock &#61; (int*)malloc(sizeof(int));//分配空间*pclient_sock &#61; client_sock;//使用线程处理请求pthread_create(&id, NULL, do_http_request, (void*)pclient_sock);//并行}close(sock);return 0;
}//1.读取请求行
void* do_http_request(void* pclient_sock)
{int len &#61; 0;char buf[256];char method[64];char url[256];char path[256];int client_sock &#61; *(int*)pclient_sock;//解参数的引用struct stat st;/*读取客户端发送的http 请求*///1.读取请求行len &#61; get_line(client_sock, buf, sizeof(buf));if (len > 0) {//读到了请求行int i &#61; 0, j &#61; 0;while (!isspace(buf[j]) && (i 0);//***定位服务器本地的html文件***//处理url 中的?{char* pos &#61; strchr(url, &#39;?&#39;);if (pos) {*pos &#61; &#39;\0&#39;;printf("real url: %s\n", url);}}sprintf(path, "./html_docs/%s", url);if (debug) printf("path: %s\n", path);//执行http 响应//判断文件是否存在&#xff0c;如果存在就响应200 OK&#xff0c;同时发送相应的html 文件,如果不存在&#xff0c;就响应 404 NOT FOUND.if (stat(path, &st) &#61;&#61; -1) {//文件不存在或是出错fprintf(stderr, "stat %s failed. reason: %s\n", path, strerror(errno));not_found(client_sock);}else {//文件存在if (S_ISDIR(st.st_mode)) {//S_SIDIR判断一个路径是否是目录strcat(path, "/index.html");}do_http_response(client_sock, path);}}else {//非get请求, 读取http 头部&#xff0c;并响应客户端 501 Method Not Implementedfprintf(stderr, "warning! other request [%s]\n", method);do {len &#61; get_line(client_sock, buf, sizeof(buf));if (debug) printf("read: %s\n", buf);} while (len > 0);unimplemented(client_sock); //请求未实现}}else {//请求格式有问题&#xff0c;出错处理bad_request(client_sock); //在响应时再实现}close(client_sock);//关闭sockif (pclient_sock) free(pclient_sock);//释放动态分配的内存return NULL;
}void do_http_response(int client_sock, const char* path) {int ret &#61; 0;FILE* resource &#61; NULL;resource &#61; fopen(path, "r");if (resource &#61;&#61; NULL) {not_found(client_sock);return;}//1.发送http 头部ret &#61; headers(client_sock, resource);////2.发送http body .if (!ret) {cat(client_sock, resource);//把文件内容一行一行读取}fclose(resource);
}/*****************************返回关于响应文件信息的http 头部*输入&#xff1a;* client_sock - 客服端socket 句柄* resource - 文件的句柄*返回值&#xff1a; 成功返回0 &#xff0c;失败返回-1
******************************/
int headers(int client_sock, FILE* resource) {struct stat st;int fileid &#61; 0;char tmp[64];char buf[1024] &#61; { 0 };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 &#61; fileno(resource);//获取到指定文件的文件描述符if (fstat(fileid, &st) &#61;&#61; -1) {//获取文件状态失败inner_error(client_sock);return -1;}snprintf(tmp, 64, "Content-Length: %ld\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) {//给浏览器发送http请求的数据fprintf(stderr, "send failed. data: %s, reason: %s\n", buf, strerror(errno));return -1;}return 0;
}//返回值&#xff1a; -1 表示读取出错&#xff0c; 等于0表示读到一个空行&#xff0c; 大于0 表示成功读取一行
int get_line(int sock, char* buf, int size)
{int count &#61; 0;//表示已经读取的字符char ch &#61; &#39;\0&#39;;//表示字符串结束符int len &#61; 0;//当前读取的字符while ((count &#61; 0) buf[count] &#61; &#39;\0&#39;;//结束符return count;//返回读取的数量
}void not_found(int client_sock) {const char* reply &#61; "HTTP/1.0 404 NOT FOUND\r\n\Content-Type: text/html\r\n\\r\n\\r\n\\r\n\\r\n\\r\n\\r\n\\r\n\

文件不存在&#xff01;\r\n\

The server could not fulfill your request because the resource specified is unavailable or nonexistent.\r\n\\r\n\";int len &#61; write(client_sock, reply, strlen(reply));if (debug) fprintf(stdout, reply);if (len <&#61; 0) {fprintf(stderr, "send reply failed. reason: %s\n", strerror(errno));}}void unimplemented(int client_sock)
{const char* reply &#61; "HTTP/1.0 501 Method Not Implemented\r\n\Content-Type: text/html\r\n\\r\n\\r\n\\r\n\\r\n\\r\n\\r\n\

HTTP request method not supported.\r\n\\r\n\";int len &#61; write(client_sock, reply, strlen(reply));if (debug) fprintf(stdout, reply);if (len <&#61; 0) {fprintf(stderr, "send reply failed. reason: %s\n", strerror(errno));}
}void cat(int client_sock, FILE* resource)
{char buf[1024];fgets(buf, sizeof(buf), resource);//读取一行while (!feof(resource)) {int len &#61; write(client_sock, buf, strlen(buf));//获取发送给客户端的字符串长度if (len <0) {//发送body 的过程中出现问题,怎么办&#xff1f;1.重试&#xff1f; 2.fprintf(stderr, "send body error. reason: %s\n", strerror(errno));break;}if (debug) fprintf(stdout, "%s", buf);fgets(buf, sizeof(buf), resource);}
}void inner_error(int client_sock)
{const char* reply &#61; "HTTP/1.0 500 Internal Sever Error\r\n\Content-Type: text/html\r\n\\r\n\\r\n\\r\n\\r\n\\r\n\\r\n\\r\n\

服务器内部出错.\r\n\\r\n\";int len &#61; write(client_sock, reply, strlen(reply));if (debug) fprintf(stdout, reply);if (len <&#61; 0){fprintf(stderr, "send reply failed. reason: %s\n", strerror(errno));}
}void bad_request(int client_sock)
{const char* reply &#61; "HTTP/1.0 400 BAD REQUEST\r\n\Content-Type: text/html\r\n\\r\n\\r\n\\r\n\\r\n\\r\n\\r\n\

Your browser sent a bad request&#xff01;\r\n\\r\n\";int len &#61; write(client_sock, reply, strlen(reply));if (len <&#61; 0) {fprintf(stderr, "send reply failed. reason: %s\n", strerror(errno));}
}


推荐阅读
  • 可转债数据智能抓取与分析平台优化
    本项目旨在优化可转债数据的智能抓取与分析平台。通过爬取集思录上的可转债信息(排除已发布赎回的债券),并结合安道全教授提出的三条安全线投资策略,新增了建仓线、加仓线和重仓线,以提供更精准的投资建议。 ... [详细]
  • 【Python爬虫实操】 不创作小说,专精网站内容迁移,超高效!(含源代码)
    本文详细介绍了如何利用Python爬虫技术实现高效网站内容迁移,涵盖前端、后端及Android相关知识点。通过具体实例和源代码,展示了如何精准抓取并迁移网站内容,适合对Python爬虫实战感兴趣的开发者参考。 ... [详细]
  • 在前一篇文章中,我们介绍了如何使用Requests库发送GET请求。本文将深入探讨如何通过Requests库发送POST请求,包括参数格式、请求封装等关键技巧,并通过“历史上的今天”API实例进行详细说明。 ... [详细]
  • 利用爬虫技术抓取数据,结合Fiddler与Postman在Chrome中的应用优化提交流程
    本文探讨了如何利用爬虫技术抓取目标网站的数据,并结合Fiddler和Postman工具在Chrome浏览器中的应用,优化数据提交流程。通过详细的抓包分析和模拟提交,有效提升了数据抓取的效率和准确性。此外,文章还介绍了如何使用这些工具进行调试和优化,为开发者提供了实用的操作指南。 ... [详细]
  • 本文深入探讨了Ajax的工作机制及其在现代Web开发中的应用。Ajax作为一种异步通信技术,改变了传统的客户端与服务器直接交互的模式。通过引入Ajax,客户端与服务器之间的通信变得更加高效和灵活。文章详细分析了Ajax的核心原理,包括XMLHttpRequest对象的使用、数据传输格式(如JSON和XML)以及事件处理机制。此外,还介绍了Ajax在提升用户体验、实现动态页面更新等方面的具体应用,并讨论了其在当前Web开发中的重要性和未来发展趋势。 ... [详细]
  • 在今天的实践中,我深入学习了网页图像抓取技术,通过编写爬虫程序批量获取网站上的图片资源。具体来说,我选择了一个包含大量高质量图片的网站作为练习对象,并成功实现了将这些图片批量下载到本地存储。这一过程不仅提升了我对爬虫技术的理解,还增强了我的编程能力。 ... [详细]
  • 本文介绍了如何利用Struts1框架构建一个简易的四则运算计算器。通过采用DispatchAction来处理不同类型的计算请求,并使用动态Form来优化开发流程,确保代码的简洁性和可维护性。同时,系统提供了用户友好的错误提示,以增强用户体验。 ... [详细]
  • Web开发框架概览:Java与JavaScript技术及框架综述
    Web开发涉及服务器端和客户端的协同工作。在服务器端,Java是一种优秀的编程语言,适用于构建各种功能模块,如通过Servlet实现特定服务。客户端则主要依赖HTML进行内容展示,同时借助JavaScript增强交互性和动态效果。此外,现代Web开发还广泛使用各种框架和库,如Spring Boot、React和Vue.js,以提高开发效率和应用性能。 ... [详细]
  • 本指南介绍了 `requests` 库的基本使用方法,详细解释了其七个主要函数。其中,`requests.request()` 是构建请求的基础方法,支持其他高级功能的实现。此外,我们还重点介绍了如何使用 `requests.get()` 方法来获取 HTML 网页内容,这是进行网页数据抓取和解析的重要步骤。通过这些基础方法,读者可以轻松上手并掌握网页数据抓取的核心技巧。 ... [详细]
  • 本文深入解析了HTML框架集(FRAMESET)的使用方法及其应用场景。首先介绍了几个关键概念,如如何通过FRAMESET标签将主视图划分为多个独立的区域,每个区域可以加载不同的HTML文件。此外,还详细探讨了FRAMESET在实际开发中的优缺点,并提供了具体的实例代码,帮助开发者更好地理解和应用这一技术。 ... [详细]
  • 在本文中,我们将为 HelloWorld 项目添加视图组件,以确保控制器返回的视图路径能够正确映射到指定页面。这一步骤将为后续的测试和开发奠定基础。首先,我们将介绍如何配置视图解析器,以便 SpringMVC 能够识别并渲染相应的视图文件。 ... [详细]
  • 本文介绍了使用 Python 编程语言高效抓取微博文本和动态网页图像数据的方法。通过详细的示例代码,展示了如何利用爬虫技术获取微博内容和动态图片,为数据采集和分析提供了实用的技术支持。对于对网络数据抓取感兴趣的读者,本文具有较高的参考价值。 ... [详细]
  • 在本文中,我们将详细介绍如何构建一个用于自动回复消息的XML类。当微信服务器接收到用户消息时,该类将生成相应的自动回复消息。以下是具体的代码实现:```phpclass We_Xml { // 代码内容}```通过这个类,开发者可以轻松地处理各种消息类型,并实现高效的自动回复功能。我们将深入探讨类的各个方法和属性,帮助读者更好地理解和应用这一技术。 ... [详细]
  • 本文详细介绍了一种利用 ESP8266 01S 模块构建 Web 服务器的成功实践方案。通过具体的代码示例和详细的步骤说明,帮助读者快速掌握该模块的使用方法。在疫情期间,作者重新审视并研究了这一未被充分利用的模块,最终成功实现了 Web 服务器的功能。本文不仅提供了完整的代码实现,还涵盖了调试过程中遇到的常见问题及其解决方法,为初学者提供了宝贵的参考。 ... [详细]
  • 本文详细探讨了 jQuery 中 `ajaxSubmit` 方法的使用技巧及其应用场景。首先,介绍了如何正确引入必要的脚本文件,如 `jquery.form.js` 和 `jquery-1.8.0.min.js`。接着,通过具体示例展示了如何利用 `ajaxSubmit` 方法实现表单的异步提交,包括数据的发送、接收和处理。此外,还讨论了该方法在不同场景下的应用,如文件上传、表单验证和动态更新页面内容等,提供了丰富的代码示例和最佳实践建议。 ... [详细]
author-avatar
拍友2602937077
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有