一个简单静态web服务器

最近看完了apue中关于socket编程的部分,想要练习,就写了一个简单的静态web服务器。由于由于不了解http协议,通过参考其他博客简单学习了一下,在自己的web服务器中只实现了http协议中的 GET指令。

代码
#include<stdio.h>
#include<string.h>
#include<strings.h>
#include<stdlib.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<signal.h>
#include<sys/stat.h>
#include<fcntl.h>
#define SERVER_STRING "Server: myWeb/0.1.0\r\n"
void* accept_request(void *arg);//对每个http请求进行处理
void header(int);//发送http信息头文件
void error_die(const char *);//错误函数
int get_line(int,char*,int);//读取每一行http请求
void not_found(int);//未找到cliet请求的文件时进行处理
void serve_file(int,const char*);//发送client请求的文件
void unimplemented(int client);//只实现了get方法,收到其他http请求时调用
void SIGHandler(int);//信号处理函数,屏蔽掉SIGPIPE,防止服务器意外终止
void header(int conn_sock)
{
    char buf[1024];
    strcpy(buf,"HTTP/1.0 200 OK\r\n");
    send(conn_sock,buf,strlen(buf),0);
    strcpy(buf,SERVER_STRING);
    send(conn_sock,buf,strlen(buf),0);
    strcpy(buf,"Content-Type: text/html\r\n");
    send(conn_sock,buf,strlen(buf),0);
    strcpy(buf,"\r\n");
    send(conn_sock,buf,strlen(buf),0);

}
void serve_file(int conn_sock,const char* path)
{
    int readnum=1;
    FILE* fd;
    char buf[1024];
    buf[0]='A';
    buf[1]='\0';
    while((readnum>0)&&strcmp("\n",buf))
        get_line(conn_sock,buf,sizeof(buf));
    fd=fopen(path,"r");
    if(fd==NULL)
    {   
        //printf("zhaobudaowenjian\n");
        not_found(conn_sock);
        return NULL;
    }
   // printf("fasong wenjian\n");
    header(conn_sock);
    while(fgets(buf,sizeof(buf),fd)>0)
    {
        send(conn_sock,buf,strlen(buf),0);
        //printf("%s\n",buf);
    }
    return NULL;
        
}
void not_found(int client)
{
 char buf[1024];
 //printf("fasong not found\n");
 sprintf(buf, "HTTP/1.0 404 NOT FOUND\r\n");
 send(client, buf, strlen(buf), 0);
 sprintf(buf, SERVER_STRING);
 send(client, buf, strlen(buf),0);
 sprintf(buf, "Content-Type: text/html\r\n");
 send(client, buf, strlen(buf), 0);
 sprintf(buf, "\r\n");
 send(client, buf, strlen(buf), 0);
 sprintf(buf, "<HTML><TITLE>Not Found</TITLE>\r\n");
 send(client, buf, strlen(buf), 0);
 sprintf(buf, "<BODY><P>The server could not fulfill\r\n");
 send(client, buf, strlen(buf), 0);
 sprintf(buf, "your request because the resource specified\r\n");
 send(client, buf, strlen(buf), 0);
 sprintf(buf, "is unavailable or nonexistent.\r\n");
 send(client, buf, strlen(buf), 0);
 sprintf(buf, "</BODY></HTML>\r\n");
 send(client, buf, strlen(buf), 0);
}

