handleAccept( buf[] = { (buf), ); stringstream sstream; sstream <<>>>><< cmd << << filename << (cmd=== filename.substr(, filename.length() - head = ; (!<< << (filename.find()!=::npos|| filename.find()!== (filename.find()!== (filename.find()!== (!file.eof()) buf[, (buf)- n =
1 recv(socket_fd, buf, sizeof(buf), 0)和send(socket_fd, buf,n,0);
recv用於接收從客戶端發送來的消息,send用於向服務端發送消息
recv/send函數原型如下
int recv(SOCKET s,char FAR * buf,int len,int flags)/int send(SOCKET s,const char FAR * buf,int len,int flags);
第一個參數表示代表對方的socket,
第二個參數為接收讀取的信息的字符串
第三個參數為該字符串的大小
第四個參數可以用來控制讀寫操作
該值可以為一下幾種
0
MSG_DONTROUTE:不查找路由表/* send without using routing tables */
MSG_OOB:接受或發送帶外數據 /* process out-of-band data */
MSG_PEEK:查看數據,並不從系統緩沖區移走數據/* peek at incoming message */
MSG_WAITALL :等待任何數據/* do not complete until packet is completely filled */
etc…
解釋:
MSG_DONTROUTE:是send函數使用的標志.這個標志告訴IP協議.目的主機在本地網絡上面,沒有必要查找路由表.這個標志一般用網絡診斷和路由程式裡面。
MSG_OOB:表示能夠接收和發送帶外的數據.關於帶外數據我們以後會解釋的.
MSG_PEEK:是recv函數的使用標志,表示只是從系統緩沖區中讀取內容,而不清除系統緩沖區的內容。這樣下次讀的時候,仍然是相同的內容。一般在有多個進程讀寫數據時能夠使用這個標志。
MSG_WAITALL:是recv函數的使用標志,表示等到任何的信息到達時才返回。使用這個標志的時候recv會一直阻塞,直到指定的條件滿足,或是發生了錯誤。
1)當讀到了指定的字節時,函數正常返回,返回值等於len
2)當讀到了文檔的結尾時,函數正常返回.返回值小於len
3)當操作發生錯誤時,返回-1,且配置錯誤為相應的錯誤號(errno)
其他的幾個選項,但是我們實際上用的很少.
關於其他的發送和接收函數
recvfrom/sendto
這兩個函數一般用在UDP中。
函數原型如下
int recvfrom(SOCKET s,char FAR * buf,int len,int flags,struct sockaddr FAR * from,int FAR * fromlen);
int sendto(SOCKET s,const char FAR * buf,int len,int flags,const struct sockaddr FAR * to,int tolen);
2 stringstream sstream;
字符串讀寫流,這裡用於將char buf[1024]的數據按默認的間隔符讀到cmd和filename中,關於cmd和filename的具體含義見3
也可以用來將數字轉換為字符串
例如
stringstream sstream;
sstream <<123456789;
string a;
sstream >> a;
cout << a << endl;
將數字123456789轉換為了字符串
3 HTTP協議請求
當客戶端連接到服務端時,會發出一個http請求
http請求由三部分組成,分別是:請求行、消息報頭、請求正文
這裡只對請求行進行介紹
請求行以一個方法符號開頭,以空格分開,後面跟著請求的URI和協議的版本,格式如下:Method Request-URI HTTP-Version CRLF
其中 Method表示請求方法;Request-URI是一個統一資源標識符;HTTP-Version表示請求的HTTP協議版本;CRLF表示回車和換行(除了作為結尾的CRLF外,不允許出現單獨的CR或LF字符)。
請求方法(所有方法全為大寫)有多種,各個方法的解釋如下:
GET 請求獲取Request-URI所標識的資源
POST 在Request-URI所標識的資源後附加新的數據
HEAD 請求獲取由Request-URI所標識的資源的響應消息報頭
PUT 請求服務器存儲一個資源,並用Request-URI作為其標識
DELETE 請求服務器刪除Request-URI所標識的資源
TRACE 請求服務器回送收到的請求信息,主要用於測試或診斷
CONNECT 保留將來使用
OPTIONS 請求查詢服務器的性能,或者查詢與資源相關的選項和需求
在本程序中只對GET請求進行處理,將請求方法讀入cmd中,和Request-URL讀入filename中,判斷是否為GET,並獲請求的資源名稱
4 file.open(filename ,ifstream::binary);
打開客戶所請求的文件,這裡使用二進制的方式打開是為了方便對圖片進行傳輸
5 string head = "HTTP/1.0 200 OK\r\nContent - type:text/plain\r\n\r\n";
在接收和解釋請求消息後,服務器返回一個HTTP響應消息。
HTTP響應也是由三個部分組成,分別是:狀態行、消息報頭、響應正文
1)、狀態行格式如下:
HTTP-Version Status-Code Reason-Phrase CRLF
其中,HTTP-Version表示服務器HTTP協議的版本;Status-Code表示服務器發回的響應狀態代碼;Reason-Phrase表示狀態代碼的文本描述。
常見狀態代碼、狀態描述、說明:
200 OK //客戶端請求成功
400 Bad Request //客戶端請求有語法錯誤,不能被服務器所理解
401 Unauthorized //請求未經授權,這個狀態代碼必須和WWW-Authenticate報頭域一起使用
403 Forbidden //服務器收到請求,但是拒絕提供服務
404 Not Found //請求資源不存在,eg:輸入了錯誤的URL
500 Internal Server Error //服務器發生不可預期的錯誤
503 Server Unavailable //服務器當前不能處理客戶端的請求,一段時間後可能恢復正常
Content-Type表示正在傳輸的類型
Content - type:text/plain:普通文本
Content - type:text/html:html網頁
Content - type:image/png:png圖片
Content - type:image/jpg:jpg圖片
2)響應報頭允許服務器傳遞不能放在狀態行中的附加響應信息,以及關於服務器的信息和對Request-URI所標識的資源進行下一步訪問的信息。在這不做詳細介紹
3)響應正文就是服務器返回的資源的內容
關於http協議更詳細的部分可以參照http://blog.csdn.net/gueter/article/details/1524447 HTTP協議詳解
6 獲得響應正文並寫到客戶端
char buf[1024];
memset(buf, 0, sizeof(buf));//初始化
file.read(buf,sizeof(buf)-1);//由於我們使用二進制的方式打開的文件所以使用了read方法
int n = file.gcount();//gcount()返回最後一個非格式化的抽取方法讀取的字符數,因為有時候讀到的會小於1023個
send(socket_fd, buf,n,0);//將buf中的n個字符發送到客戶端
7
file.close()和closesocket(socket_fd)
關閉打開的文件和socket_fd
程序代碼下載:http://files.cnblogs.com/magicsoar/WebServer.rar
p.s
程序做的還不是很完善,對一些情況比如send,reve是否正在執行也沒有進行檢查,客戶端請求的文件不存在也沒有返回404,
我會在以後對程序進行完善的,並將一些新的心得寫出來。
第一次寫博客,希望大家能指出我的不足來,我會虛心接受並改進的。
接下來可能會將自己大一大二做的軟件,游戲拿出來,寫一寫,與大家分享,或者是讀書的心得等,也可能會是我目前正在學習C++網絡爬蟲。