一个简单web server 的实现

昨天在 Source Forge 中下了 Ani-server 的源码, 代码不过几百行, 已经有一个基本 web server 的功能.

参照其思路, 自己实现了下, 有点意思.

基本思路:

1)取得一 socket 的 fd -->  bind --> listen -->select --> accept, 

    得到一 cli_fd, 通过它可以和请求服务的浏览器通信

2)从 cli_fd 读取数据, 判断是否是 http 的 GET, 获得被请求的文件, 将文件数据写入 cli_fd.

源码:

/* 
 * author: dengzhaoqun
 * date: 2011/07/21
 * platform: linux
 */
#include <sys/select.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <unistd.h>

#define DEFAULT_PAGE "index.html"
#define WEB_ROOT "/home/xiaodeng/aniroot"

void connection(int fd);
int recv_new(int fd, char *str);
void send_new(int fd, char *str);
int get_file_size(int fd);

int main()
{
	int ser_fd, cli_fd, max_fd, fd;
	struct sockaddr_in ser_addr;
	struct sockaddr_in cli_addr;
	int ser_addr_len, cli_addr_len;
	fd_set readfds, master;
	int ret;


	ser_fd = socket(AF_INET, SOCK_STREAM, 0);
	if(ser_fd == -1)
	{
		perror("socket failed");
		exit(1);
	}

	ser_addr_len = sizeof(ser_addr);
	memset(&ser_addr, 0, ser_addr_len);

	ser_addr.sin_family = AF_INET;
	ser_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
	ser_addr.sin_port = htons(80);

	ret = bind(ser_fd, (struct sockaddr *)&ser_addr, ser_addr_len);
	if(ret == -1)
	{
		perror("bind failed");
		exit(1);
	}

	listen(ser_fd, 5);

	FD_ZERO(&master);
	FD_SET(ser_fd, &master);
	max_fd = ser_fd;

	while(1)
	{
		readfds = master;
		ret = select(max_fd + 1, &readfds, 0, 0, 0);
		if(ret == -1)
		{	
			perror("select failed");
		}
		else
		{
			for(fd = 0; fd < max_fd + 1; fd ++)
			{
				if(FD_ISSET(fd, &readfds))
				{
					if(fd == ser_fd)
					{
						cli_addr_len = sizeof(cli_addr);
						memset(&cli_addr, 0, cli_addr_len);
						cli_fd = accept(ser_fd,
							       	(struct sockaddr *)&cli_addr,
								(socklen_t *)&cli_addr_len);
						if(cli_fd > max_fd)
						{
							max_fd = cli_fd;
						}

						FD_SET(cli_fd, &master);
					}
					else
					{
						connection(fd);
						FD_CLR(fd, &master);
					}
				}
			}
		}
	}

}/* main() */



void
connection(int fd)
{
	char request[1000], resource[1000], *ptr;
	int ret;
	int page_fd;

	ret = recv_new(fd, request);
	
	ptr = strstr(request, " HTTP/");
	if(ptr == NULL)
	{
		perror("Not HTTP request");
	}
	else
	{
		*ptr = 0;
		ptr = NULL;

		if(strncmp(request, "GET ", 4) == 0)
		{
			ptr = request + 4;
		}
		if(strncmp(request, "HEAD ", 5) == 0)
		{
			ptr = request + 5;
		}
		if(ptr == NULL)
		{
			perror("Unknown Request. \n");
		}
		else
		{
			memset(resource, 0, sizeof(resource));
			strncpy(resource, WEB_ROOT, sizeof(resource));
			strncat(resource, ptr, sizeof(resource) - sizeof(WEB_ROOT) -1);
			if(ptr[strlen(ptr) -1] == '/' )
			{
				strncat(resource, DEFAULT_PAGE, sizeof(resource)
						- strlen(resource)
						- sizeof(DEFAULT_PAGE)
						- 1);
			}

			if((page_fd = open(resource, O_RDONLY)) == -1)
			{
				perror("open failed, file not found");
				send_new(fd, "HTTP/1.1 404 NOT FOUND\r\n");
				send_new(fd, "<html><head>404 NOT FOUND</head>");
				send_new(fd, "<body>Sorry, NOT FOUND</body></html>");
				close(fd);
			}
			else
			{
				int file_size;
				
				memset(request, 0, sizeof(request));
				file_size = get_file_size(page_fd);
				read(page_fd, request, file_size);
				
				send_new(fd, "HTTP/1.1 200 OK\r\n");
				send_new(fd, request);
				close(fd);
			}
		}
	}
}/* connection() */
				

void
send_new(int fd, char *str)
{
	int len;
	len = strlen(str);
	write(fd, str, len);
}/* send_new() */

int
get_file_size(int fd)
{
	struct stat buf;
	
	fstat(fd, &buf);

	return(buf.st_size);
}/* get_file_size() */

int
recv_new(int fd, char *str)
{
	char *p = str;
	int flag;

	flag = 0;
	while(1)
	{
		read(fd, p, 1);
		if(*p == '\r')
			flag = 1;
		if( flag && (*p == '\n'))
		{
			flag = 0;
			break;
		}
		p ++;
	}
	*(p-1) = '\0';

	return(strlen(str));
}/* recv_new() */


编译后运行, 在浏览器中输入 "http://localhost/filename.html" , 即可在浏览器中显示 WEB_ROOT 目录下的相应 html 文件.


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值