为了更好的了解HTTP协议, 特意谢了一个简单HTTP服务器, 代码只有400行. 因为很简单, 所以效率也不怎么高, 而且支持的特性也不多, 不过也可以运行, 性能跟Apache差不多.

=============================================================================================

#include <fcntl.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netinet/tcp.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define BUFFER_INIT_SIZE 512

enum {
   
mickey_reading_header_stage,
   
mickey_writing_header_stage,
   
mickey_writing_body_stage,
   
mickey_closing_stage
};

enum {
   
mickey_http_ok,
   
mickey_http_notfound,
   
mickey_http_error
};

enum {
   
mickey_method_get,
   
mickey_method_head,
   
mickey_method_unknow
};

typedef struct {
    char
*buff;
    int
size;
    int
free;
} mickey_buffer_t;

typedef struct {
    int
sock;
   
mickey_buffer_t *request;
   
mickey_buffer_t *response;
    int
keepalive;
    int
method;
   
mickey_buffer_t *uri;
    int
status;
    int
stage;
    FILE
*handle;
} mickey_connection_t;

static int srv_sock;

mickey_buffer_t *mickey_buffer_new() {
   
mickey_buffer_t *object;
   
    object =
malloc(sizeof(*object));
    if (object)
{
       
object->buff = malloc(BUFFER_INIT_SIZE + 1);
       
if (!object->buff) {
           
free(object);
           
return NULL;
       
}
       
object->size = BUFFER_INIT_SIZE;
       
object->free = BUFFER_INIT_SIZE;
    }
    return
object;
}

int mickey_buffer_append_length(mickey_buffer_t *buf, void
*data, int length) {
    int lack,
need = 0;
    char
*temp;
   
    if (length
> buf->free) {
       
lack = length - buf->free;
       
while (need < lack)
           
need += BUFFER_INIT_SIZE;
       
temp = realloc(buf->buff, buf->size +
need + 1);
       
if (!temp)
           
return -1;
       
buf->buff = temp;
       
buf->size += need;
       
buf->free += need;
    }
   
memcpy(buf->buff + (buf->size -
buf->free), data, length);
   
buf->free -= length;
   
buf->buff[buf->size -
buf->free] = '\0';
    return
0;
}

int mickey_buffer_append(mickey_buffer_t *buf, void *data)
{
    return
mickey_buffer_append_length(buf, data, strlen((char *)data));
}

int mickey_buffer_find_string(mickey_buffer_t *buf, char *str)
{
    int idx =
buf->size - buf->free;
    int slen =
strlen(str);
    int i;
    for (i = 0;
i < idx; i++) {
       
if (idx - i >= slen) {
           
if (!memcmp(buf->buff + i, str, slen)) return
1;
       
} else {
           
break;
       
}
    }
    return
0;
}

int mickey_buffer_length(mickey_buffer_t *buf) {
    return
buf->size - buf->free;
}

void mickey_buffer_print(mickey_buffer_t *buf) {
   
fprintf(stderr, "%s", buf->buff);
}

void mickey_buffer_clean(mickey_buffer_t *buf) {
   
buf->free = buf->size;
}

void mickey_buffer_free(mickey_buffer_t *buf) {
    if
(!buf)
       
return;
    if
(buf->buff)
       
free(buf->buff);
   
free(buf);
}

int mickey_header_finish(mickey_connection_t *conn) {
    int end =
conn->request->size -
conn->request->free;
    if
(conn->request->buff[end - 1] == '\n'
&&
       
conn->request->buff[end - 2] == '\r'
&&
       
conn->request->buff[end - 3] == '\n'
&&
       
conn->request->buff[end - 4] ==
'\r')
       
return 1;
    return
0;
}

