【windows socket+HTTP伺服器客戶端】
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;
}
相關文章
- 【windows socket+TCP伺服器客戶端】WindowsTCP伺服器客戶端
- 【windows socket+UDP伺服器客戶端】WindowsUDP伺服器客戶端
- VNC客戶端是Windows,VNC客戶端是Windows如何進行操作VNC客戶端Windows
- vnc windows客戶端,vnc windows客戶端下載,具體使用教程。VNCWindows客戶端
- VNC客戶端推薦,Windows系統下VNC客戶端推薦VNC客戶端Windows
- Windows下安裝redis客戶端WindowsRedis客戶端
- 小弟求救伺服器-客戶端程式伺服器客戶端
- windows 上rsync客戶端使用方法Windows客戶端
- GitHub Windows客戶端無法登入GithubWindows客戶端
- 客戶端(windows)訪問FTP伺服器遇到的問題(總結)客戶端WindowsFTP伺服器
- Windows客戶端的JProfiler遠端監控Linux上的Tomcat伺服器Windows客戶端LinuxTomcat伺服器
- MQTT伺服器搭建服務端和客戶端MQQT伺服器服務端客戶端
- oracle 客戶端與伺服器端的關係Oracle客戶端伺服器
- js 客戶端與伺服器端的通訊JS客戶端伺服器
- 客戶端 post ,get 訪問伺服器客戶端伺服器
- Redis從客戶端登入伺服器Redis客戶端伺服器
- Oracle客戶端連線伺服器教程Oracle客戶端伺服器
- windows tftp客戶端,5步掌握win7開啟tftp客戶端的方法教程WindowsFTP客戶端Win7
- windows7下安裝oracle客戶端WindowsOracle客戶端
- 客戶端到伺服器端的通訊過程客戶端伺服器
- Redis原始碼剖析——客戶端和伺服器Redis原始碼客戶端伺服器
- 伺服器獲取真實客戶端 IP伺服器客戶端
- 使用 WebSocket 客戶端連線 MQTT 伺服器Web客戶端MQQT伺服器
- 客戶端怎麼連線到伺服器?客戶端伺服器
- redis伺服器/客戶端安裝與配置Redis伺服器客戶端
- 【轉載】MySQL客戶端伺服器協議MySql客戶端伺服器協議
- 客戶端Ping伺服器工具[Python]客戶端伺服器Python
- Easyvision中的伺服器與客戶端伺服器客戶端
- Thrift安裝與伺服器、客戶端的編寫執行演示(windows版本)伺服器客戶端Windows
- IT 團隊文件工具 showdoc 推出 Windows 版客戶端Windows客戶端
- 實戰:Windows防火牆保護客戶端安全Windows防火牆客戶端
- WINDOWS8.1安裝ORACLE客戶端及配置WindowsOracle客戶端
- Nagios 監控windows客戶端安裝教程iOSWindows客戶端
- windows bilibili客戶端快取影片匯出Windows客戶端快取
- dubbo客戶端客戶端
- Pulsar客戶端客戶端
- mqtt 客戶端MQQT客戶端
- windows tftp客戶端,教你幾個步驟掌握win7開啟tftp客戶端的方法WindowsFTP客戶端Win7