聶永的博客

          記錄工作/學(xué)習(xí)的點點滴滴。

          c_socket.io_server筆記之處理靜態(tài)文件

          緣由

          在編 寫純C語言版socket.io服務(wù)器 時,選擇了libev作為網(wǎng)絡(luò)基礎(chǔ)層代碼,可以離epoll模型遠一些,再說還可以避免單獨使用Epoll,寫出不易維護的多層嵌套代碼,聽說,有時Epoll出現(xiàn)一些“偽信號”小問題,沒有那么空閑精力,繞過之,選擇成熟度非常高的libev好了。
          有關(guān)libev的文章,中文資料不多,英文資料也不多。這里推薦三篇:
          - libev 設(shè)計分析
          - libev ev_io源碼分析
          - 官方文檔http://pod.tst.eu/http://cvs.schmorp.de/libev/ev.pod,更為全面一些,閱讀時可以獲得較總體認知。

          這里把在編寫c_socket.io_server程中使用libev的一些地方做些筆記,記錄下來,也方便以后查閱。

          預(yù)備知識

          所有代碼的編寫、編譯、測試和運行等,都在Ubuntu下進行,另外實例嚴重依賴libev和http-parser HTTP解析庫。
          其它依賴,可以從https://github.com/yongboy/csocket.ioserver處下載。

          處理靜態(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
          ./static
          server /home/yongboy/yourstaticfolder

          Enjoy 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

          公告

          所有文章皆為原創(chuàng),若轉(zhuǎn)載請標明出處,謝謝~

          新浪微博,歡迎關(guān)注:

          導(dǎo)航

          <2013年3月>
          242526272812
          3456789
          10111213141516
          17181920212223
          24252627282930
          31123456

          統(tǒng)計

          常用鏈接

          留言簿(58)

          隨筆分類(130)

          隨筆檔案(151)

          個人收藏

          最新隨筆

          搜索

          最新評論

          閱讀排行榜

          評論排行榜

          主站蜘蛛池模板: 普宁市| 长葛市| 荥阳市| 亚东县| 巧家县| 七台河市| 宁武县| 怀化市| 阳泉市| 潜江市| 台东市| 繁峙县| 文水县| 石棉县| 建德市| 荣成市| 宝丰县| 利津县| 屯昌县| 旬邑县| 三穗县| 云阳县| 普安县| 亳州市| 葫芦岛市| 扶余县| 科技| 柳州市| 安丘市| 鄂伦春自治旗| 武城县| 汾阳市| 双牌县| 普陀区| 湟源县| 丹江口市| 澄江县| 宁武县| 兴化市| 嘉禾县| 宁强县|