Linux程序设计综合训练之简易Web服务器
1.功能需求:
(1)学习网络套接字编程、HTPP协议、Web服务器等知识;
(2)设计一简单Web服务器,提供静态网页浏览服务功能。
2.实现的功能:
(1)C语言实现基于socket的Web服务器
(2)使用socket通信,使用进程运行
(3)实现遍历指定目录
(4)实现对静态网页的浏览
(5)访问普通文本
(6)执行cgi程序
(7)执行shell程序
(8)浏览图片(jpg,jpeg,gif)
(9)记录日志文件
3.开发环境:
Vmware Workstation 6.4 虚拟机下,用C语言进行开发,开发工具包括:vim,gcc,gdb。
4.实现细节:
客户和服务器都是进程,服务器设立服务,然后进入循环接收和处理请求。客户连接到服务器,然后发送,接收挥着交换数据,最后退出。该交互过程中主要包含三个操作:
(1) 服务器设立服务
a) 建立服务器端socket
i. 创建一个socket
ii. 绑定地址
iii. 监听接入请求
Socket=make_soerver_socket(int protnum)
return -1 if error,
or a server socket listening at port”protnum”
(2) 客户连接到服务器(浏览器)
a) 建立到服务器的链接
i. 创建一个socket
ii. 连接到服务器
(3) 服务器和客户处理事
a) 具体的会话内容
b) 使用fork
i. 当有一个新的访问请求时便创建一个进程来完成事务。
Process_request(fd);
c) 服务器的功能
i. 列举目录信息
ii. Cat文件
iii. 运行程序
(4) web服务器协议
a) http请求:get
Telnet创建一个socket并调用connect来连接web服务器。服务器接受连接请求,并创建一个基于socket的从客户端的键盘到web服务进程的数据通道。
b) http应答:ok
服务器读取请求,检查请求,然后返回一个请求。应答有两部分:头部和内容。头部以状态起始。状态行含有两个或更多的字符串。第一个字符串是协议的版本,第二个是返回码。
结构体:
sockaddr_in(在netinet/in.h中定义):
struct sockaddr_in {
short int sin_family; /* Address family */
unsigned short int sin_port; /* Port number */
struct in_addr sin_addr; /* Internet address */
unsigned char sin_zero[8]; /* Same size as struct sockaddr */
};
struct hostent {
char *h_name; /*地址的正式名称*/
char **h_aliases; /* 空字节-地址的预备名称的指针*/
int h_addrtype; /*地址类型; 通常是AF_INET*/
int h_length; /*地址的比特长度*/
char **h_addr_list; /* 零字节-主机网络地址指针。网络字节顺序*/
};
struct in_addr {
in_addr_t s_addr; /*结构体in_addr 用来表示一个32位的IPv4地址*/
};
5.实现代码:在我的上传资源中有完整代码
socklib.c
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <strings.h>
#include <netdb.h>
#include <netinet/in.h>
#include <time.h>
#include <sys/utsname.h> #define HOSTLEN 256
#define BACKLOG 10 int make_server_socket_q(int,int); int make_server_socket(int protnum)
{
return make_server_socket_q(protnum,BACKLOG);
} int make_server_socket_q(int portnum,int backlog)
{
struct sockaddr_in saddr;
int sock_id;
//创建服务器socket
sock_id=socket(PF_INET, SOCK_STREAM, 0); if(sock_id==-1)//失败
{
return -1;
}
bzero((void *)&saddr,sizeof(saddr));
saddr.sin_addr.s_addr=htonl(INADDR_ANY);
saddr.sin_port=htons(portnum);
saddr.sin_family=AF_INET;
//绑定
if(bind(sock_id,(struct sockaddr *)&saddr,sizeof(saddr))!=0)
return -1;
//监听
if(listen(sock_id,backlog)!=0)
return -1;
return sock_id; } int connect_to_server(char *host,int portnum)
{
int sock;
struct sockaddr_in servadd;//the number to call
struct hostent *hp;//used to get number
//得到一个socket
sock = socket(PF_INET,SOCK_STREAM,0);//get a line
if(sock==-1)
return -1;
//链接
bzero(&servadd,sizeof(servadd));
hp = gethostbyname(host);
if(hp==NULL)
return -1;
bcopy( hp->h_addr,(struct sockaddr*)&servadd.sin_addr, hp->h_length);
// servadd.sin_addr=htonl(INADDE_ANY);
servadd.sin_port=htons(portnum);
servadd.sin_family=AF_INET;
if(connect(sock,(struct sockaddr*)&servadd,sizeof(servadd))!=0)
return -1;
return sock;
}
webserv.c
/*
build :gcc webserv.c socklib.c -o webserv -w
run :./webserv 8080
*/
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <sys/utsname.h>
#include <fcntl.h>
#include <unistd.h>
#include <time.h> main(int ac,char*av[])
{
//socket描述符和accept描述符
int sock,fd;
FILE *fpin;
//保存请求
char request[BUFSIZ];
if(ac==1)
{
fprintf(stderr,"usage:ws portnum\n");
exit(1);
}
sock =make_server_socket(atoi(av[1]));//atoi方法将字符串变成整型
if(sock==-1) exit(2); //创建日志文件
createLog(); while(1)
{
//该函数会阻塞等待客户端请求到达
fd =accept(sock,NULL,NULL);
//只读方式接收请求(文件流)
fpin=fdopen(fd,"r");
//得到请求
fgets(request,BUFSIZ,fpin);
//打印到控制台请求记录
printf("got a call :request = %s",request);
//记录日志文件
writeLog(request);
read_til_crnl(fpin);
//处理请求
process_rq(request,fd);
//结束本次请求
fclose(fpin);
}
} //创建日志文件
createLog()
{
if((access("./log",F_OK))!=-1)
{
//日志文件存在则清空内容
int ret = open("./log", O_WRONLY | O_TRUNC);
if(ret == -1)
{
printf("打开日志文件失败!\n");
return;
}
close(ret);
}
else
{
//日志文件不存在则创建
system("touch ./log");
system("chmod 744 ./log");
}
} //记录日志文件
writeLog(char *request)
{
char temp[225]="got a call : request = ";
strcat(temp,request);
int logfd;
//打开文件
if((logfd=open("./log",O_RDWR|O_APPEND,0644))<0)
{
perror("打开日志文件出错!");
exit(1);
}
//获取当前时间
int z;
struct tm *t;
time_t tt;
time(&tt);
t = localtime(&tt);
char time[18];
sprintf(time,"%4d年%02d月%02d日 %02d:%02d:%02d\r\n", t->tm_year + 1900, t->tm_mon + 1, t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec);
z=write(logfd,time,150);
if(z<0)
{
perror("写入日志文件出错!");
exit(1);
}
//关闭文件
if(close(logfd))
{
perror("关闭日志文件出错!");
exit(1);
} } //读取完整的请求
read_til_crnl(FILE*fp)
{
char buf[BUFSIZ];
while(fgets(buf,BUFSIZ,fp)!=NULL&&strcmp(buf,"\r\n")!=0);
} //处理请求
process_rq(char *rq, int fd)
{
char cmd[BUFSIZ],arg[BUFSIZ];
//创建子进程,如果不是子进程则结束
if (fork()!=0)
return;
strcpy(arg,"./");
if (sscanf(rq,"%s%s",cmd,arg+2)!=2)
return;
if(strcmp(cmd,"GET")!=0)//只能处理静态网页get方式
cannot_do(fd);
else if (not_exist(arg))//请求出错
do_404(arg,fd);
else if (isadir(arg))//判断是否为目录
do_ls(arg,fd);
else if (ends_in_cgi(arg))//是否为cgi程序
do_exec(arg,fd);
else if (ends_in_sh(arg))//是否为sh程序
do_exec_sh(arg,fd);
else
do_cat(arg,fd);
} //获取头部信息
header(FILE *fp,char*content_type)
{
fprintf(fp,"HTTP/1.0 200 OK\r\n");
if(content_type)
fprintf(fp,"Content-type: %s\r\n",content_type);
} //请求501错误
cannot_do(int fd)
{
FILE *fp =fdopen(fd,"w"); fprintf(fp,"HTTP/1.0 501 Not Implemented\r\n");
fprintf(fp,"Content-type:text/plain\r\n");
fprintf(fp,"\r\n");
fprintf(fp,"Sorry,HTTP 501!\n\n无法处理请求!");
fclose(fp); } //请求出错404
do_404(char *item,int fd)
{
FILE *fp=fdopen(fd,"w"); fprintf(fp,"HTTP/1.0 404 Not Found\r\n");
fprintf(fp,"Content-type:text/plain\r\n");
fprintf(fp,"\r\n");
fprintf(fp,"Sorry,HTTP 404!\n\nThe item you requested: %s \r\nis not found\r\n",item);
fclose(fp);
} //判断是否为目录
isadir(char*f)
{
struct stat info;
return (stat(f,&info)!=-1&&S_ISDIR(info.st_mode)); } //不存在
not_exist(char *f)
{
struct stat info;
return (stat(f,&info)==-1);
} //显示目录下内容
do_ls(char*dir,int fd)
{
FILE *fp; fp = fdopen(fd,"w");
header(fp,"text/plain;charset=UTF-8");
fprintf(fp,"\r\n");
fflush(fp); dup2(fd,1);
dup2(fd,2);
close(fd);
execlp("ls","ls","-l",dir,NULL);
perror(dir);
exit(1);
} //返回文件类型
char* file_type(char *f)//return 'extension' of file
{
char *cp;
if((cp=strrchr(f,'.'))!=NULL)
return cp+1;
return " ";
} //cgi类型文件
ends_in_cgi(char *f)
{
return(strcmp(file_type(f),"cgi")==0);
} //执行shell程序
ends_in_sh(char *f)
{
return(strcmp(file_type(f),"sh")==0);
} do_exec_sh(char *prog,int fd)
{
system(prog);
}//shell //执行可执行程序cgi
do_exec(char *prog,int fd)
{
FILE *fp;
fp =fdopen(fd,"w");
header(fp,NULL);
fflush(fp); dup2(fd,1);
dup2(fd,2);
close(fd);
execl(prog,prog,NULL);
perror(prog); } //显示当前目录下全部文件或目录
do_cat(char*f,int fd)
{
char *extension=file_type(f);
char *content="text/html";
FILE *fpsock,*fpfile;
int c; if(strcmp(extension,"html")==0)
content="text/html";
else if (strcmp(extension,"gif")==0)
content="image/gif";
else if(strcmp(extension,"jpg")==0)
content="image/jpeg";
else if(strcmp(extension,"jpeg")==0)
content="image/jpeg"; fpsock = fdopen(fd,"w");
fpfile = fopen(f,"r");
if(fpsock!=NULL&&fpfile!=NULL)
{
header(fpsock,content);
fprintf(fpsock,"\r\n");
while((c=getc(fpfile))!=EOF)
putc(c,fpsock);
fclose(fpfile);
fclose(fpsock);
}
exit(0);
}
Linux程序设计综合训练之简易Web服务器的更多相关文章
- Jexus是一款Linux平台上的高性能WEB服务器和负载均衡网关
什么是Jexus Jexus是一款Linux平台上的高性能WEB服务器和负载均衡网关,以支持ASP.NET.ASP.NET CORE.PHP为特色,同时具备反向代理.入侵检测等重要功能.可以这样说,J ...
- 在Linux上使用web2py_uwsgi_nginx搭建web服务器
本文介绍在Linux使用Python+Nginx+web2py+uWSGI搭建一个web服务器的过程. Python 2.7.11 解压安装包 tar -zxvf Python-2.7.11.tgz ...
- [js高手之路]node js系列课程-创建简易web服务器与文件读写
web服务器至少有以下几个特点: 1.24小时不停止的工作,也就是说这个进程要常驻在内存中 2.24小时在某一端口监听,如: http://localhost:8080, www服务器默认端口80 3 ...
- linux下通过curl访问web服务器
在通过xshell或者其他远程连接工具连接linux服务器,没安装浏览器,却要测试web服务的请求: 可以使用curl 访问web服务器 例如返回百度的主页内容 #curl www.baidu.com ...
- 手写简易WEB服务器
今天我们来写一个类似于Tomcat的简易服务器.可供大家深入理解一下tomcat的工作原理,本文仅供新手参考,请各位大神指正!首先我们要准备的知识是: Socket编程 HTML HTTP协议 服务器 ...
- Windows&linux使用集成环境搭建 web 服务器
文章更新于:2020-02-17 按照惯例,需要的文件附上链接放在文首 文件名:phpStudy_64.7z 文件大小:78.3 M 下载链接https://www.lanzous.com/i9c6l ...
- 自己实现一个简易web服务器
一个web服务器是网络应用中最基础的环节. 构建需要理解三个内容: 1.http协议 2.socket类 3.服务端实现原理 1.1 HTTP http请求 一般一个http请求包括以下三个部分: 1 ...
- 写一个简易web服务器、ASP.NET核心知识(4)--转载
第一次尝试(V1.0) 1.理论支持 这里主要要说的关于Socket方面的.主要是一个例子,关于Socket如何建立服务端程序的简单的代码. static void Main(string[] arg ...
- 简易web服务器
当通过Socket开发网络应用程序的时候,首先需要考虑所使用的网络类型,主要包括以下三个方面: 1)Socket类型,使用网络协议的类别,如IPv4的类型为PF_INET. 2)数据通信的类型,常见的 ...
随机推荐
- ES6对象扩展
前面的话 随着JS应用复杂度的不断增加,开发者在程序中使用对象的数量也在持续增长,因此对象使用效率的提升就变得至关重要.ES6通过多种方式来加强对象的使用,通过简单的语法扩展,提供更多操作对象及与对象 ...
- Postgres by BigSQL and Hadoop_fdw
Postgres by BigSQL and hadoop_fdw 测试Postgresql和远程Hive的Join操作. 测试环境 Centos6.8 HDP2.4集群,其中Hive Server2 ...
- java中方法的参数传递机制
问:当一个对象被当作参数传递到一个方法后,此方法可改变这个对象的属性,并可返回变化后的结果,那么这里到底是值传递还是引用传递? 答:是值传递.Java 编程语言只有值传递参数.当一个对象实例作为一个 ...
- linux(centos)下安装PHP的PDO扩展
PHP 数据对象PDO扩展为PHP访问数据库定义了一个轻量级的一致接口.PDO 提供了一个数据访问抽象层,这意味着,不管使用哪种数据库,都可以用相同的函数(方法)来查询和获取数据.最近在我们的建站和O ...
- ES6中的模块
前面的话 JS用"共享一切"的方法加载代码,这是该语言中最容出错且容易令人感到困惑的地方.其他语言使用诸如包这样的概念来定义代码作用域,但在ES6以前,在应用程序的每一个JS中定义 ...
- echarts_部分图表配置_dataZoom精确控制显示数据数量
echarts为我们提供了dataZoom组件,当数据过多时就有了它的用武之地,业务场景:数据返回100调可是为了前端显示效果默认只显示20条,其他数据由dataZoom控制显示隐藏: functio ...
- webpack开发与生产环境配置
前言 作者去年就开始使用webpack, 最早的接触就来自于vue-cli.那个时候工作重点主要也是 vue 的使用,对webpack的配置是知之甚少,期间有问题也是询问大牛 @吕大豹.顺便说一句,对 ...
- TCP 滑动窗口
滑动窗口协议 流量控制方法 PUSH 慢启动 隔一个报文段确认"的策略实际就是因为 delayed ack,同时接收到两个待确认的ACK包时,就立即发送确认包. 滑动窗口实例 解 ...
- java对excel表格的上传和下载处理
Excel表格文件的上传和下载,java中涉及到文件肯定会有io流的知识. 而excel文件就要涉及到poi技术,而excel的版本包括:2003-2007和2010两个版本, 即excel的后缀名为 ...
- 解决Ubuntu手动安装vim后无法正常…
首先声明这个问题很坑爹~ 问题描述:下载了vim7.3版本的源码,在虚拟机里面的ubuntu12中手动安装成功后.在使用vim编辑文档时,进入编辑模式出现如下现象:1.使用方向键会打印出"A ...