void mickey_parse_header(mickey_connection_t *conn) {
    char
*eol;
    char
method[16], uri[256], protocol[32];
   
    eol =
strchr(conn->request->buff,
'\n');
    if (eol ==
NULL) {
       
conn->stage = mickey_closing_stage;
       
return;
    }
   
    if (*(eol-1)
== '\r')
       
*(eol-1) = '\0';
    *eol =
'\0';
   
sscanf(conn->request->buff, "s %6s
2s", method, uri, protocol);
    if
(!strcmp(method, "GET")) {
       
conn->method = mickey_method_get;
    } else if
(!strcmp(method, "HEAD")) {
       
conn->method = mickey_method_head;
    } else
{
       
conn->method = mickey_method_unknow;
    }
   
mickey_buffer_append(conn->uri, ".");
   
mickey_buffer_append(conn->uri, uri);
    if
(mickey_buffer_find_string(conn->uri, "..")) {
       
fprintf(stderr, "[x] mickey found connection header exists
(..)\n");
       
conn->stage = mickey_closing_stage;
    }
}

void connection_reading_header(mickey_connection_t *conn)
{
    char
buff[1024];
    int
nrecv;
   
    nrecv =
read(conn->sock, buff, 1024);
    if (nrecv
> 0) {
       
mickey_buffer_append_length(conn->request, buff,
nrecv);
       
if (mickey_header_finish(conn)) {
           
mickey_parse_header(conn);
           
conn->stage = mickey_writing_header_stage;
       
}
    } else
{
       
fprintf(stderr, "[x] mickey cannot read data from
connection.\n");
       
conn->stage = mickey_closing_stage;
    }
}

void connection_make_header(mickey_connection_t *conn) {
    struct stat
stat_buf;
   
    if
(stat(conn->uri->buff,
&stat_buf) == -1) {
       
conn->status = mickey_http_notfound;
       
mickey_buffer_append(conn->response, "HTTP/1.0 404
Not Found\r\n");
    } else
{
       
if (S_ISDIR(stat_buf.st_mode)) {
           
mickey_buffer_append(conn->uri, "index.html");
           
if (stat(conn->uri->buff,
&stat_buf) == -1) {
               
conn->status = mickey_http_notfound;
               
mickey_buffer_append(conn->response, "HTTP/1.0 404
Not Found\r\n");
           
} else {
               
char content_length[256];

conn->handle =
fopen(conn->uri->buff, "r");
               
if (!conn->handle) {
                   
mickey_buffer_append(conn->response, "HTTP/1.0 500
Internal Server Error\r\n");
               
} else {
                   
mickey_buffer_append(conn->response, "HTTP/1.0 200
OK\r\n");
                   
sprintf(content_length, "Content-Length: %d\r\n",
stat_buf.st_size);
                   
mickey_buffer_append(conn->response,
content_length);
               
}
           
}
       
} else if (S_ISREG(stat_buf.st_mode)) {
           
char content_length[256];

conn->handle =
fopen(conn->uri->buff, "r");
           
if (!conn->handle) {
               
mickey_buffer_append(conn->response, "HTTP/1.0 500
Internal Server Error\r\n");
           
} else {
               
mickey_buffer_append(conn->response, "HTTP/1.0 200
OK\r\n");
               
sprintf(content_length, "Content-Length: %d\r\n",
stat_buf.st_size);
               
mickey_buffer_append(conn->response,
content_length);
           
}
       
} else {
           
mickey_buffer_append(conn->response, "HTTP/1.0 500
Internal Server Error\r\n");
       
}
    }
   
mickey_buffer_append(conn->response, "\r\n");
   
//mickey_buffer_print(conn->response);
}

void connection_send_header(mickey_connection_t *conn) {
    int bytes =
mickey_buffer_length(conn->response);
    int nsend, i
= 0;
   
    while (i
< bytes) {
       
nsend = write(conn->sock,
conn->response->buff + i, bytes -
i);
       
if (nsend > 0) {
           
i += nsend;
       
} else {
           
sleep(1);
           
continue;
       
}
    }
   
conn->stage = mickey_writing_body_stage;
}

void connection_send_body(mickey_connection_t *conn) {
    char
buff[1024];
    int
bytes;
   
    if
(!conn->handle) {
       
conn->stage = mickey_closing_stage;
       
return;
    }
   
    while
(!feof(conn->handle)) {
       
int ws = 0;
       
int hs = 0;

fread(buff, 1024, 1, conn->handle);
       
bytes = strlen(buff);
       
while (ws < bytes) {
           
hs = write(conn->sock, buff + ws, bytes - ws);
           
if (hs > 0) {
               
ws += hs;
           
} else {
               
sleep(1);
               
continue;
           
}
       
}
    }
   
fclose(conn->handle);
   
conn->stage = mickey_closing_stage;
}

mickey_connection_t *connection_new(int sock) {
   
mickey_connection_t *conn;
   
    conn =
malloc(sizeof(*conn));
    if (conn)
{
       
conn->sock     
= sock;
       
conn->keepalive = 0;
       
conn->stage    
= mickey_reading_header_stage;
       
conn->status   
= mickey_http_ok;
       
conn->request   =
mickey_buffer_new();
       
conn->response  =
mickey_buffer_new();
       
conn->uri      
= mickey_buffer_new();
       
if (!conn->request || !conn->response
|| !conn->uri) {
           
mickey_buffer_free(conn->request);
           
mickey_buffer_free(conn->response);
           
mickey_buffer_free(conn->uri);
           
free(conn);
           
return NULL;
       
}
       
return conn;
    }
   
    return
NULL;
}

void connection_exit(mickey_connection_t *conn) {
    if
(conn->request)
       
mickey_buffer_free(conn->request);
    if
(conn->response)
       
mickey_buffer_free(conn->response);
    if
(conn->uri)
       
mickey_buffer_free(conn->uri);
   
free(conn);
}

void *connection_thread_entrance(void *arg) {
   
mickey_connection_t *conn = (mickey_connection_t *)arg;
   
   
pthread_detach(pthread_self());
   
   
fprintf(stderr, "[+] === mickey thread working ===\n");
   
    while (1)
{
       
switch (conn->stage) {
       
case mickey_reading_header_stage:
           
connection_reading_header(conn);
           
break;
       
case mickey_writing_header_stage:
           
connection_make_header(conn);
           
connection_send_header(conn);
           
break;
       
case mickey_writing_body_stage:
           
connection_send_body(conn);
           
break;
       
case mickey_closing_stage:
           
close(conn->sock);
           
connection_exit(conn);
           
goto THREAD_CLOSING;
           
break;
       
}
    }

THREAD_CLOSING:
   
fprintf(stderr, "[-] === mickey thread closing ===\n");
   
pthread_exit(NULL);
}

int initialize_server() {
    struct
sockaddr_in addr;
    struct
linger ling = {0, 0};
    int flags =
1;
   
    srv_sock =
socket(AF_INET, SOCK_STREAM, 0);
    if (srv_sock
== -1)
       
return -1;
   
setsockopt(srv_sock, SOL_SOCKET, SO_REUSEADDR,
&flags, sizeof(flags));
   
setsockopt(srv_sock, SOL_SOCKET, SO_KEEPALIVE,
&flags, sizeof(flags));
   
setsockopt(srv_sock, SOL_SOCKET, SO_LINGER, &ling,
sizeof(ling));
#if !defined(TCP_NOPUSH)
   
setsockopt(srv_sock, IPPROTO_TCP, TCP_NODELAY,
&flags, sizeof(flags));
#endif
   
   
addr.sin_family = AF_INET;
   
addr.sin_port = htons(8080);
   
addr.sin_addr.s_addr = htonl(INADDR_ANY);
   
    if
(bind(srv_sock, (struct sockaddr *) &addr,
sizeof(addr)) == -1)
       
return -1;
    if
(listen(srv_sock, 1024) == -1)
       
return -1;
    return
srv_sock;
}

void socket_accept_connection() {
    int
new_sock;
    socklen_t
len;
    struct
sockaddr addr;
   
mickey_connection_t *new_conn;
    pthread_t
tid;
   
    while (1)
{
       
new_sock = accept(srv_sock, &addr,
&len);
       
if (new_sock == -1) {
           
if (errno != EAGAIN && errno !=
EWOULDBLOCK) {
               
continue;
           
}
           
goto ERROR_FLAG;
       
}
       
new_conn = connection_new(new_sock);
       
if (!new_conn) {
           
fprintf(stderr, "[x] mickey not enough momery.\n");
           
close(new_sock);
           
goto ERROR_FLAG;
       
}
       
pthread_create(&tid, NULL,
connection_thread_entrance, new_conn);
    }
   
ERROR_FLAG:
   
fprintf(stderr, "[x] mickey cannot accept client
connection.\n");
   
return;
}

int main(int argc, char **argv) {
   
chdir("./");
    if
(initialize_server() == -1) {
       
fprintf(stderr, "[x] mickey initialize failed.\n");
       
exit(1);
    }
   
socket_accept_connection();
   
exit(0);
}

分享:

 

0

喜欢

0

赠金笔

一个简单的HTTP服务器(多线程)的更多相关文章

  1. 自己动手模拟开发一个简单的Web服务器

    开篇:每当我们将开发好的ASP.NET网站部署到IIS服务器中,在浏览器正常浏览页面时,可曾想过Web服务器是怎么工作的,其原理是什么?“纸上得来终觉浅,绝知此事要躬行”,于是我们自己模拟一个简单的W ...

  2. 响应式编程笔记三:一个简单的HTTP服务器

    # 响应式编程笔记三:一个简单的HTTP服务器 本文我们将继续前面的学习,但将更多的注意力放在用例和编写实际能用的代码上面,而非基本的APIs学习. 我们会看到Reactive是一个有用的抽象 - 对 ...

  3. 使用纯php构建一个简单的PHP服务器

    使用原生PHP构建一个简单的PHPWeb服务器 1.目录机构 webserver --src -- Response.php -- Server.php -- Request.php -- vendo ...

  4. 一个简单的web服务器

    写在前面 新的一年了,新的开始,打算重新看一遍asp.net本质论这本书,再重新认识一下,查漏补缺,认认真真的过一遍. 一个简单的web服务器 首先需要引入命名空间: System.Net,关于网络编 ...

  5. [置顶] 在Ubuntu下实现一个简单的Web服务器

    要求: 实现一个简单的Web服务器,当服务器启动时要读取配置文件的路径.如果浏览器请求的文件是可执行的则称为CGI程序,服务器并不是将这个文件发给浏览器,而是在服务器端执行这个程序,将它的标准输出发给 ...

  6. Tomcat剖析(二):一个简单的Servlet服务器

    Tomcat剖析(二):一个简单的Servlet服务器 1. Tomcat剖析(一):一个简单的Web服务器 2. Tomcat剖析(二):一个简单的Servlet服务器 3. Tomcat剖析(三) ...

  7. Tomcat剖析(一):一个简单的Web服务器

    Tomcat剖析(一):一个简单的Web服务器 1. Tomcat剖析(一):一个简单的Web服务器 2. Tomcat剖析(二):一个简单的Servlet服务器 3. Tomcat剖析(三):连接器 ...

  8. 自己模拟的一个简单的web服务器

    首先我为大家推荐一本书:How Tomcat Works.这本书讲的很详细的,虽然实际开发中我们并不会自己去写一个tomcat,但是对于了解Tomcat是如何工作的还是很有必要的. Servlet容器 ...

  9. java实现一个简单的Web服务器

    注:本段内容来源于<JAVA 实现 简单的 HTTP服务器> 1. HTTP所有状态码 状态码 状态码英文名称 中文描述 100 Continue 继续.客户端应继续其请求 101 Swi ...

随机推荐

  1. Android 界面滑动实现---Scroller类 从源码和开发文档中学习(让你的布局动起来)

    在android学习中,动作交互是软件中重要的一部分,其中的Scroller就是提供了拖动效果的类,在网上,比如说一些Launcher实现滑屏都可以通过这个类去实现..   例子相关博文:Androi ...

  2. MSSQL- select @@identity的用法

    转载自:http://blog.163.com/zhangqian_sms/blog/static/544483382008925112539620/ 用select @@identity得到上一次插 ...

  3. 内省(二)之BeanUtils工具类

    上一篇内省(Introspector)讲到的是采用JavaAPI中的类来操作bean及其属性,而Apache也开源了第三方框架来简化和丰富了对bean属性的操作,这个框架就是BeanUtils. 使用 ...

  4. nyoj 55 懒省事的小明 优先队列 multiset 还有暴力

    懒省事的小明 时间限制: 3000 ms  |  内存限制: 65535 KB 难度: 3   描述       小明很想吃果子,正好果园果子熟了.在果园里,小明已经将所有的果子打了下来,而且按果子的 ...

  5. SilkTest Q&A 11

    101. 如何从其他的机器访问脚本? 答案:将包含脚本的文件夹共享出来…非常简单…你可以使用connect()在你本机运行脚本从而使得它们在其他的一些机器上执行…但是其他人无法访问这些脚本,除非你将它 ...

  6. Python+Django+SAE系列教程15-----输出非HTML内容(图片/PDF)

    一个Django视图函数 必须 接受一个HttpRequest 实例作为它的第一个參数 返回一个HttpResponse 实例 从一个视图返回一个非HTML 内容的关键是在构造一个 HttpRespo ...

  7. win2003的IIS無法使用,又一次安裝提示找不到iisadmin.mfl文件

    我的系統是win2003 繁體版 sp2,現在iis無法使用,我同事的也是,也不知道是不是跟在網域中有關係,因為我用虛擬機的繁體系統win2003 R2版iis能够正常使用,不過曾经那台電腦也是在網域 ...

  8. 很好用的一个类:TJvAppXMLFileStorage

    以前写软件的时候,在处理软件配置信息的时候,总是要写很多重复的代码,把配置信息写到Ini文件当中.昨天到网上查了一下,发现JVCL中提供了一个非常好用的类TJvAppXMLFileStorage,可以 ...

  9. Qt之生成Window资源文件(.rc 文件)

    简述 qmake 可以随意地自动生成一个适当填充的 Windows 资源文件.本节主要讲解如何用 qmake 处理一个 Windows 资源文件,并将其链接到一个可执行应用程序(EXE)或动态链接库( ...

  10. C++ Map 容器

    1.Map是c++的一个标准容器,它提供了很好一对一的关系. Map是一种关联是容器,在map中增加和删除元素非常容易.可以修改一个特定的节点而不对其他节点不产生影响,由于map是一种关联式容器,Ke ...