简单web服务器

  昨天看了一下boa的源码,简单的浏览了一遍,boa是非常优秀的嵌入式web服务器,虽然只支持单任务,但它的高效是其他web服务器不能比拟的,正好这段时间在学习socket编程,那么动手写一个简单的web服务器,应该是可行的,虽然自己写的肯定不会太好,但如果写一遍,那么对与web服务器的运行机制与底层原理会更加的清晰,那么开始写一个简单的web服务器吧。
   首先需要分析下http协议,可以在百度百科中查阅详细的资料,这里给出链接: HTTP协议 ,
因为只实现简单的web服务,所以功能会简化很多,只对报文中常见的信息头进行解析,看看请求报文格式:
       
GET /index.html HTTP/1.1
Accept:image/gif.image/jpeg,*/*
Accept-Language:zh-cn
Connection:Keep-Alive
Host:localhost
Accept-Encoding:gzip,deflate

GET /index.html HTTP/1.1         包括请求的文件名, http协议版本
Accept:image/gif.image/jpeg,*/*  表示支持的文件类型
除了第一行是必须信息外,其他的都是非必选项目,可以具体的添加一些必须的项目
在同web服务器建立连接后,将可以把以上的报文以字符串的形式写入到socket描述符中,可以理解为文件的写入,而底层的东西已经有系统和tcp/ip协议栈完成了。对与底层的具体实现,显然不是这里的重点,具体可以参照其他书目。
 
这样,我们的web服务器受到了请求报文,就可以根据第一行的信息去读取文件信息,在把加入回复报文头部信息,一起发出去。看看应答报文格式:

HTTP/1.1 200 OK
Server:Apache Tomcat/5.0.12
Content-Type: text/html
Content-Length:28

<body>
Hello HTTP!
</body>
HTTP/1.1 200 OK : 该行信息中,需要注意的是数字200, 这是web服务器应答状态码,200表示正常,如果文件不存在,则返回404,具体应答状态码具体参照http协议说明。这里特别说明的是,对于不同的执行结构要返回不同的应答状态码。
   在上边的应答报文格式中,可以清楚的看到,在信息头部和文件头部之间是有一个空行相隔的,这是需要特别注意的,浏览器正式同个这个换行符来区分那是头信息、那么文件信息的。 
用一张图片来展示下http报文请求与应答的过程: 
  
 
 
 我们知道,在boa移植过程中,需要在/var/下,创建www目录,而这个目录就是boa默认的网页文件存放的根目录,这里我们是实现web服务器时,通过设定环境变量来制定网页文件存放的根目录,设定环境变量为:HTTP_DIR,在代码中,通过函数char *getenv("HTTP_DIR")来获取环境变量的内容。
  通过上边的叙述,简单的web服务器雏形已经完成,报文的请求与应答都可以完成,那么怎么来完成并发,也就是说可以同时相应多个客户端,我们可以通过多进程与多线程两种方法来实现。下边一一说明。
   采用多进程来完成web服务器的并发时,需要注意一些小的问题,我们知道,在进行fork()时,子进程会复制父进程运行空间内的所有内容,如果在父进程中已经完成了socket的创建、端口的绑定、listen()、accept(),那么在子进程中也会出现和父进程一样的网络socket描述符。先看代码吧。
 

if((pid = fork()) == 0)

{
    // Child close listening socket
    close(Socket_fd);
    ClientHandler(&acptSock);
    // Process done with this client, close it
    close(socket_fd);
    // Child exit
    exit(0);    
}

else

{    // Parent close the accepted connection
    close(connfd);
}

  显然可以看出,在子进程和父进程中都存在socket_fd描述符,如果在子进程中不关闭socket_fd,那么就在同时存在两个socket_fd,这样会存在隐患,设想下,父进程要退出,如果只有父进程拥有socket_fd,那么父进程close(socket_fd)后,子进程的网络连接也就断开了,但这里的实际情况是父进程和子进程都拥有socket_fd,父进程关闭socket_fd后,子进程仍然能够正常的通信,那么这个子进程就不受父进程控制了,成了不听老子话的小子了。所以一定要关闭子进程中的socket_fd。同样,对与文件描述符connfd,这是一个当前连接的描述符,正常情况下,子进程退出既关闭conn_fd,可如果在父子进程同时拥有conn_fd的情况下,子进程退出后,虽然关闭了conn_fd,但父进程中仍然存在conn_fd,实际子进程的连接并没有关闭。上述的情况在使用进程实现并发中需要注意,这些错误不是太容易发现。
  来看看通过线程实现的web服务并发:


conn_fd = accept(lstnSocket,(struct sockaddr *)&pin,&address_size);
if(conn_fd > 0)
{
          pthread_create(&thread_id, NULL,
          (void *)Client_Process,
          (void *)conn_fd);
}

void *Client_Process(void *arg)// 客户端服务程序

{
    int conn_fd = (int)arg;
    ClientHandler(&acptSock);
    close(conn_fd);
    return;
}

 通过线程来实现web服务器的并发思路是非常清晰的,在pthread_create中,只能传入一个参数,如果要传入多个参数,可以通过结构体传入。

   这样,一个支持多连接的简单web服务器就完成了,今天就写到这,明天加入以下功能:客户端登陆日志、错误信息日志,报文出错检测、加入默认主页服务,如果时间容许,加入对cgi的支持。

<script type=text/javascript charset=utf-8 src="http://static.bshare.cn/b/buttonLite.js#style=-1&uuid=&pophcol=3&lang=zh"></script> <script type=text/javascript charset=utf-8 src="http://static.bshare.cn/b/bshareC0.js"></script>
阅读(1437) | 评论(0) | 转发(0) |
0

上一篇:规范链表操作

下一篇:TFTP协议详解

给主人留下些什么吧!~~
评论热议
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
一个简易的web服务器通常包括以下几个步骤: 1. 创建一个TCP socket并绑定到指定的IP地址和端口号。 2. 监听来自客户端的连接请求,使用accept()函数接受客户端的连接请求。一旦接受请求,服务器将创建一个新的线程或进程来处理该连接。 3. 接收来自客户端的HTTP请求,解析请求头和请求体。 4. 处理客户端的请求,例如读取文件或执行CGI程序,并生成HTTP响应。HTTP响应包括响应头和响应体。 5. 发送HTTP响应给客户端,关闭连接。 以下是一个简单的Python代码实现: ```python import socket HOST = '127.0.0.1' # 指定服务器IP地址 PORT = 8000 # 指定服务器端口号 def handle_request(client_socket): request_data = client_socket.recv(1024) print(request_data.decode()) response_body = '<html><body><h1>Hello, World!</h1></body></html>' response_header = f'HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nContent-Length: {len(response_body)}\r\n\r\n' response = response_header.encode() + response_body.encode() client_socket.sendall(response) def run_server(): listen_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) listen_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) listen_socket.bind((HOST, PORT)) listen_socket.listen(1) print(f'Server is listening on {HOST}:{PORT}...') while True: client_socket, client_address = listen_socket.accept() handle_request(client_socket) client_socket.close() if __name__ == '__main__': run_server() ``` 这个简单web服务器监听8000端口,一旦接收到客户端的连接请求,就会生成一个HTTP响应,其中包含一个简单的HTML页面。这个服务器只能处理一个客户端请求并返回响应,如果需要支持多个客户端同时连接,需要使用多线程或异步IO等技术。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值