【Socket】linux下http服务器开发
2)工作原理
1)客户端
一台客户机与服务器建立连接后,会发送一个请求给服务器,请求方式的格式为:统一资源定位符(URL)、协议版本号,后边是MIME信息,包括请求修饰符、客户机信息和可能的内容。
2)服务器端
1)服务器接收到客户机的请求后,首先解析请求信息,根据不同的请求模式给予相应的响应信息。HTTP中规定了6种请求格式,但最常用到的是GET和POST请求
2)任何服务器除了包括HTML文件以外,还有一个HTTP驻留程序,用于响应用户的请求。
3)服务器端驻留程序接收到请求后,在进行必要的操作后回送所要求的文件,在这一过程中,在网络上发送和接收的数据已经被分成一个或多个数据包(Packet),每个数据包包括:要传送的数据;控制信息,即告诉网络怎样处理数据包。
3)在HTTP请求格式中,最重要的信息有两个,一个是请求的内容,另一个是请求的方法。
4)数据对是由字段组成,其格式为:valuename=value;数据对与数据对之间由&连接,这是HTTP中定义的请求规则。
5)在通常的WEB应用中,大数据量的提交一般选用POST方法,小数据量的提交,如查询操作,一般采用GET方法。
6)请求实体由实体名和实体值组成,它的格式为:Entity:value
2.HTTP服务器设计
1)实现功能
1)在HTTP服务器中,经常要处理的请求是GET,MesteryServer中将要实现针对GET请求的处理
服务器并不支持CGI功能,所以针对GET中的CGI部分的请求处理,在本版本中不予支持。
2)利用Gtk编写可视化的界面,三个按钮用于启动、关闭和暂停服务,一个标签用于显示服务状态。
3)文件传输功能是本 服务器最基本的功能,负责可靠传送用户请求的资源文件。
4)具有日志功能,会将客户的请求信息存储到本地的日志文件里,以XML格式进行存储。
2)业务功能
1)针对GET请求功能,在这里只支持资源文件的下载,并且可以断点下载。
2)如果是请求资源,则予以响应;如果涉及CGI的请求,则不予以响应。
3)系统只支持GET请求,对于POST、HEAD、TRACE等请求,都予以忽略。
4)对于HTTP服务器,它的运行模式是基于请求、响应机制,而下面的文件传输功能,其实是请求的具体执行过程,当服务器端成功解析请求命令后,会针对请求的类型,生成相应的响应码,并传送相应的资源文件。
5)当请求工作执行完毕时,会形成一条日志记录,并插入到日志文件中。
6)考虑到服务的关联性,服务器将为每一个请求开启一个单独的线程进行服务,该线程实现客户端请求接受、请求分析、响应码生成与传输、响应文件查找与传输、客户套接字关闭、日志记录写入等功能
3)可视化界面
其设计与业务的逻辑采用松耦合,双方只是通过消息的方式,传递控制命令与状态信息。
4)主服务功能
提供端口绑定(HTTP默认端口是80)、服务侦听、客户端套接字维护、业务线程创建等。
5)界面模块
1)由两个子模块组成:界面显示子模块->绘出程序的运程界面;按钮事件处理子模块
2)界面模块与主服务模块之间的消息传递采用全局变量共享的方式。
6)主服务模块
1)以线程身份存在。
2)不断轮询全局变量gServerStatus,并通过这个状态来动态调整真实服务状态。
7)业务处理模块
1)程序核心部分。
2)由请求分析子模块、响应处理子模块、文件传输子模块、日志添加子模块等几个子模块组成。
3.测试效果
1)本地测试