void* accept_request(void* arg)
{
    char Buffer[1024];
    char commond[64];
    char url[64];
    char path[128];
    int readnum=0;
    int i=0,j=0;
    int conn_sock=(int)arg;
    struct stat st;
    if((readnum=get_line(conn_sock,Buffer,1024))<0)//获取第一条http请求
        error_die("get_line error");
    //printf("Buffer:%s\n",Buffer);
    
    while(!isspace(Buffer[i])&&(i<sizeof((commond)-1)))//将第一条请求中的方法提取出来
    {
        commond[i]=Buffer[i];
        i++;
    }
    commond[i]='\0';
    //printf("commond:%s\n",commond);
    if(strcasecmp(commond,"GET"))//如果请求方法不是get
    {
		while((readnum>0) && strcmp("\n",Buffer))
		{         
            readnum=get_line(conn_sock,Buffer,sizeof(Buffer));
	    	//printf("%s\n",Buffer);
		}
        //printf("%s wei shixian",commond);
        sprintf(Buffer, "HTTP/1.0 501 Method Not Implemented\r\n");
        send(conn_sock, Buffer, strlen(Buffer), 0);
        sprintf(Buffer, SERVER_STRING);
        send(conn_sock, Buffer, strlen(Buffer), 0);
        sprintf(Buffer, "Content-Type: text/html\r\n");
        send(conn_sock, Buffer, strlen(Buffer), 0);
        sprintf(Buffer, "\r\n");
        send(conn_sock, Buffer, strlen(Buffer), 0);
        sprintf(Buffer, "<HTML><HEAD><TITLE>Method Not Implemented\r\n");
        send(conn_sock, Buffer, strlen(Buffer), 0);
        sprintf(Buffer, "</TITLE></HEAD>\r\n");
        send(conn_sock, Buffer, strlen(Buffer), 0);
        sprintf(Buffer, "<BODY><P>HTTP request method not supported.\r\n");
        send(conn_sock, Buffer, strlen(Buffer), 0);
        sprintf(Buffer, "</BODY></HTML>\r\n");
        send(conn_sock, Buffer, strlen(Buffer), 0);
    	close(conn_sock);
        return NULL;
    }
    j=i;
    i=0;
    while(isspace(Buffer[j])&&j<sizeof(Buffer))
        j++;
    while(!isspace(Buffer[j])&&j<sizeof(Buffer)&&i<(sizeof(url)-1))//提取请求中的文件名
    {
        url[i]=Buffer[j];
        i++;
        j++;
    }
    url[i]='\0';
    // printf("url:%s\n",url);
    // printf("pthread end\n");
    sprintf(path, "file%s", url);//html文件都放在file文件夹中
    if(path[strlen(path)-1]=='/')//如果请求中未写明请求文件,默认未/index.html
        strcat(path,"index.html");
    //printf("path:%s\n",path);
    if(stat(path,&st)==-1)//如果文件未找到
    {

        while((readnum>0) && strcmp("\n",Buffer))
		{         
			readnum=get_line(conn_sock,Buffer,sizeof(Buffer));
		}
        not_found(conn_sock);
    }
    else
    {
       // printf("fasong wenjian\n");
        serve_file(conn_sock,path);
    }
    // printf("close socket\n");
         close(conn_sock);

    return NULL;        
}
void SIGHandler(int signo)
{
    if(signo==SIGPIPE)
    {
        printf("recv SIGPIPE\n");
    }
}
int get_line(int sock,char* buf,int size)
{
 int i = 0;
 char c = '\0';
 int n;

 while ((i < size - 1) && (c != '\n'))
 {
  n = recv(sock, &c, 1, 0);
  if (n > 0)
  {
   if (c == '\r')
   {
    n = recv(sock, &c, 1, MSG_PEEK);
    if ((n > 0) && (c == '\n'))
     recv(sock, &c, 1, 0);
    else
     c = '\n';
   }
   buf[i] = c;
   i++;
  }
  else
   c = '\n';
 }
 buf[i] = '\0';
 
 return(i);
}
int main(void)
{
    pthread_t pid;
    int server_sock;
    int port=7777;
    int client_sock;
    struct sigaction act;
    struct sockaddr_in client_name;
    struct sockaddr_in server_addr;
    act.sa_handler=SIGHandler;
    sigemptyset(&act.sa_mask);
    act.sa_flags=0;
    sigaction(SIGPIPE,&act,NULL);
    int client_name_len=sizeof(client_name);
    server_sock=socket(AF_INET,SOCK_STREAM,0);
    if(server_sock<0)
        error_die("socket error");
    bzero(&server_addr,sizeof(server_sock));
    server_addr.sin_family=AF_INET;
    server_addr.sin_port=htons(port);
    server_addr.sin_addr.s_addr=htonl(INADDR_ANY);
    if(bind(server_sock,(struct sockaddr*)&server_addr,sizeof(server_addr))<0)
        error_die("bind error");
    if(listen(server_sock,3)<0)
        error_die("listen error");
    //printf("server run\n");
    while(1)
    {
        client_sock=accept(server_sock,(struct sockaddr*)&client_name,&client_name_len);
        //printf("accetped\n");
        if(client_sock<0)
            error_die("accept error");
        if(pthread_create(&pid,NULL,accept_request,(void*)client_sock)<0)
           error_die("pthread create error");
	//accept_request((void*)client_sock);
	//close(client_sock);
    }
    
}
void error_die(const char* sc)
{
    perror(sc);
    exit(1);
}

