c_socket.io_server筆記之處理靜態(tài)文件
緣由
在編
有關(guān)libev的文章,中文資料不多,英文資料也不多。這里推薦三篇:
-
-
- 官方文檔
這里把在編寫c_socket.io_server
程中使用libev的一些地方做些筆記,記錄下來,也方便以后查閱。
預(yù)備知識
所有代碼的編寫、編譯、測試和運行等,都在Ubuntu下進行,另外實例嚴重依賴libev和
其它依賴,可以從
處理靜態(tài)文件
這里設(shè)計一個靜態(tài)文件WEB服務(wù)器,非常簡單,僅僅滿足socket.io服務(wù)器最基本的需求,因此別苛求太多。但比網(wǎng)上很多大把類似文章多了一點寫入管道時緩沖區(qū)已滿問題的處理。
這里簡單說一下處理靜態(tài)文件的思路。
計算靜態(tài)文件路徑以及擴展名和內(nèi)容類型
char file_path[200];
sprintf(file_path, "%s%s", static_folder, url_str);
獲取文件內(nèi)容和以及優(yōu)先輸出響應(yīng)頭部
char file_path[200];
sprintf(file_path, "%s%s", static_folder, url_str);
int file = open(file_path, O_RDONLY);
struct stat info;
if (fstat(file, &info) == -1) {
fprintf(stderr, "the file %s is NULL\n", file_path);
write(client->fd, RESPONSE_404, strlen(RESPONSE_404));
close(file);
free_res(loop, client);
return 0;
}
char file_ext[50];
get_extension(file_path, file_ext);
char content_type[50];
get_content_type(file_ext, content_type);
int file_len = info.st_size;
char head_msg[200] = "";
sprintf(head_msg, RESPONSE_TEMPLATE, content_type, file_len);
write(client->fd, head_msg, strlen(head_msg));
很顯然,這里引入fcntl.h頭部文件,調(diào)用fstat初始化stat結(jié)構(gòu),可判斷文件是否存在,以及文件大小等。
讀取文件內(nèi)容到緩沖區(qū),循環(huán)寫入
int read_count;
int buf_size = 8 * 1024;//8096;
char buffer[buf_size + 1];
while ((read_count = read(file, buffer, buf_size)) > 0) {
int bytes_left = read_count;
char *ptr = buffer;
int need_break = 0;
while (bytes_left > 0) {
ssize_t write_len = write(client->fd, ptr, bytes_left);
if (write_len == -1) {
fprintf(stderr, "write failed(errno = %d): %s\n", errno, strerror(errno));
switch (errno) {
case EAGAIN:
case EINTR:
case EINPROGRESS:
fprintf(stderr, "now sleep 0.2s\n");
ev_sleep(0.2);
break;
default:
need_break = 1;
break;
}
} else if (write_len == 0) {
need_break = 1;
fprintf(stderr, "write_len is zero, and break now\n");
break;
} else if (write_len < bytes_left) {
bytes_left -= write_len;
ptr += write_len;
fprintf(stderr, "write client with something wrong wtih bytes_left = %d & write_len = %d and write the left data !\n", (int)bytes_left, (int)write_len);
} else {
break;
}
}
if (need_break) {
break;
}
}
close(file);
需要注意,構(gòu)造的一個大約8K+1的緩沖區(qū)buffer,不是每次都可以正常完整輸出到socket對端,write輸出不完整,會返回-1,系統(tǒng)返回errno值,在errno = EAGAIN,EINTR,EINPROGRESS時,需要再次將緩沖區(qū)中尚未寫入的剩下數(shù)據(jù)再次寫入到請求端。這樣可以避免常見的半包、包不完整問題。
關(guān)閉socket描述符,當前請求結(jié)束
free_res(loop, client);
請求完成,一定要記得關(guān)閉socket描述符,釋放相應(yīng)資源等。 這樣一個較為完整的HTTP請求,靜態(tài)文件就處理完畢了。
編譯運行
先編譯:
gcc staticserver.c -o staticserver ../include/libev.a ../include/http-parser/http_parser.o -lm
運行之:
./static_server ../static
命令輸入錯誤,如輸入靜態(tài)路徑為空,會報錯的,哈哈:
Error: invald path parmeter
Usage: ./static_server
Example:
./staticserver ../static
./staticserver /home/yongboy/yourstaticfolderEnjoy it~
測試一下吧
curl -i http://192.168.190.150:8000/index.html
在瀏覽器內(nèi),測試一下,支持圖片樣式等,完好顯示。
需要注意,要傳入靜態(tài)文件目錄路徑,相對的路徑,或絕對的路徑,都是可以接受的。 最后,附上完整代碼:
posted on 2013-03-25 16:46 nieyong 閱讀(2155) 評論(0) 編輯 收藏 所屬分類: socket.io