http-server
http协议格式
http-server 代码
#include <arpa/inet.h>
#include <ctype.h>
#include <netinet/in.h>
#include <pthread.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/epoll.h>
#define PORT 9001
#define QUEUE_MAX_COUNT 5
#define BUFF_SIZE 1024
#define SERVER_STRING "Server: hoohackhttpd/0.1.0\r\n"
#define OK 1
#define FAILED -1
#define CR '\r'
#define LF '\n'
#define CRLF "\r\n"
#define BLANK ' '
#define COLON ':'
#define DEBUG 0
#define HTML_PATH "/home/king/share/hik/Makehttpd-master/"
#define dprint(expr) printf(#expr " = %g\n", expr)
#define EPOLL_MAX_NUM 1000
#define RESPONSE_STR "<!DOCTYPE html><html><head><title>Welcome to http server!</title><style>body{width: 35em; \
margin: 0 auto;font-family: Tahoma, Verdana, Arial, sans-serif;}</style></head><body><h1>Welcome to http server!</h1></body></html>"
typedef struct _request_packet {
char *method;
char *url;
char *version;
} req_pack;
void success_header(int sockfd)
{
char buf[BUFF_SIZE];
strcpy(buf, "HTTP/1.1 200 OK\r\n");
send(sockfd, buf, strlen(buf), 0);
strcpy(buf, SERVER_STRING);
send(sockfd, buf, strlen(buf), 0);
sprintf(buf, "Content-Type: text/html\r\n");
send(sockfd, buf, strlen(buf), 0);
strcpy(buf, "\r\n");
send(sockfd, buf, strlen(buf), 0);
}
void process_get(int sockfd, req_pack *rp)
{
success_header(sockfd);
send(sockfd, RESPONSE_STR, strlen(RESPONSE_STR), 0);
}
int parse_start_line(int sockfd, char *recv_buf, req_pack *rp)
{
char *p = recv_buf;
char *ch = p;
int i = 0;
int k = 0;
if (*ch < 'A' || *ch > 'Z') {
return -1;
}
printf("recv buf : %s \n", recv_buf);
while (*ch != CR) {
if (*ch != BLANK) {
k++;
} else {
char* tmp= (char *)malloc(k * sizeof(char));
memset(tmp, 0, sizeof(char));
int offset = 0;
if(i >= 1)
{
offset += strlen(rp->method);
}
if(i >= 2)
{
offset += strlen(rp->url);
}
strncpy(tmp, recv_buf + offset, k);
if(i == 0)
{
rp->method = tmp;
}else if(i == 1)
{
rp->url = tmp;
}else{
rp->version = tmp;
}
k = 0;
i++;
}
ch++;
}
return (i + 2);
}
int parse_request(int sockfd, char *recv_buf, req_pack *rp)
{
int offset;
offset = parse_start_line(sockfd, recv_buf, rp);
if (offset == -1) {
return FAILED;
}
return OK;
}
void handle_request(void *arg)
{
int cli_fd = *(int *)arg;
char buf[BUFF_SIZE];
int req_len = 0;
int recv_len = 0;
char recv_buf[BUFF_SIZE];
int i = 0;
req_pack *rp = NULL;
rp = (req_pack *)malloc(sizeof(req_pack));
memset(rp, 0, sizeof(req_pack));
/* 调用recv函数接收客户端发来的请求信息 */
recv_len = recv(cli_fd, recv_buf, BUFF_SIZE, 0);
if (recv_len <= 0) {
return;
}
/* 解析请求 */
req_len = parse_request(cli_fd, recv_buf, rp);
if (req_len == FAILED) {
return;
}
if (rp->url == NULL || rp->method == NULL) {
perror("empty url or method");
exit(-1);
}
if (strcasecmp(rp->method, "POST") == 0) {
/*process_post(cli_fd);*/
} else {
process_get(cli_fd, rp);
}
free(rp);
rp = NULL;
close(cli_fd);
}
int startup()
{
int server_fd = -1;
u_short port = PORT;
struct sockaddr_in server_addr;
/* 创建一个socket */
server_fd = socket(AF_INET, SOCK_STREAM, 0);
if (server_fd == -1) {
perror("socket");
exit(-1);
}
memset(&server_addr, 0, sizeof(server_addr));
/* 初始化sockaddr_in结构体,设置端口,IP,和TCP/IP协议族等信息 */
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(PORT);
server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
int on = 1;
if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(int)) <
0) {
perror("setsockopt");
exit(-1);
}
/* 把套接字地址结构绑定到套接字 */
if (bind(server_fd, (struct sockaddr *)&server_addr,
sizeof(server_addr)) < 0) {
perror("bind");
exit(-1);
}
/* 启动socket监听请求,开始等待客户端发来的请求 */
if (listen(server_fd, QUEUE_MAX_COUNT) < 0) {
perror("listen");
exit(-1);
}
printf("http server running on port %d\n", port);
return server_fd;
}
int main()
{
/* 定义server和client的文件描述符 */
int server_fd = -1;
struct sockaddr_in client_addr;
char recv_buf[BUFF_SIZE];
int recv_len = 0;
socklen_t client_addr_len = sizeof(client_addr);
int epoll_fd = 0;
struct epoll_event event, *server_events;
server_fd = startup();
// 创建epoll监听事件
epoll_fd = epoll_create(EPOLL_MAX_NUM);
if (epoll_fd < 0) {
perror("epoll create");
exit(-1);
}
event.events = EPOLLIN;
event.data.fd = server_fd;
// 创建epoll控制器
if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, server_fd, &event) < 0) {
perror("epoll ctl");
exit(-1);
}
server_events = malloc(sizeof(struct epoll_event) * EPOLL_MAX_NUM);
int err = 0;
pthread_t t_id;
while (1) {
// 等待事件到来
int active_event_cnt =
epoll_wait(epoll_fd, server_events, EPOLL_MAX_NUM, -1);
int i = 0;
for (i = 0; i < active_event_cnt; i++) {
// 如果是当前监听的事件
if (server_events[i].data.fd == server_fd) {
int client_fd1 = accept(
server_fd, (struct sockaddr *)&client_addr,
&client_addr_len);
if (client_fd1 < 0) {
perror("accept");
exit(-1);
}
char ip[20];
printf("new connection[%s:%d]\n",
inet_ntop(AF_INET, &client_addr.sin_addr,
ip, sizeof(ip)),
ntohs(client_addr.sin_port));
printf("client fd: %d\n", client_fd1);
event.events = EPOLLIN | EPOLLET;
event.data.fd = client_fd1;
epoll_ctl(epoll_fd, EPOLL_CTL_ADD, client_fd1,
&event);
} else if (server_events[i].events & EPOLLIN) {
int *client_fd = (int *)malloc(sizeof(int));
*client_fd = server_events[i].data.fd;
err = pthread_create(&t_id, NULL,
(void *)handle_request,
(void *)client_fd);
if (err != 0) {
perror("thread create");
}
pthread_detach(t_id);
} else if (server_events[i].events & EPOLLOUT) {
printf("EPOLLOUT\n");
}
}
}
close(server_fd);
return 0;
}
makefile如下
server_obj = server.o
CFLAGS = -g
LIBS = -lpthread #-lsocket
CC = gcc
server: $(server_obj)
$(CC) $(server_obj) $(LIBS)-o server
./server
server.o: server.c
$(CC) $(CFLAGS) -c server.c
.PHONY: clean
clean:
rm -rf $(server_obj) $(client_obj) server
http-client
使用浏览器访问上述构建的server,可解析出来对应的打印
注:该demo仅仅是用于模拟http-server是如何工作的,敬请参考