WebService的简单实现
一、socket主机创建和使用过程
1、socket()//创建套接字
2、Setsockopt()//将套接字属性设置为允许和特定地点绑定
3、Bind()//将套接字绑定特定地址端口
4、Listen()//打开监听端口属性
以下重复进行
5、Accept()//接收客户端的连接请求
6、Read()//从客户端读数据
7、Write()//将处理好的结果发送给客户端
二、HTTP传输协议
基于socket的TCP通信,按HTTP传输协议格式化传输内容。
示例:
1、客户端发送HTTP请求
GET/txt?hal=1000HTTP/1.1
Host:localhost:1024
User-Agent:Mozilla/5.0(X11;Linuxi686;rv:2.0)Gecko/20100101Firefox/4.0
Accept:text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language:zh-cn,zh;q=0.5
Accept-Encoding:gzip,deflate
Accept-Charset:GB2312,utf-8;q=0.7,*;q=0.7
Keep-Alive:115
Connection:keep-alive
GET:发送HTTP请求的方法,还可以是SET或者POST
/txt?hal=1000是请求根目录下的txt文件内容并传入参数hal=1000
HTTP/1.1表示HTTP版本是1.1
2、服务端传回HTTP响应
HTTP/1.0200OK
Server:ReageWebServer
Content-Type:text/html
<!DOCTYPEhtmlPUBLIC"-//W3C//DTDXHTML1.0Strict//EN""http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<htmlxmlns="http://www.w3.org/1999/xhtml">
<!--Copyright(c)2000-2008QuadralayCorporation.Allrightsreserved.-->
<head>
<title>WebWorksHelp5.0</title>
</head>
<body>wuff</body>
</html>
前面四行(包括空行)是消息体,后面是消息。一般要指明消息体的长度,方便客户端的接收处理。
三、示例程序
======================================================================
/*
*主要实现功能,处理浏览器的get请求信息,发送网页文件。处理404、403等错误。
*1.实现绑定本机机器的1024端口作为ReageWeb服务提供网页服务的端口。(避免与机器上装有web服务器产生端口冲突)
*2.实现get获取网页方式。
*3.实现index.html作为网站的首页面
*作者:Reage
*blog:http://blog.csdn.net/rentiansheng
*/
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<sys/un.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<fcntl.h>
#include<string.h>
#include<sys/stat.h>
#include<signal.h>
#defineMAX1024
intres_socket;
voidapp_exit();
/*
@description:开始服务端监听
@parameter
ip:web服务器的地址
port:web服务器的端口
@result:成功返回创建socket套接字标识,错误返回-1
*/
intsocket_listen(char*ip,unsignedshortintport){
intres_socket;//返回值
intres,on;
structsockaddr_inaddress;
structin_addrin_ip;
res=res_socket=socket(AF_INET,SOCK_STREAM,0);
setsockopt(res_socket,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on));
memset(&address,0,sizeof(address));
address.sin_family=AF_INET;
address.sin_port=htons(port);
address.sin_addr.s_addr=htonl(INADDR_ANY);//inet_addr("127.0.0.1");
res=bind(res_socket,(structsockaddr*)&address,sizeof(address));
if(res){printf("portisused,nottorepeatbind\n");exit(101);};
res=listen(res_socket,5);
if(res){printf("listenportiserror;\n");exit(102);};
returnres_socket;
}
/*
@description:向客户端发送网页头文件的信息
@parameter
conn_socket:套接字描述符。
status:http协议的返回状态码。
@s_status:http协议的状态码的含义
@filetype:向客户端发送的文件类型
*/
voidsend_http_head(intconn_socket,intstatus,char*s_status,char*filetype){
charbuf[MAX];
memset(buf,0,MAX);
sprintf(buf,"HTTP/1.0%d%s\r\n",status,s_status);
sprintf(buf,"%sServer:ReageWebServer\r\n",buf);
sprintf(buf,"%sContent-Type:%s\r\n\r\n",buf,filetype);
write(conn_socket,buf,strlen(buf));
}
/*
@description:向客户端发送错误页面信息
@parameter
conn_socket:套接字描述符。
status:http协议的返回状态码。
@s_status:http协议的状态码的含义
@filetype:向客户端发送的文件类型
@msg:错误页面信息内容
*/
voidsend_page_error(intconn_socket,intstatus,char*s_status,char*msg){
charbuf[MAX];
sprintf(buf,"<html><head></head><body><h1>%s</h1><hr>ReageWebServer0.01</body></head>",msg);
send_http_head(conn_socket,status,s_status,"text/html");
write(conn_socket,buf,strlen(buf));
}
/*
@description:向客户端发送文件
@parameter
conn_socket:套接字描述符。
@file:要发送文件路径
*/
intsend_html(intconn_socket,char*file){
intf;
charbuf[MAX];
inttmp;
structstatfile_s;
//如果file为空,表示发送默认主页。主页暂时固定
if(0==strlen(file)){
strcpy(file,"index.html");
}
//如果获取文件状态失败,表示文件不存的,发送404页面,暂时404页面内容固定。
if(stat(file,&file_s)){
send_page_error(conn_socket,404,"Notfound","Notfound<br/>Reagedoesnotimplementthismothod\n");
return0;
}
//如果不是文件或者无读权限,发送无法读取文件
if(!(S_ISREG(file_s.st_mode))||!(S_IRUSR&file_s.st_mode)){
send_page_error(conn_socket,403,"Forbidden","Forbidden<br/>Reagecouldn'treadthefile\n");
return0;
}
//发送头文件,现在只提供html页面
send_http_head(conn_socket,200,"OK","text/html");
f=open(file,O_RDONLY);
if(0>f){
//打开文件失败,发送404页面,其实感觉发送5xx也可以的,服务器内部错误
send_page_error(conn_socket,404,"Notfound","Notfound<br/>Reagecouldn'treadthefile\n");
return0;
}
buf[MAX-1]=0;//将文件内容缓冲区最后的位设置位结束标志。
//发送文件的内容
while((tmp=read(f,buf,MAX-1))&&EOF!=tmp){
write(conn_socket,buf,strlen(buf));
}
}
/*
@description:提取url中可用的信息。访问的网页和数据访问方式
@parameter:
conn_socket:与客户端链接的套接字
uri:要处理的url,注意不是浏览器中的url,而是浏览器发送的http请求
@resutl:
*/
intdo_uri(intconn_socket,char*uri){
char*p;
p=strchr(uri,'?');
if(p){*p=0;p++;}
send_html(conn_socket,uri);
}
voidulog(char*msg){}
voidprint(char*msg){
ulog(msg);
printf(msg);
}
intmain(intargc,char*argv[]){
intconn_socket;
inttmp;
intline;
structsockaddr_inclient_addr;
charbuf[MAX];
intlen=sizeof(client_addr);
charmethod[100],uri[MAX],version[100];
charpwd[1024];
res_socket=socket_listen("127.0.0.1",1024);
//当按ctrl+c结束程序时调用,使用app_exit函数处理退出过程
signal(SIGINT,app_exit);
while(1){
conn_socket=accept(res_socket,(structsockaddr*)&client_addr,&len);
printf("reage\n");
line=0;
//从客户端获取请求信息
while(0==(tmp=read(conn_socket,buf,MAX-1))||tmp!=EOF){
buf[MAX-1]=0;
break;//我只使用了第一行的请求信息,所以丢弃其他的信息
}
//send_http_head(conn_socket,200,"text/html");
sscanf(buf,"%s%s%s",method,uri,version);
//目前只处理get请求
if(!strcasecmp(method,"get"))
//send_html(conn_socket,"h.html");
do_uri(conn_socket,uri+1);
close(conn_socket);
}
}
voidapp_exit(){
//回复ctrl+c组合键的默认行为
signal(SIGINT,SIG_DFL);
//关闭服务端链接、释放服务端ip和端口
close(res_socket);
printf("\n");
exit(0);
}
======================================================================