4.疑问解答
前几天群里有人在讨论TCP和UDP,然后又引出了HTTP服务器,于是我就给他们推荐了这篇文章,但是大家看过之后还是有很多疑问,这里我根据自己的理解简单描述下。
❶主服务模块的设计原理
可以看见,程序界面是用gtk写的,当点击“开始”按钮的时候,会动态创建该线程,其线程回调函数原型为
void* server_process(void *p)
{
int serverSocket;
struct sockaddr_in server_addr;
struct sockaddr_in clientAddr;
int addr_len = sizeof(clientAddr);
if((serverSocket = socket(AF_INET,SOCK_STREAM,0)) < 0)
{
perror( "error: create server socket!!!");
exit(1);
}
bzero(&server_addr,sizeof(server_addr));
server_addr.sin_family =AF_INET;
server_addr.sin_port = htons(SERVER_PORT);
server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
if(bind(serverSocket,(struct sockaddr *)&server_addr,sizeof(server_addr)) < 0)
{
perror("error: bind address !!!!");
exit(1);
}
if(listen(serverSocket,5)<0)
{
perror("error: listen !!!!");
exit(1);
}
gIsRun = 1;
printf("MesteryServer is running.....\n");
while(gIsRun)
{
int clientsocket;
clientsocket = accept(serverSocket,(struct sockaddr *)&clientAddr,(socklen_t*)&addr_len);
if(clientsocket < 0)
{
perror("error: accept client socket !!!");
continue;
}
if(gServerStatus == 0)
{
close(clientsocket);
}
else if(gServerStatus == 1)
{
pthread_t threadid;
int temp;
temp = pthread_create(&threadid, NULL, processthread, (void *)&clientsocket);
/*if(threadid !=0)
{
pthread_join(threadid,NULL);
}*/
}
}
close(serverSocket);
}
从程序中可以看见,当绑定本地服务地址和端口后,便调用listen()函数进行侦听,while(gIsRun)表示主服务模块已经启动;然后采用阻塞式等待用户连接的到来,在连接到来的时候,还需要判断gServerStatus的值,即系统是否允许提供服务,如果允许,则创建服务线程。
pthread_create(&threadid, NULL, processthread, (void *)&clientsocket);该线程的回调函数为processthread(),具体如下
void* processthread(void *para)
{
int clientsocket;
char buffer[1024];
int iDataNum =0;
int recvnum=0;
clientsocket = *((int *)para);
printf("<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<BEGIN [%d]>>>>>>>>>>>>>>>>>>>>>>>\n",clientsocket);
struct HttpRequest httprequest;
httprequest.content = NULL;
httprequest.path = NULL;
httprequest.path = (char *)malloc(1024);
httprequest.rangeflag = 0;
httprequest.rangestart = 0;
while(1)
{
iDataNum = recv(clientsocket,buffer+recvnum,sizeof(buffer)-recvnum-1,0);
if(iDataNum <= 0)
{
close(clientsocket);
pthread_exit(NULL);
return 0;
}
recvnum += iDataNum;
buffer[recvnum]='\0';
if(strstr(buffer,"\r\n\r\n")!=NULL || strstr(buffer,"\n\n")!=NULL)
break;
}
printf("request: %s\n",buffer); //解析请求信息并处理请求信息
switch(getrequest(buffer,&httprequest))
{
case GET_COMMON:
processgetcommon(clientsocket,&httprequest);
break;
case GET_CGI:
processgetcgi(clientsocket,&httprequest);
break;
case POST:
processpost(clientsocket,&httprequest);
break;
case HEAD:
processhead(clientsocket,&httprequest);
break;
default:
break;
}
insertlognode(pfilelog,&httprequest);
if(httprequest.path != NULL)
free(httprequest.path);
if(httprequest.content != NULL)
free(httprequest.content);
close(clientsocket);
printf("<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<END [%d]>>>>>>>>>>>>>>>>>>>>>>>\n",clientsocket);
pthread_exit(NULL);
}
可以看见,在这个线程里面,便开始对请求进行业务分析了。
❷协议解析
这个比较简单,因为HTTP协议的格式是固定的,所以只用对其按照HTTP的格式进行逐步解析就可以了。
❸文件传输
文件传输是归在GET_COMMON类的
//解析请求信息并处理请求信息
switch(getrequest(buffer,&httprequest))
{
case GET_COMMON:
processgetcommon(clientsocket,&httprequest);
break;
case GET_CGI:
processgetcgi(clientsocket,&httprequest);
break;
case POST:
processpost(clientsocket,&httprequest);
break;
case HEAD:
processhead(clientsocket,&httprequest);
break;
default:
break;
}
processgetcommon()函数实现如下
void processgetcommon(int s,struct HttpRequest *prequest)
{
//先判断文件是否存在
FILE *fp = isexistfile(prequest->path);
printf("%s\n",prequest->path);
struct stat finfo;
if(fp == NULL)
{
responsecode(s,404,prequest);
}
else
{
if(prequest->rangeflag == 0)
{
stat(prequest->path,&finfo);
prequest->rangetotal = finfo.st_size;
}
responsecode(s,200,prequest);
transferfile(s,fp,prequest->rangeflag,prequest->rangestart,prequest->rangetotal);
fclose(fp);
}
}
它先会判断有没有这个文件,如果没有,就生成404响应码,如果有,就返回200响应码,然后首先对prequest->rangeflag进行一个判断,看是否是断点续传,然后便开始传输文件,传输文件函数transferfile()如下
int transferfile(int s,FILE *fp,int type,int rangstart,int totallength)
{
if(type == 1)
{
//为1,则表示当前从指定的位置传送文件
fseek(fp,rangstart,0);
}
int sendnum = 0;
int segment = 1024;
while(!feof(fp)&&sendnum < totallength)
{
char buf[segment];
memset(buf,0,1024);
int i = 0;
while(!feof(fp) && i < segment && sendnum+i < totallength)
{
buf[i++] = fgetc(fp);
}
if(sendsegment(s,buf,i) == 0)
return 0;
sendnum += i;
}
return 1;
}
可以看见,具体的传输文件,是调用sendsegment()函数来实现的。
int sendsegment(int s, char *buffer,int length)
{
if(length <= 0)
return 0;
printf("%s\n",buffer);
int result = send(s,buffer,length,0);
if(result < 0)
return 0;
return 1;
}
而在sendsegment()函数里面,就是用的socket里面的send()函数来实现的。
❹其它功能
对于其它的功能,比如日志操作的,就是属于文件类的了;响应码则是属于对返回信息的一个格式处理,只要按照HTTP协议来就可以了;界面则是用gtk绘制就行了,这个空间比较大,只有绑定相应按钮和处理函数就行了。
5.源代码
请到我原博客附件下载:http://infohacker.blog.51cto.com/6751239/1155176
【Socket】linux下http服务器开发的更多相关文章
- linux下http服务器开发
linux下http服务器开发 1.mystery引入 1)超文本传输协议(HTTP)是一种应用于分布式.合作式.多媒体信息系统的应用层协议 2)工作原理 1)客户端一台客户机与服务器建立连接后,会发 ...
- Linux下配置PHP开发环境
转载于: http://www.uxtribe.com/php/405.html 该站下有系列PHP文章. 在Linux下搭建PHP环境比Windows下要复杂得多.除了安装Apache,PHP等软件 ...
- Linux教程:如何在Linux下进行C++开发?
Linux是一类Unix计算机操作系统的统称,Linux操作系统的内核的名字也是“Linux”, 在Linux下进行C++开发,需要注意许多问题,比如:减少不必要的编辑动作,减少编辑的时间. Wind ...
- Linux下不同服务器间数据传输--转载
因为工作原因,需要经常在不同的服务器见进行文件传输,特别是大文件的传输,因此对linux下不同服务器间数据传输命令和工具进行了研究和总结.主要是rcp,scp,rsync,ftp,sftp,lftp, ...
- Linux下不同服务器间数据传输
因为工作原因,需要经常在不同的服务器见进行文件传输,特别是大文件的传输,因此对linux下不同服务器间数据传输命令和工具进行了研究和总结.主要是rcp,scp,rsync,ftp,sftp,lftp, ...
- Linux下使用Eclipse开发Hadoop应用程序
在前面一篇文章中介绍了如果在完全分布式的环境下搭建Hadoop0.20.2,现在就再利用这个环境完成开发. 首先用hadoop这个用户登录linux系统(hadoop用户在前面一篇文章中创建的),然后 ...
- linux下不同服务器间数据传输(rcp,scp,rsync,ftp,sftp,lftp,wget,curl)(zz)
linux下不同服务器间数据传输(rcp,scp,rsync,ftp,sftp,lftp,wget,curl) 分类: linux2011-10-10 13:21 8773人阅读 评论(1) 收藏 举 ...
- linux下的服务器搭建集成环境
linux下的服务器搭建集成环境 ——写给初学者的我们 1.准备工具 1.1 SecureCRT SecureCRT是一款支持SSH(SSH1和SSH2)的终端仿真程序,简单地说是Windows下登录 ...
- Linux下dns服务器搭建
Linux下dns服务器搭建1-环境Red Hat Enterprise Linux Server release 6.7 (Santiago)2-配置本地yum源安装dns相关包yum -y ins ...
随机推荐
- JAVA List删除时需注意的地方
JAVA的LIST在删除时,一般会用list.remove(o); 但这样往往会出现问题,先来看下面的这段代码: package com.demo; import java.util.ArrayLis ...
- windows 系统变量
%ALLUSERSPROFILE% : 列出所有用户Profile文件位置.%APPDATA% : 列出应用程序数据的默认存放位置.%CD% : 列出当前目录.%CLIENTNAME% : 列出联接到 ...
- ROS学习(十三)—— 编写简单的Service和Client (C++)
一.编写Service节点 1.节点功能: 我们将创建一个简单的service节点("add_two_ints_server"),该节点将接收到两个整形数字,并返回它们的和. 2. ...
- cron执行service
在Cron的环境下,是没有定义路径的,所以,service xxx start等等要使用绝对路径 => /sbin/service xxx start service的路径可以用whereis ...
- android图片等比例缩放 填充屏幕
在ImageView的t同事设置两个属性 android:adjustViewBounds="true"android:scaleType="fitXY"
- php数组添加元素的方法
PHP数组添加一个元素的方式: push(), arr[], Php代码 $arr = array(); array_push($arr, el1, el2 ... eln); 但其实有一种更直 ...
- Linux下实现脚本监测特定进程占用内存情况
Linux系统下,我们可以利用以下命令来获取特定进程的运行情况: cat /proc/$PID/status 其中PID是具体的进程号,这个命令打印出/proc/特定进程/status文件的内容,信息 ...
- easyui combobox 动态加载的两种方法
reload 方法 javascript代码 //指定id 和 text 否则始终选择第一个 $('#contact_city').combobox({ valueField:'id', textFi ...
- 在CentOS上编译安装MySQL 5.7.13步骤详解
MySQL 5.7主要特性 更好的性能 对于多核CPU.固态硬盘.锁有着更好的优化,每秒100W QPS已不再是MySQL的追求,下个版本能否上200W QPS才是用户更关心的. 更好的InnoDB存 ...
- Echarts 如何与 百度地图结合?
官方demo:http://echarts.baidu.com/examples/editor.html?c=map-polygon 需要按顺序加载以下几个资源,然后就可以在echarts配置中使用 ...