比较 ip 的 输入值是否为 any
是: 设置 sin_addr.s_addr = INADDR_ANY
否:设置 sin_addr.s_addr = inet_addr(ip);
1. 创建 sockaddr_in 结构体
设置关键字参数
sin_family = AF_INET
sin_port = hton(port)
2. 创建套接字
int fd = socket(AF_INET, SOCK_STREAM, 0);
3. 设置端口复用
setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
4. 绑定套接字
bind(fd, (struct sockaddr*)&addr, sizeof(addr)) ;
5. 监听套接字
listen(fd, 10);
6. 接收套接字
accept(listen_socket, (struct sockaddr*)&client_addr, &len);
多线程 HTTP 服务器
传入参数(ip/port)
7. 创建子线程对套接字进行处理
pthread_create(&tid, NULL, handle_client, (void*)&client_fd);
void* handle_client(void* arg);
accept_request(int sock);
8. 关闭套接字
close(listen_socket);
pthread_detach(tid);
创建数组用来保存方法、路径
char buf[MAXSIZE] = {0};
char method[MAXSIZE/32];
char url[MAXSIZE];
char path[MAXSIZE];
逐行读数据
get_line(int sock, char* buf, int size);
MSG_PEEK 窥看外来消息。
获取方法,保存到 method 数组
while(!isspace(buf[i]) && i < strlen(buf) && j < sizeof(method) - 1){
method[j++] = buf[i++];
}
method[j] = '\0';
获取 URL, 保存到 url 数组
j = 0;
while(isspace(buf[i])){
i++;
}
while(!isspace(buf[i]) && i < strlen(buf) && j < sizeof(url) - 1){
url[j++] = buf[i++];
}
url[j] = '\0';
strcasecmp(method, "post") == 0
如果是 post
令 cgi = 1
判断方法是 get 或者是 post
如果是 get
判断是否带"?"
令 cgi = 1
如果带"?"
把"?"替换成"\0"
保存"?"后面的参数
如果不带"?"
令 cgi = 0
将 wwwRoot 拼接到 url 之前,以命令形式输出到 path
sprintf(path,"wwwRoot%s",url);
如果请求的资源是 web 根目录,自动拼接上首页
if(path[strlen(path-1)] == '/'){
strcat(path, HOMEPAGE);
}
stat() 通过文件名 filename 获取文件信息,并保存在结构体 stat 中
请求的资源如果是目录,给每个目录下加一个缺省的首页
if(S_ISDIR(st.st_mode)){
//请求的资源如果是目录,给每个目录下加一个缺省的首页
strcat(path, HOMEPAGE);
}
stat(path, &st)
判断请求的资源是否具有可执行权限
S_IXUSR(S_IEXEC) 00100
文件所有者具可执行权限
S_IXGRP 00010
S_IXOTH 00001
行权限
行权限
用户组具可执
其他用户具可执
if((st.st_mode & S_IXUSR) || (st.st_mode & S_IXGRP) || (st.st_mode & S_IXOTH)){
cgi = 1;
}
如果等于 1
exe_cgi(sock, path, method, cur_url);
比较方法是否为 get
如果为 get
将空行之前的请求报文处理完
clear_head()
如果不为 get(就是 post)
找到正文中的"Content_length: "字段,将正文长度保存起来
sprintf(line, "HTTP/1.0 200 OK\r\n");
发送响应报文,发送之前加上报头
sprintf(line, "\r\n");
创建两个管道,父进程需要知道子进程的执行结果,子进程需要知道参数
send(sock, line, strlen(line), 0);
判断 cgi 是否等于 1
创建父子进程
父进程负责从管道读取子进程的执行结果,并将 post 方法的参数通过管道传送给子进程
子进程将方法和参数导入环境变量,这是因为进行程序替换之后,子进程将丢失参数
子进程将管道重定向到标准输入和标准输出
以只读的方式打开请求资源
如果不等于 1
echo_www(sock, path, st.st_size);
发送响应报文
status line
Content-Length
空行
请求资源
sendfile(sock, fd, NULL, size);
关闭文件