说一下在写的过程中踩到的大坑:
1)get_line函数中,没有给读取的信息后面加上’\0’,导致读取到的http请求会有乱码,使得服务器不能正常工作。在main函数和accept_request函数中一直差错都找不到错误,最后才想到可能是get_line函数出现了问题(?)
2)server对client发送信息之前都有一个将client的请求全部读取掉的操作:

		while((readnum>0) && strcmp("\n",Buffer))
		{         
            readnum=get_line(conn_sock,Buffer,sizeof(Buffer));
	    	//printf("%s\n",Buffer);
		}

在最初的编写中并没有这个操作,在server端获取第一条请求指令解析后,将client所需要的html文件发送给client后关闭套接字即可。但是发现服务器并不能正常运行,浏览器访问时会出现ERR_CONNECTION_RESET错误。
通过查阅资料后知道,在通常情况下,调用close的时候,会发FIN包,但是,如果接收端没有用recv把内核缓冲区的数据取完,却执行了关闭socket的操作(比如调用close或者进程挂掉), 那么这就是异常的情况,此时接收端会回RST报文。这个RST报文会导致浏览器出现ERR_CONNECTION_RESET错误。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
定义: 这是一款桌面级的WEB服务器 包含一个静态的http服务器与一个js脚本引擎 可以展示静态的网页与生成简单的动态页面 适合个人在windows服务器上面简单的建立http服务 支持情况: 静态http session application(仅仅能存取文本) 服务端执行的js脚本 数据库访问(反正ODBC支持的库都支持) 自定义的com组件引用 简易说明: 1 建立站点 打开编辑 设置 输入站点名称与路径 点击添加即可建立一个站点 如: myweb c: www 注意路径要以 结尾 在站点根目录下添加filter sjs与endfilter sjs(这是两个必须的过滤器 如无需写代码放两个空文件即可) 2 静态资源与动态页面 在server sjs里的server execFileTypes定义可以配置将哪些文档类型作为动态页面 当请求静态资源时会直接返回资源 当请求一个动态页面时 请求会依次在filter sjs 请求页面 endfilter sjs 进行转发 当然也可以在filter sjs里写代码来终止转发 动态页面中如果文档类型为 sjs服务器将识别为纯的服务端执行js脚本(好比servlet什么的) 在其他类型的动态页面文档中 目前有3种类型嵌入标签可用: <%c %>标签表示嵌入一段服务端执行js脚本 如:<body><%c response responseText+ "hello js"%>< body> 将返回页面<body>hello js< body> <%i %>标签表示引用资源 如:<%i src "parts part htm"%> <% %>标签表示插值 如:<body><% "hello js"%>< body> 将返回页面<body>hello js< body> 3 com组件引用 为了让web应用有更多功能扩展 该服务器可以在脚本中引用其他com组件 这里有2种引用方法 一种方法在设置里面添加引用变量名 组件名 引用类型;来添加其他com组件的引用 组件名写成 组件工程名 组件类名 形式 就像使用CreateObject时一样 引用类型可写sing与muti 其中sing为所有请求共用一个组件实例对象 muti为每个请求引用独立的组件实例对象 另外还可以在代码里使用objectLoader loadComObject attr comNm 来添加组件引用 attr为引用变量名 comNm为组件名 注意:一个新的组件在引用前应先用regsvr32 dll注册">定义: 这是一款桌面级的WEB服务器 包含一个静态的http服务器与一个js脚本引擎 可以展示静态的网页与生成简单的动态页面 适合个人在windows服务器上面简单的建立http服务 支持情况: 静态http session application(仅仅能存取文本 [更多]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值