读取文件中数据发送到client:cat函数
//把文件resource中的数据读取到client中
void cat(int client, FILE *resource)
{
char buf[1024];
/从文件结构指针resource中读取数据,保存至buf中
fgets(buf, sizeof(buf), resource);
//处理文件流中剩下的字符
//检测流上的文件结束符,文件结束返回非0值,结束返回0
while (!feof(resource))
{
send(client, buf, strlen(buf), 0);
fgets(buf, sizeof(buf), resource);
}
}
将信息传送个client:headers, serve_file
/服务器向client发送响应头部信息
void headers(int client, const char *filename)
{
char buf[1024];
(void)filename; /* could use filename to determine file type */
strcpy(buf, "HTTP/1.0 200 OK\r\n");
send(client, buf, strlen(buf), 0);
strcpy(buf, SERVER_STRING);
send(client, buf, strlen(buf), 0);
sprintf(buf, "Content-Type: text/html\r\n");
send(client, buf, strlen(buf), 0);
strcpy(buf, "\r\n");
send(client, buf, strlen(buf), 0);
}
//调用 cat 把服务器文件内容返回给浏览器
void serve_file(int client, const char *filename)
{
FILE *resource = NULL;
int numchars = 1;
char buf[1024];
//读取,丢弃头部
buf[0] = 'A'; buf[1] = '\0';
while ((numchars > 0) && strcmp("\n", buf)) /* read & discard headers */
numchars = get_line(client, buf, sizeof(buf));
resource = fopen(filename, "r");
if (resource == NULL)
not_found(client);//文件不存在,返回404错误
else
{
headers(client, filename);//服务器向client发送响应头部信息,200
cat(client, resource);//将文件中的信息发送到client
}
fclose(resource);
}
tinyhttpd服务器端的核心函数

main函数
int main(void)
{
int server_sock = -1;
u_short port = 0; //端口号
int client_sock = -1;
struct sockaddr_in client_name;
socklen_t client_name_len = sizeof(client_name);
pthread_t newthread;
server_sock = startup(&port);//服务器端套接字设置
printf("httpd running on port %d\n", port);
/*多线程并发服务器模型*/
while (1)
{
//主线程 ,阻塞等待客户端连接请求
client_sock = accept(server_sock,
(struct sockaddr *)&client_name,
&client_name_len);
if (client_sock == -1)
error_die("accept");
//创建工作线程,执行回调函数accept_request,参数client_sock
if (pthread_create(&newthread , NULL, accept_request,(void *)&client_sock) != 0)
perror("pthread_create");
}
//关闭套接字,就协议栈而言,即关闭TCP连接
close(server_sock);
return(0);
}
服务器端套接字初始化设置:start_up
/*服务器端套接字初始化设置*/
int startup(u_short *port)
{
int httpd = 0;
struct sockaddr_in name;
httpd = socket(PF_INET, SOCK_STREAM, 0);//创建服务器端套接字
if (httpd == -1)
error_die("socket");
memset(&name, 0, sizeof(name));
name.sin_family = AF_INET;//地址簇
name.sin_port = htons(*port);//指定端口
name.sin_addr.s_addr = htonl(INADDR_ANY);//通配地址
if (bind(httpd, (struct sockaddr *)&name, sizeof(name)) < 0)//绑定到指定地址和端口
error_die("bind");
if (*port == 0) /* if dynamically allocating a port *///动态分配一个端口
{
int namelen = sizeof(name);
/*在以端口号0调用bind后,getsockname用于返回由内核赋予的本地端口号*/
if (getsockname(httpd, (struct sockaddr *)&name, &namelen) == -1)
error_die("getsockname");
*port = ntohs(name.sin_port);//网络字节顺序转换为主机字节顺序,返回主机字节顺序表达的数
}
if (listen(httpd, 5) < 0)//服务器客户端请求。套接字排队的最大连接个数5
error_die("listen");
return(httpd);
}
接收客户端的请求报文:accept_request
该函数结合前面的照片看,更容易理解
/* HTTP协议规定,请求从客户端发出,最后服务器端响应该请求并返回。
* 这是目前HTTP协议的规定,服务器不支持主动响应,所以目前的HTTP
* 协议版本都是基于客户端请求,然后响应的这种模型。 */
/*accept_request函数解析客户端请求,判断是请求静态文件还是cgi代码
(通过请求类型以及参数来判定),如果是静态文件则将文件输出给前端,
如果是cgi则进入cgi处理函数*/
void* accept_request(void* pclient)
{
char buf[1024];
int numchars;
char method[255];
char url[255];
char path[512];
size_t i, j;
struct stat st;
int cgi = 0; /* becomes true if server decides this is a CGI
* program */
char *query_string = NULL;
int client = *(int*)pclient;
//获取一行HTTP请求报文
numchars = get_line(client, buf, sizeof(buf));
i = 0; j = 0;
//提取其中的方法post或get到method
while (!ISspace(buf[j]) && (i < sizeof(method) - 1))
{
method[i] = buf[j];
i++; j++;
}
method[i] = '\0';
//tinyhttpd只实现了get post 方法
if (strcasecmp(method, "GET") 0&& strcasecmp(method, "POST"))
{
unimplemented(client);
return NULL;
}
//cgi为标志位,1表示开启CGI解析(POST方法)
if (strcasecmp(method, "POST") == 0)
cgi = 1;
i = 0;
//跳过method后面的空白字符
while (ISspace(buf[j]) && (j < sizeof(buf)))
j++;
//获取url
while (!ISspace(buf[j]) && (i < sizeof(url) - 1) && (j < sizeof(buf)))
{
url[i] = buf[j];
i++; j++;
}
url[i] = '\0';
//如果是get方法,url可能带?参数
if (strcasecmp(method, "GET") == 0)
{
query_string = url;
while ((*query_string != '?') && (*query_string != '\0'))
query_string++;
if (*query_string == '?')
{
//带参数需要执行cgi,解析参数
cgi = 1;
*query_string = '\0';
query_string++;
}
}
//以上 将起始行 解析完毕
sprintf(path, "htdocs%s", url);
//如果path是一个目录,默认设置首页为index.html
if (path[strlen(path) - 1] == '/')
strcat(path, "index.html");
//函数定义: int stat(const char *file_name, struct stat *buf);
//函数说明: 通过文件名filename获取文件信息,并保存在buf所指的结构体stat中
//返回值: 执行成功则返回0,失败返回-1,错误代码存于errno(需要include <errno.h>)
if (stat(path, &st) == -1) {
//访问的网页不存在,则不断的读取剩余的请求头部信息,并丢弃
while ((numchars > 0) && strcmp("\n", buf)) /* read & discard headers */
numchars = get_line(client, buf, sizeof(buf));
not_found(client);
}
else
{
//访问你的网页存在则进行处理
//S_IFDIR 判断是否为目录
if ((st.st_mode & S_IFMT) == S_IFDIR)
strcat(path, "/index.html");
、
//S_IXUSR:文件所有者具有可执行权限,
//S_IXGRP:用户组具有可执行权限
if ((st.st_mode & S_IXUSR) ||
(st.st_mode & S_IXGRP) ||
(st.st_mode & S_IXOTH) )
cgi = 1;
if (!cgi)
//将静态文件返回
serve_file(client, path);
else
execute_cgi(client, path, method, query_string);
}
//THHP协议是面向无连接的,所以要关闭
close(client);
return NULL;
}
本文来自电脑杂谈,转载请注明本文网址:
http://www.pc-fly.com/a/shumachanpin/article-121791-2.html
第四就把海警舰队扩