#include <stdio.h>
#include <unistd.h>
#include <error.h>
#include <stdlib.h>
#include <pthread.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <string.h>
#define MAX_LINE_NUMS 1024
#define BUFF_SIZE 1024
#define SERVER_STRING "Server: jdbhttpd/0.1.0\r\n"
typedef struct sockaddr_in sock_addr_in_t;
typedef struct sockaddr sock_addr_t;
// 初始化套接字
int init_socket(int *port)
{
int fd = -1;
if ((fd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
{
perror("socket:");
return -1;
}
sock_addr_in_t srv_addr;
bzero(&srv_addr, sizeof(srv_addr));
srv_addr.sin_family = AF_INET;
srv_addr.sin_addr.s_addr = htons(INADDR_ANY);
srv_addr.sin_port = htons(*port);
if (bind(fd, (sock_addr_t*)&srv_addr, sizeof(srv_addr) ) == -1)
{
perror("bind:");
return -1;
}
if (listen(fd, 10) == -1)
{
perror("listen:");
return -1;
}
if (*port == 0)
{
socklen_t len = sizeof (srv_addr);
bzero(&srv_addr, sizeof(srv_addr));
if (getsockname(fd, (sock_addr_t*)&srv_addr, &len) == -1)
perror("getsockname:");
printf("%d\n",srv_addr.sin_port);
*port = ntohs(srv_addr.sin_port);
}
return fd;
}
// 一次读取一个字符,直到碰到 \n
int get_line(int fd, char *buff, int max_len)
{
char c;
int i = 0;
if (fd > 0)
{
while (read(fd, &c, 1) > 0)
{
buff[i ++] = c;
if (c == '\n')
{
return i;
}
if (i == max_len - 2)
{
buff[max_len - 1] = 0;
return max_len - 1;
}
}
}
return i;
}
// 从请求行中解析出:http方法,路径,http协议版本
void parse_request_line(char *buff, char *method, char *path, char *protocol)
{
int ib = 0, i = 0;
int len = strlen(buff);
while (ib < len)
{
if (isspace(buff[ib]))
{
ib ++;
break;
}
method[i ++] = buff[ib ++];
}
i = 0;
while (ib < len)
{
if (isspace(buff[ib]))
{
ib ++;
break;
}
path[i ++] = buff[ib ++];
}
i = 0;
while (ib < len)
protocol[i ++] = buff[ib ++];
}
void send_line_header(int fd)
{
char buff[BUFF_SIZE] = {0};
// 响应行
strcpy(buff, "HTTP/1.0 200 OK\r\n");
write(fd, buff, strlen(buff));
// 响应头
strcpy(buff, SERVER_STRING);
write(fd, buff, strlen(buff));
strcpy(buff, "Content-Type: text/html\r\n");
write(fd, buff, strlen(buff));
strcpy(buff, "\r\n");
write(fd, buff, strlen(buff));
}
void send_html(int fd, FILE *fstream)
{
char buff[BUFF_SIZE] = {0};
while (!feof(fstream))
{
int len = fread(buff, 1, BUFF_SIZE, fstream);
if (write(fd, buff, len) == -1)
perror("write: ");
}
}
void *accept_client(void *arg)
{
int client_fd = (int)arg;
char buff[MAX_LINE_NUMS] = {0};
int len = get_line(client_fd, buff, MAX_LINE_NUMS);
buff[len - 1] = buff[len - 2] = 0;
char method[16] = {0};
char url[64] = {0};
char path[64] = {0};
char protocol[16] = {0};
parse_request_line(buff, method, url, protocol);
// 因为TCP是可靠协议,如果不接受数据会引起重传,所以丢弃请求头
memset(buff, 0, MAX_LINE_NUMS);
int head_nums = get_line(client_fd, buff, MAX_LINE_NUMS);
while (head_nums > 0 && strcmp("\r\n", buff) != 0)
{
memset(buff, 0, MAX_LINE_NUMS);
head_nums = get_line(client_fd, buff, MAX_LINE_NUMS);
}
send_line_header(client_fd);
sprintf(path, "html%s", url);
if (path[strlen(path) - 1] == '/')
{
strcat(path, "index.html");
}
FILE *fstream = fopen(path, "r");
printf("%s\n", path);
perror("fopen:");
send_html(client_fd, fstream);
fclose(fstream);
close(client_fd);
return (void*)0;
}
int main(int argc, char **argv)
{
int port = 80;
int srv_fd;
if (argc == 2)
{
port = atoi(argv[1]);
}
if ((srv_fd = init_socket(&port)) == -1)
{
fprintf(stderr, "init_socket error\n");
exit(0);
}
printf("http is listening on port %d\n", port);
sock_addr_in_t client_addr;
int client_fd;
socklen_t len;
pthread_t thread_id;
while (1)
{
bzero(&client_addr, sizeof(client_addr));
if ((client_fd = accept(srv_fd, (sock_addr_t*)&client_addr, &len)) == -1)
perror("accept:");
else
{
pthread_create(&thread_id, NULL, accept_client, (void*)client_fd);
pthread_detach(thread_id);
}
}
close(srv_fd);
exit(0);
}