Windows Socket+HTTP服务器客户端
Winsock
是 Windows下套接字标准
。
1.HTTP协议:
HTTP是基于客户端/服务器的请求,响应协议。
请求:由客户端向服务器发起,指定了要从服务器获取的资源。请求包含了协议首部,指明了客户端处理能力信息,如可以处理的文件类型,支持的语言,编码方式等。
响应:服务器收到客户端的请求后,解析这个请求,构造响应,并发送给客户端。响应同样包含了协议首部,指明了服务器的相关信息。
2.简易HTTP服务器与客户端:
实际中的HTTP协议,考虑到多种需求,协议具有一定的复杂性,这里我们只实现一个简单的HTTP服务器与客户端,重在理解HTTP协议的工作原理。
Winsock编程下,客户端通过socket向客户端发送一段数据(即请求),这段数据包含了客户端请求的资源(即文件)。
客户端收到这段数据后,对这段数据进行处理(即解析URL),提取客户端请求的资源名,根据资源名找到服务器资源,将资源与其他信息处理后(即响应)发送给客户端。
3.HTTP服务器与客户端实质:
HTTP协议是建立在socket之上的,本质上是两个程序通过socket相互发送数据。HTTP协议,规定了发送方发送数据的格式以及接受方如何使用接受的数据。实现HTTP服务器与客户端,HTTP协议的实现体现在双发对发送与接受数据的处理上。最简单的例子,客户端向服务器发送一个"GET 1.html"数据,服务器收到数据后,解读"GET 1.html",明白客户端想得(GET)到1.html文件,服务器将1.html文件的内容发送给客户端,客户端接收到含1.html文件内容的数据后,新建1.html文件并写入服务器端发送来的数据。
4.牛刀小试:
先在VC6.0中运行服务器,再打开一个VC6.0运行客户端。
运行效果:
常用的浏览器也是客户端,显然我们的客户端只是简单的将html文件内容打印,浏览器则会按HTML规则处理html文件然后显示。
服务器与客户端参考了《Winsock网络编程经络》
客户端程序:
#include
#include
#pragma comment(lib,"ws2_32.lib")
char *http_req_hdr_tmp1="GET %s HTTP/1.1\r\n"
"Accept:image/gif,image/jpeg,*/*\r\nAccept-language:zh-ch\r\n"
"Accept-Encoding:gzip,deflate\r\nHost:%s:%d\r\n"
"Uert-Agent:Jiangwei's Browser<0.1>\r\nConnection:keep-Alive\r\n\r\n";
void http_pares_request_url(char *buf,char *host,
int *port,char *file_name)
{
int length=0;
char port_buf[8];
char *buf_end=(char *)(buf+strlen(buf));
char *begin,*host_end,*colon,*file;
//寻找主机开始位置
begin = strstr(buf,"//");
begin = (begin ? begin+2:buf);
colon = strchr(begin,':');
host_end = strchr(begin,'/');
if(host_end == NULL)
{
host_end = buf_end;
}
else
{
//得到文件名
file = strrchr(host_end,'/');
if(file && (file+1)!=buf_end)
{
strcpy(file_name,file+1);
}
}
if(colon)
{
//得到端口号
colon++;
length = host_end-colon;
memcpy(port_buf,colon,length);
port_buf[length]=0;
*port = atoi(port_buf);
host_end = colon -1;
}
//得到主机
length = host_end-begin;
memcpy(host,begin,length);
host[length]=0;
}
int main()
{
WSADATA wsa;
WSAStartup(MAKEWORD(2,0),&wsa);
SOCKET http_sock;
SOCKADDR_IN serveraddr;
struct hostent *host_ent;
int result;
int Send_len;
char data_buf[1024];
char host[256]="127.0.0.1";
int port=8080;
unsigned long addr;
char file_name[256]="index.html";
FILE *file_web;
/*input为URL
5.html为请求的文件
此时服务器程序文件夹下要要有5.html
*/
char *input="http://127.0.0.1:8080/5.html";
http_pares_request_url(input,host,&port,file_name);
//判断地址是否可用
addr=inet_addr(host);
if(addr==INADDR_NONE)
{
host_ent=gethostbyname(host);
if(!host_ent)
{
printf("服务器不可用!\n");
return -1;
}
memcpy(&addr,host_ent->h_addr_list[0],host_ent->h_length);
}
//根据输入的地址信息,初始化IP,Port
serveraddr.sin_family = AF_INET;
serveraddr.sin_port = htons(port);
serveraddr.sin_addr.s_addr = addr;
//创建服务器socket
http_sock = socket(AF_INET,SOCK_STREAM,0);
//请求连接服务器socket
result = connect(http_sock,(SOCKADDR *)&serveraddr,sizeof(serveraddr));
if(result==SOCKET_ERROR)
{
closesocket(http_sock);
printf("[Web]连接失败!");
return -1;
}
//发送http请求
Send_len=sprintf(data_buf,http_req_hdr_tmp1,input,host,port);
result=send(http_sock,data_buf,Send_len,0);
if(result==SOCKET_ERROR)
{
printf("[Web]发送失败!\n");
return -1;
}
file_web=fopen(file_name,"w+");
//循环接收服务器发来的数据
do
{
result = recv(http_sock,data_buf,1024,0);
if(result > 0)
{
//将接收的数据写入文件
fwrite(data_buf,1,result,file_web);
//打印接收的数据
data_buf[result]=0;
printf("%s",data_buf);
}
}while(result>0);
//关闭文件
fclose(file_web);
//关闭套接字
closesocket(http_sock);
WSACleanup();
return 0;
}
服务器程序:
#include
#include
#pragma comment(lib,"ws2_32.lib")
//定义文件类型对应的content-tyoe
struct doc_type{
char *suffix;
char *type;
};
//文件
struct doc_type file_type[]=
{
{"html", "text/html"},
{"gif", "imag/gif"},
{"jpeg", "imag/jpeg"},
{NULL, NULL}
};
//响应首部内容
char *http_res_hdr_tmp1="HTTP/1.1 200 OK \r\nServer:Jiangwei's Server<0.1>\r\n"
"Accept-Ranges:bytes\r\nContent-Length:%d\r\nConnection:close\r\n"
"Content-Type:%s\r\n\r\n";
//通过后缀,查找到对应的content-type
char *http_get_type_by_suffix(const char *suffix)
{
struct doc_type *type;
for(type=file_type;type->suffix;type++)
{
if(strcmp(type->suffix,suffix)==0)
return type->type;
}
return NULL;
}
//解析客户端发送过来的请求
void http_parse_request_cmd(char *buf,int buflen,char *file_name,char *suffix)
{
int length=0;
char *begin,*end,*bias;
//查找URL开始位置
begin=strchr(buf,' ');
begin++;
//查找URL结束位置
end=strchr(begin,' ');
*end=0;
bias=strrchr(begin,'/');
length=end-bias;
//找到文件名开始的位置
if((*bias=='/')||(*bias=='\\'))
{
bias++;
length--;
}
//得到客户端请求的文件名
if(length>0)
{
memcpy(file_name,bias,length);
file_name[length]=0;
begin = strchr(file_name,'.');
if(begin)
strcpy(suffix,begin+1);
}
}
int http_send_response(SOCKET soc,char *buf,int buf_len)
{
int read_len,file_len,hdr_len,send_len;
char *type;
char read_buf[1024];
char http_header[1024];
char file_name[256]="index.html";
char suffix[16]="html";
FILE *res_file;
//通过解析URL,得到文件名
http_parse_request_cmd(buf,buf_len,file_name,suffix);
//打开文件
res_file=fopen(file_name,"rb+");
if(res_file==NULL)
{
printf("[Web]文件:%s 不存在!\n",file_name);
return 0;
}
//计算文件大小
fseek(res_file,0,SEEK_END);
file_len=ftell(res_file);
fseek(res_file,0,SEEK_SET);
//获得文件content-type
type=http_get_type_by_suffix(suffix);
if(type==NULL)
{
printf("[Web]没有相关的文件类型!\n");
return 0;
}
//构造响应首部,加入文件长度,content-type信息
hdr_len=sprintf(http_header,http_res_hdr_tmp1,file_len,type);
send_len=send(soc,http_header,hdr_len,0);
if(send_len==SOCKET_ERROR)
{
fclose(res_file);
printf("[Web]发送失败,错误:%d\n",WSAGetLastError());
return 0;
}
//发送文件
do
{
read_len=fread(read_buf,sizeof(char),1024,res_file);
if(read_len>0)
{
send_len=send(soc,read_buf,read_len,0);
file_len-=read_len;
}
}while((read_len>0) && (file_len>0));
fclose(res_file);
return 1;
}
int main(){
WSADATA wsa;
WSAStartup(MAKEWORD(2,0),&wsa);
SOCKET serversoc,acceptsoc;
SOCKADDR_IN serveraddr;
SOCKADDR_IN fromaddr;
char Recv_buf[1024];
int from_len=sizeof(fromaddr);
int result;
int Recv_len;
//创建socket
serversoc = socket(AF_INET,SOCK_STREAM,0);
if(serversoc==INVALID_SOCKET)
{
printf("[Web]创建套接字失败!");
return -1;
}
//初始化服务器IP,Port
serveraddr.sin_family = AF_INET;
serveraddr.sin_port = htons(8080);
serveraddr.sin_addr.s_addr = htonl(INADDR_ANY);
//绑定socket
result=bind(serversoc,(SOCKADDR *)&serveraddr,sizeof(serveraddr));
if(result==SOCKET_ERROR)
{
closesocket(serversoc);
printf("[Web]绑定套接字失败!");
return -1;
}
//监听socket请求
result = listen(serversoc,3);
printf("[Web]服务器正在运行.....\n");
while(1)
{
//接收请求
acceptsoc = accept(serversoc,(SOCKADDR *)&fromaddr,&from_len);
if(acceptsoc==INVALID_SOCKET)
{
printf("[Web]接收请求失败!");
break;
}
printf("[Web]连接来自 IP: %s Port: %d \n",inet_ntoa(fromaddr.sin_addr),ntohs(fromaddr.sin_port));
//接收来自客户端的请求
Recv_len=recv(acceptsoc,Recv_buf,1024,0);
if(Recv_len==SOCKET_ERROR)
{
printf("[Web]接收数据失败!");
break;
}
Recv_buf[Recv_len]=0;
//向客户端发送响应数据
result = http_send_response(acceptsoc,Recv_buf,Recv_len);
closesocket(acceptsoc);
}
closesocket(serversoc);
WSACleanup();
return 0;
}