ezlippi / tinyhttpd Goto Github PK
View Code? Open in Web Editor NEWTinyhttpd 是J. David Blackstone在1999年写的一个不到 500 行的超轻量型 Http Server,用来学习非常不错,可以帮助我们真正理解服务器程序的本质。官网:http://tinyhttpd.sourceforge.net
License: GNU General Public License v3.0
Tinyhttpd 是J. David Blackstone在1999年写的一个不到 500 行的超轻量型 Http Server,用来学习非常不错,可以帮助我们真正理解服务器程序的本质。官网:http://tinyhttpd.sourceforge.net
License: GNU General Public License v3.0
在execute_cgi()函数中,你在fork()之后 调用sprintf(buf, "HTTP/1.0 200 OK\r\n");send(client, buf, strlen(buf), 0);这会导致这个信息发了两次,发代码之前都不用一下的吗?
相关代码如下:
` if (pid == 0) /* child: CGI script */
{
char meth_env[255];
char query_env[255];
char length_env[255];
dup2(cgi_output[1], STDOUT);
dup2(cgi_input[0], STDIN);
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);
}
execl(path, NULL);
exit(0);
} else { /* parent */
close(cgi_output[1]);
close(cgi_input[0]);
if (strcasecmp(method, "POST") == 0)
for (i = 0; i < content_length; i++) {
recv(client, &c, 1, 0);
write(cgi_input[1], &c, 1);
}
while (read(cgi_output[0], &c, 1) > 0)
send(client, &c, 1, 0);
close(cgi_output[0]);
close(cgi_input[1]);
waitpid(pid, &status, 0);
}`
这段程序功能大致了解:子进程执行cgi脚本,父进程从client的post请求中读取数据,如果output中有输出,则返回给client。
不清楚的是:input和output管道作用是什么?子进程为何使用dup复制output[1]到标准输出,复制input[0]到标准输入?
无道理的猜测:父进程将post中请求数据传入cgi脚本,子进程执行完成后,若有数据,则写入output[0]中,父进程从中读取。
哪位大佬路过解答一波~
初学,
源码部门如下
char meth_env[255];
char query_env[255];
char length_env[255];
,,,
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);
}
请问这些环境变量有什么作用?
我将httpd放在云服务器上运行,然后在自己的windows电脑上的浏览器里输入"http://ip地址:端口号",但是不能进行访问,为什么不可以呢?
void accept_request(void *arg)
{
int client = (intptr_t)arg;
......
}
intptr_t转换后的client不是传入的值。
系统版本:Ubuntu 16.04 LTS
[Edited]
Dear team,
I submited a bug request. But It's my mistake! Sorry about that.
Regards,
h4niz
安装了perl,并且也在两个cgi问件中更改了perl的路径,make之后运行文件,输入127.0.1.1:端口号,什么都不显示。可能是perl-cgi没安装好,哪位大佬知道perl-cgi怎么安装啊,我看了很多教程没装好。
看了作者的介绍,Fork自sourceForge,
所以是作者自己修改成C语言了?
自己在运行项目时遇到了一些问题,现在已经解决。
问题
成功运行httpd
后,可以通过命令curl -v localhost:4000/index.html
和curl -v localhost:4000/color.cgi
从服务端获得回应,但无法通过windows浏览器访问服务端。
解决
1、设置防火墙开放相应端口firewall-cmd --add-port=4000/tcp
。这里修改了代码,将端口固定为4000而不是让系统随机分配。
2、使用whereis perl
命令获得perl程序的地址,然后vim htdocs/color.cgi
,将第一行的perl地址改为自己查到的路径。
在windows浏览器中输入(自己的虚拟机ip):4000
,可成功访问index.html
界面,输入颜色可获得color.cgi
的运行结果。
我用客户端程序连接服务器,也什么都没有得到,我看之前有个人问这个问题,但是现在的代码应该没有那种问题了吧。
执行make all报错了
gcc -g -W -Wall -lpthread -o httpd httpd.c
httpd.c: In function ‘execute_cgi’:
httpd.c:281:9: warning: null argument where non-null required (argument 2) [-Wnonnull]
execl(path, NULL);
^~~~~
httpd.c:281:9: warning: not enough variable arguments to fit a sentinel [-Wformat=]
/tmp/cck2ie6l.o:在函数‘main’中:
/home/zhujianchen/work/git/Tinyhttpd/httpd.c:503:对‘pthread_create’未定义的引用
collect2: error: ld returned 1 exit status
make: *** [Makefile:4:httpd] 错误 1
您好,请问这是啥问题了
原作者J.David Blackstone
在accept_request函数中,sprintf(path, "htdocs%s", url);明显是有问题的,htdocs后面没有‘/’,连接url会有问题。
`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);//文件的最后一行并没有发送出去
}
}`
在main函数中,传递给线程的参数client_sock是一个local variable,会产生竞争吗?
if (pthread_create(&newthread , NULL, (void *)accept_request, (void *)&client_sock) != 0) perror("pthread_create");
比如连接1对应的线程运行前,连接2把client_sock修改了。
是否应该在堆上分配
stat(path, &st)
我修改了文件权限,目录权限,切换了用户,使用sodu 来执行还是获取不到文件状态
if (bind(httpd, (struct sockaddr *)&name, sizeof(name)) < 0)
error_die("bind");
if (port == 0) / if dynamically allocating a port */
{
socklen_t namelen = sizeof(name);
if (getsockname(httpd, (struct sockaddr *)&name, &namelen) == -1)
error_die("getsockname");
*port = ntohs(name.sin_port);
}
为什么不在bind前判断端口是否为0,为0就调用getsockname让系统自动分配。
现在在bind后,如果bind的端口为0,bind失败、分配了也没用,代码里也没有重新bind。
运行之后没反应呀,应该输入什么?
perl -Tw color.cgi
Can't locate CGI.pm in @inc (you may need to install the CGI module) (@inc contains: /usr/local/Cellar/perl/5.34.0/lib/perl5/site_perl/5.34.0/darwin-thread-multi-2level /usr/local/Cellar/perl/5.34.0/lib/perl5/site_perl/5.34.0 /usr/local/Cellar/perl/5.34.0/lib/perl5/5.34.0/darwin-thread-multi-2level /usr/local/Cellar/perl/5.34.0/lib/perl5/5.34.0 /usr/local/lib/perl5/site_perl/5.34.0) at color.cgi line 4.perimeter81 persepolis-download-manager
BEGIN failed--compilation aborted at color.cgi line 4.
好像找不到cgi相关的东西,导致cgi脚本报错
path不是html文件的路径吗?并没有执行脚本的操作啊,我这里没有看懂,还有能给我举个测试实例吗?
可否改成浏览器访问时,显示当前目录文件列表,供客户端下载。类似于python -m http.server
的效果
numchars = get_line(client, buf, sizeof(buf));
i = 0; j = 0;
while (!ISspace(buf[i]) && (i < sizeof(method) - 1))
{
method[i] = buf[i];
i++;
}
....
if (strcasecmp(method, "GET") && strcasecmp(method, "POST"))
{
unimplemented(client);
return;
}
此时直接return会发生管道破裂,内核会给进程发送signal SIGPIPE, Broken pipe消息,进程终结。所以在return之前要关闭accept函数的fd
main函数中 ”accept_request(&client_sock);“ 将地址传入。
accept_request函数中 ”int client = (intptr_t)arg;“ 将地址赋给client,将导致recv读不到数据。
pthread_create(&newthread , NULL, (void *)accept_request, (void *)&client_sock ), 这里有问题吧, 你的client_sock地址指向的内容在下一次请求接收时,会发生变化.
在mian()中的client_sock以指针形式传入accept_request()中,但在accept_request中(57行)将指针直接赋值给int型的client变量中导致后面recv失败。
如题,这种多线程的网络服务器代码,没有收回线程,为什么不用设置成游离态呢?求大神解答。
首先是网上说的将index.html的访问权限改为不可执行,参考第一个评论https://zhuanlan.zhihu.com/p/105656761
但是还是浏览器运行后一直显示加载,我使用gdb调试后发现,clint_sock本来是4但是在传入accept_request之后,赋给client的值是-8764
此时我注意到了intptr_t并查阅了相关的资料,发现这个类型的大小等于机器的位数,由此保证void转换成立,但是我编写了一个程序发现这个转化后的值等于指针的值而不是client的值,因此我将int client=(intptr_t)arg;改为了int client=(int*)arg;使得浏览器可以访问,但是这个用法不知道是哪里出现了问题。
最后输入颜色没有显示的问题是由于perl路径设置错误,输入which perl获得perl的路径,在check.cgi和color.cgi中的第一行的路径改为自己的就可以了。
希望可以帮到一些和我遇到同样问题的人
//waitpid(pid, &status, 0);我觉得应该放在这里啊
while (read(cgi_output[0], &c, 1) > 0)
send(client, &c, 1, 0);
close(cgi_output[0]);
close(cgi_input[1]);
/*此处存在疑问,不应该将waitpid放在前面吗?以确保子进程先运行*/
waitpid(pid, &status, 0);
waitpid放在最后如何保证子进程结束后可以send给client?
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);
}
}
cat 函数在读取长度小于 1024 的文件时,在第一次 fgets 后 resourse 就已经被设为 eof 了,因此也无法进入循环 send 了。
test.txt
hello world
a.c
#include <stdio.h>
#define MAXLEN 20
int main() {
FILE *fp;
char str[MAXLEN];
fp = fopen("test.txt", "r");
if (fp == NULL) {
perror("ERROR OPEN FILE");
return -1;
}
if (fgets(str, sizeof(str), fp) != NULL) {
puts(str);
}
if (feof(fp)) {
puts("eof");
} else {
puts("no eof");
}
fclose(fp);
return 0;
}
执行结果
我已经把index.html和上一层文件都设为不可执行依然无法访问,在Windows浏览器上一直转圈圈,Carl c 中断后,短时间再次运行会报错段偏移,请问怎么解决?
sprintf(path, "htdocs%s", url);
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);
}
这里discard headers是一定需要的么?
下面的是我在主机Ubuntu Kylin 16.04 LTS上编译信息,我已经按照说明注释了该注释的部分和需要去除注释的部分,但是依然有warning,而且在地址栏输入127.0.0.1:4000时,没有那个index响应。
我是一个初学者,所以还不理解源代码。我根据Google的结果,参照qiyeboy的Tinyhttpd
将execl(path, NULL) 修改为 execl(path, path, NULL),虽然编译httpd.c时没有warming了,但是输入127.0.0.1:4000依然没有响应。还望指点一下。
gcc -g -W -Wall -lpthread -o httpd httpd.c
httpd.c: In function ‘execute_cgi’:
httpd.c:282:9: warning: null argument where non-null required (argument 2) [-Wnonnull]
execl(path, NULL);
^
httpd.c:282:9: warning: not enough variable arguments to fit a sentinel [-Wformat=]
gcc -W -Wall -o client simpleclient.c
simpleclient.c: In function ‘main’:
simpleclient.c:26:9: warning: implicit declaration of function ‘exit’ [-Wimplicit-function-declaration]
exit(1);
^
simpleclient.c:26:9: warning: incompatible implicit declaration of built-in function ‘exit’
simpleclient.c:26:9: note: include ‘<stdlib.h>’ or provide a declaration of ‘exit’
simpleclient.c:32:5: warning: incompatible implicit declaration of built-in function ‘exit’
exit(0);
^
simpleclient.c:32:5: note: include ‘<stdlib.h>’ or provide a declaration of ‘exit’
simpleclient.c:8:14: warning: unused parameter ‘argc’ [-Wunused-parameter]
int main(int argc, char *argv[])
^
simpleclient.c:8:26: warning: unused parameter ‘argv’ [-Wunused-parameter]
int main(int argc, char *argv[])
^
Hi, LippiOuYang!
Why we need '-lsocket' in Makefile?
我本地是MAC ,启动tinyhttpd后,用postman或者浏览器不断发送请求,用activity monitor观察内存不断变大,如果持续下去内存岂不是要爆掉,如何优化呢?
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.