凡客專賣店網(wǎng)站優(yōu)化入門
實(shí)現(xiàn)簡單的Web服務(wù)器端
現(xiàn)在開始在HTTP協(xié)議的基礎(chǔ)上編寫Web服務(wù)器端。先給出Windows平臺下的示例,再給出Linux下的示例。在這里我假設(shè)各位都有了有關(guān)HTTP的知識,如果不知道HTTP協(xié)議的具體內(nèi)容可以參考的往期博客,有了這些基礎(chǔ)就不難分析源代碼。因此,除了一些簡單的注釋外,不再另行說明代碼。
實(shí)現(xiàn)基于Windows的多線程Web服務(wù)器端
Web服務(wù)器端采用HTTP協(xié)議,即使用IOCP或epoll模型也不會大幅提升性能(當(dāng)然并不是完全沒有)。客戶端和服務(wù)器端交換1次數(shù)據(jù)后將立即斷開連接,沒有足夠時間發(fā)揮IOCP或epoll的優(yōu)勢。在服務(wù)器端和客戶端保持較長連接的前提下頻繁發(fā)送大小不一的消息時(最典型的就是網(wǎng)游服務(wù)器端),才能真正發(fā)揮出這2種模型的優(yōu)勢。
因此,我通過多線程模型實(shí)現(xiàn)Web服務(wù)器端。也就是說,客戶端每次請求時,都創(chuàng)建1個新線程響應(yīng)客戶端請求。
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<winsock2.h>
#include <process.h>#define BUF_SIZE 2048
#define BUF_SMALL 100unsigned WINAPI RequestHandler(void* arg);
char* ContentType(char* file);
void SendData(SOCKET sock,char* ct, char* fileName);
void SendErrorMSG(SOCKET sock);
void ErrorHandling(char *message);int main(int argc, char *argv[]){WSADATA wsaData;SOCKET hServSock,hClntSock;SOCKADDR_IN servAdr, clntAdr;HANDLE hThread;DWORD dwThreadID;int clntAdrSize;if(argc!=2){printf("Usage : %s <port>\n", argv[0]);exit(1);}if(WSAStartup(MAKEWORD(2,2),&wsaData)!=0)ErrorHandling("WSAStartup() error!");hServSock=socket(PF_INET,SOCK_STREAM, 0);memset(&servAdr,0, sizeof(servAdr));servAdr.sin_family=AF_INET;servAdr.sin_addr.s_addr=htonl(INADDR_ANY);servAdr.sin_port=htons(atoi(argv[1]));if(bind(hServSock,(SOCKADDR*)&servAdr, sizeof(servAdr))==SOCKET_ERROR)ErrorHandling("bind() error");if(listen(hServSock, 5)==SOCKET_ERROR)ErrorHandling("listen() error");/*請求及響應(yīng)*/while(1){clntAdrSize=sizeof(clntAdr);hClntSock=accept(hServSock,(SOCKADDR*)&clntAdr, &clntAdrSize);printf("Connection Request : %s:%d\n",inet_ntoa(clntAdr.sin_addr),ntohs(clntAdr.sin_port));hThread=(HANDLE)_beginthreadex(NULL,0,RequestHandler,(void*)hClntSock,0,(unsigned *)&dwThreadID);}closesocket(hServSock);WSACleanup();return 0;
}unsigned WINAPI RequestHandler(void *arg){SOCKET hClntSock=(SOCKET)arg;char buf[BUF_SIZE];char method[BUF_SMALL];char ct[BUF_SMALL];char fileName[BUF_SMALL];recv(hclntsock, buf, BUF_SIZE, 0);if(strstr(buf,"HTTP/")==NULL){//查看是否是HTTP提出的請求SendErrorMSG(hClntSock);closesocket(hClntSock);return 1;}strcpy(method,strtok(buf,"/"));if(strcmp(method,"GET"))//我這里只實(shí)現(xiàn)了GET的請求方式SendErrorMSG(hClntSock);strcpy(fileName,strtok(NULL,”/“)); //查看請求文件名strcpy(ct,ContentType(fileName)); //查看Content-typeSendData(hClntSock, ct, fileName); //響應(yīng)return 0;
}void SendData(SOCKET sock, char* ct, char* fileName){char protocol[]="HTTP/1.0 200 OK\r\n";char servName[]="Server:simple web server\r\n";char cntLen[]="Content-length:2048\r\n";char cntType[BUF_SMALL];char buf[BUF_SIZE];FILE* sendFile;sprintf(cntType,"Content-type:%s\r\n\r\n",ct);if((sendFile=fopen(fileName,"r"))==NULL){SendErrorMSG(sock);return;}/*傳輸頭信息*/send(sock, protocol, strlen(protocol),0);send(sock, servName, strlen(servName),0);send(sock, cntLen,strlen(cntLen),0);send(sock, cntType, strlen(cntType), 0);/*傳輸請求數(shù)據(jù)*/while(fgets(buf, BUF_SIZE, sendFile)!=NULL)send(sock,buf, strlen(buf),0);closesocket(sock);//由HTTP協(xié)議響應(yīng)后斷開
}void SendErrorMSG(SOCKET sock){//發(fā)生錯誤時傳遞消息char protocol[]="HTTP/1.0 400 Bad Request\r\n";char servName[]="Server:simple web server\r\n";char cntLen[]="Content-length:2048\r\n";char cntType[]="Content-type:text/html\r\n\r\n";char content[]="<html><head><title>NETWORK</title></head>""<body><font size=+5><br>發(fā)生錯誤!查看請求文件名和請求方式!""</font></body></html>";send(sock, protocol, strlen(protocol),0);send(sock,servName,strlen(servName),0);send(sock, cntLen, strlen(cntLen),0);send(sock, cntType,strlen(cntType),0);send(sock, content,strlen(content),0);closesocket(sock);
}char* ContentType(char* file){ //區(qū)分Content-typechar extension[BUF_SMALL];char fileName[BUF_SMALL];strcpy(fileName, file);strtok(fileName,".");strcpy(extension, strtok(NULL,"."));if(!strcmp(extension, "html")||!strcmp(extension,"htm"))return "text/html";elsereturn "text/plain";
}void ErrorHandling(char* message){fputs(message, stderr);fputc('\n', stderr);exit(1);
}
大家可以通過瀏覽器來訪問一下自己的HTTP服務(wù)器,嘗試一下結(jié)果。
實(shí)現(xiàn)基于 Linux的多線程Web服務(wù)器端
Linux下的Web服務(wù)器端與上述示例不同,將使用標(biāo)準(zhǔn)I/O函數(shù)。在此列出的目的主要是為了
讓各位多復(fù)習(xí)各種知識點(diǎn),沒有任何特殊含義。
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<arpa/inet.h>
#include<sys/socket.h>
#include <pthread.h>#define BUF_SIZE 1024
#define SMALL_BUF 100void* request_handler(void* arg);
void send_data(FILE* fp, char* ct, char* file_name);
char* content_type(char* file);
void send_error(FILE* fp);
void error_handling(char* message);int main(int argc, char *argv[]){int serv_sock, clnt_sock;struct sockaddr_in serv_adr, clnt_adr;int clnt_adr_size;char buf[BUF_SIZE];pthread_t t_id;if(argc!=2){printf("Usage :%s <port>\n", argv[0]);exit(1);}serv_sock=socket(PF_INET, SOCK_STREAM, 0);memset(&serv_adr, 0, sizeof(serv_adr));serv_adr.sin_family=AF_INET;serv_adr.sin_addr.s_addr=htonl(INADDR_ANY);serv_adr.sin_port = htons(atoi(argv[1]));if(bind(serv_sock,(struct sockaddr*)&serv_adr, sizeof(serv_adr))==-1)error_handling("bind() error");if(listen(serv_sock,20)==-1)error_handling("listen() error");while(1){clnt_adr_size=sizeof(clnt_adr);clnt_sock=accept(serv_sock, (struct sockaddr*)&clnt_adr, &clnt_adr_size);printf("Connection Request : %s:%d\n",inet_ntoa(clnt_adr.sin_addr),ntohs(clnt_adr.sin_port));pthread_create(&t_id, NULL, request_handler, &clnt_sock);pthread_detach(t_id);}close(serv_sock);return 0;
}void* request_handler(void *arg){int clnt_sock=*((int*)arg);char req_line[SMALL_BUF];FILE* clnt_read;FILE* clnt_write;char method[10];char ct[15];char file _name[30];clnt_read=fdopen(clnt_sock,"r");clnt_write=fdopen(dup(clnt_sock),"w");fgets(reg_line, SMALL_BUF, clnt_read);if(strstr(req_line, "HTTP/")==NULL){send_error(clnt_write);fclose(clnt_read);fclose(clnt_write);return;}strcpy(method, strtok(req_line,"/"));if(strcmp(method,"GET")!=0){//判斷是否為實(shí)現(xiàn)的GEI方法,如果不是則結(jié)束連接send_error(clnt_write);fclose(clnt_read);fclose(clnt_write);return;}strcpy(file_name, strtok(NULL," /"));strcpy(ct,content_type(file_name));fclose(clnt_read);//完成讀,半關(guān)閉send_data(clnt_write, ct, file_name);
}void send_data(FILE* fp, char* ct, char* file _name){char protocol[]="HTTP/1.0 200 oK\r\n";char server[]="Server:Linux web Server \r\n";char cnt_len[]="Content-length:2048\r\n";char cnt_type[SMALL_BUF];char buf[BUF_SIZE];FILE* send_file;sprintf(cnt_type, "Content-type:%s\r\n\r\n",ct);send_file=fopen(file_name,"r");if(send_file==NULL){send_error(fp);return;}/*傳輸頭信息*/fputs(protocol, fp);fputs(server,fp);fputs(cnt_len, fp);fputs(cnt_type, fp);/*傳輸請求數(shù)據(jù)*/while(fgets(buf, BUF_SIZE,send_file)!=NULL){fputs(buf,fp);fflush(fp);}fflush(fp);fclose(fp);
}char* content_type(char* file){char extension[SMALL_BUF];char file_name[SMALL_BUF];strcpy(file_name, file);strtok(file_name,".");strcpy(extension,strtok(NULL,"."));if(!strcmp(extension,"html")||!strcmp(extension,"htm"))return "text/html";elsereturn"text/plain";
}void send_error(FILE* fp){char protocol[]="HTTP/1.0 400 Bad Request\r\n";char server[]="Server:Linux Web Server Ir\n";char cnt_len[]="Content-length:2048\r\n";char cnt_type[]="Content-type:text/html\r\n\r\n";char content[]="<html><head><title>NETWORK</title></head>""<body><font size=+5><br>發(fā)生錯誤!查看請求文件名和請求方式!""</font></body></html>";fputs(protocol,fp);fputs(server,fp);fputs(cnt_len, fp);fputs(cnt_type,fp);fflush(fp);
}void error_handling(char* message){fputs(message, stderr);fputc('\n', stderr);exit(1);
}
以上就是網(wǎng)絡(luò)編程的所有知識,大家也可以選擇繼續(xù)深入學(xué)習(xí)。
?