聶永的博客

          記錄工作/學習的點點滴滴。

          c_socket.io_server筆記之htmlfile塊傳輸

          關于htmlfile chunked傳輸

          Google天才工程師們使用一個稱為“htmlfile”的 ActiveX 解決了在 IE 中的加載顯示問題,具體是封裝了一個基于 iframe 和 htmlfile 的 JavaScript comet 對象,支持 IE、Mozilla Firefox 瀏覽器,但需要服務器端配合使用。
          稍微熟悉一下有關Transfer-Encoding: chunked的同學,會感覺一點技術含量都沒有。但那是他們的事情,笨鳥先飛,記錄下來,以作備忘。
          我們做一個時間顯示,每隔一秒自動顯示在頁面上。那么做這件事情的順序,就很簡單。

          輸出頭部

          chunked塊傳輸,需要瀏覽器支持,服務器需要提前告訴瀏覽器端:

          #define HTMLFILE_RESPONSE_HEADER \
          "HTTP/1.1 200 OK\r\n" \
          "Connection: keep-alive\r\n" \
          "Content-Type: text/html; charset=utf-8\r\n" \
          "Transfer-Encoding: chunked\r\n" \
          "\r\n"......
          write_ori(client, HTMLFILE_RESPONSE_HEADER);
          

          在socket.io服務器中,數據量不大,傳輸內容無須gzip壓縮,畢竟壓縮算法要耗費一些CPU時間。

          傳輸部分HTML預備內容

          這部分不是必須的,為了調用客戶端javascript方便,可以提前定義好調用函數。

          #define HTMLFILE_RESPONSE_FIRST \
              "<html><head><title>htmlfile chunked example</title><script>var _ = function (msg) { document.getElementById('div').innerHTML = msg; };</script></head><body><div id=\"div\"></div>"......
          char target_message[strlen(HTMLFILE_RESPONSE_FIRST) + 20];
          sprintf(target_message, "%X\r\n%s\r\n", (int)strlen(HTMLFILE_RESPONSE_FIRST), HTMLFILE_RESPONSE_FIRST);
          write_ori(client, target_message);
          

          除了http header頭部輸出,剩下內容的輸出,需要注意輸出的簡單格式:

          具體輸出內容長度16進制數字表示\r\n具體輸出內容\r\n

          2D
          <script>_('now time is 1364040943');</script>

          掌握了格式要求之后,其它的,就沒有什么難點。

          設置定時器,周期性循環

          client->timeout.data = client;
          ev_timer_init(&client->timeout, timeout_cb, 1.0, 1.0);
          ev_timer_start(loop, &client->timeout);
          

          時間觸發函數timeout_cb每一秒會定時觸發:

          static void timeout_cb(EV_P_ struct ev_timer *timer, int revents) {
          if (EV_ERROR & revents) {
              fprintf(stderr, "error event in timer_beat\n");
              return ;
          }
          if (timer == NULL) {
              fprintf(stderr, "the timer is NULL now !\n");
          }
          client_t *client = timer->data;
          if (client == NULL) {
               fprintf(stderr, "Timeout the client is NULL !\n");
              return;
          }
          char target_msg[50];
          snprintf(target_msg, 50, "now time is %d", (int)ev_time());
          write_body(client, target_msg);
          }
          

          OK,基本功能完畢。

          編譯運行

          編譯一下:

          gcc htmlfile.c -o htmlfile ../include/libev.a -lm

          運行它:

          ./htmlfile

          打開瀏覽器,輸入地址 http://192.168.190.150:8080/htmlfile,可以看到時間一點點的流逝,諸如:

          now time is 1364043695

          完整代碼

          #include <arpa/inet.h>
          #include <stdlib.h>
          #include <stdio.h>
          #include <string.h>
          #include <fcntl.h>
          #include <errno.h>
          #include <err.h>
          #include "../include/ev.h"
          #define HTMLFILE_RESPONSE_HEADER \
          "HTTP/1.1 200 OK\r\n" \
          "Connection: keep-alive\r\n" \
          "Content-Type: text/html; charset=utf-8\r\n" \
          "Transfer-Encoding: chunked\r\n" \
          "\r\n"
          #define HTMLFILE_RESPONSE_FIRST \
          "<html><head><title>htmlfile chunked example</title><script>var _ = function (msg) { document.getElementById('div').innerHTML = msg; };</script></head><body><div id=\"div\"></div> "
          #define SERVER_PORT 8080
          struct ev_loop *loop;
          typedef struct {
          int fd;
          ev_io ev_read;
          ev_timer timeout;
          } client_t;
          ev_io ev_accept;
          static void free_res(struct ev_loop *loop, ev_io *ws);
          int setnonblock(int fd) {
          int flags = fcntl(fd, F_GETFL);
          if (flags < 0)
          return flags;
          flags |= O_NONBLOCK;
          if (fcntl(fd, F_SETFL, flags) < 0)
          return -1;
          return 0;
          }
          static int format_message(const char *ori_message, char *target_message) {
          return sprintf(target_message, "%X\r\n<script>_('%s');</script>\r\n", ((int)strlen(ori_message) + 23), ori_message);
          }
          static void write_ori(client_t *client, char *msg) {
          if (client == NULL) {
          fprintf(stderr, "the client is NULL !\n");
          return;
          }
          write(client->fd, msg, strlen(msg));
          }
          static void write_body(client_t *client, char *msg) {
          char body_msg[strlen(msg) + 100];
          format_message(msg, body_msg);
          write_ori(client, body_msg);
          }
          static void timeout_cb(EV_P_ struct ev_timer *timer, int revents) {
          if (EV_ERROR & revents) {
          fprintf(stderr, "error event in timer_beat\n");
          return ;
          }
          if (timer == NULL) {
          fprintf(stderr, "the timer is NULL now !\n");
          }
          client_t *client = timer->data;
          if (client == NULL) {
          fprintf(stderr, "Timeout the client is NULL !\n");
          return;
          }
          char target_msg[50];
          snprintf(target_msg, 50, "now time is %d", (int)ev_time());
          write_body(client, target_msg);
          }
          static void read_cb(struct ev_loop *loop, ev_io *w, int revents) {
          client_t *client = w->data;
          int r = 0;
          char rbuff[1024];
          if (revents & EV_READ) {
          r = read(client->fd, &rbuff, 1024);
          }
          if (EV_ERROR & revents) {
          fprintf(stderr, "error event in read\n");
          free_res(loop, w);
          return ;
          }
          if (r < 0) {
          fprintf(stderr, "read error\n");
          ev_io_stop(EV_A_ w);
          free_res(loop, w);
          return;
          }
          if (r == 0) {
          fprintf(stderr, "client disconnected.\n");
          ev_io_stop(EV_A_ w);
          free_res(loop, w);
          return;
          }
          write_ori(client, HTMLFILE_RESPONSE_HEADER);
          char target_message[strlen(HTMLFILE_RESPONSE_FIRST) + 20];
          sprintf(target_message, "%X\r\n%s\r\n", (int)strlen(HTMLFILE_RESPONSE_FIRST), HTMLFILE_RESPONSE_FIRST);
          write_ori(client, target_message);
          client->timeout.data = client;
          ev_timer_init(&client->timeout, timeout_cb, 1.0, 1.0);
          ev_timer_start(loop, &client->timeout);
          }
          static void accept_cb(struct ev_loop *loop, ev_io *w, int revents) {
          struct sockaddr_in client_addr;
          socklen_t client_len = sizeof(client_addr);
          int client_fd = accept(w->fd, (struct sockaddr *) &client_addr, &client_len);
          if (client_fd == -1) {
          fprintf(stderr, "the client_fd is NULL !\n");
          return;
          }
          client_t *client = malloc(sizeof(client_t));
          client->fd = client_fd;
          if (setnonblock(client->fd) < 0)
          err(1, "failed to set client socket to non-blocking");
          client->ev_read.data = client;
          client->timeout.data = NULL;
          ev_io_init(&client->ev_read, read_cb, client->fd, EV_READ);
          ev_io_start(loop, &client->ev_read);
          }
          int main(int argc, char const *argv[]) {
          loop = ev_default_loop(0);
          struct sockaddr_in listen_addr;
          int reuseaddr_on = 1;
          int listen_fd = socket(AF_INET, SOCK_STREAM, 0);
          if (listen_fd < 0)
          err(1, "listen failed");
          if (setsockopt(listen_fd, SOL_SOCKET, SO_REUSEADDR, &reuseaddr_on, sizeof(reuseaddr_on)) == -1)
          err(1, "setsockopt failed");
          memset(&listen_addr, 0, sizeof(listen_addr));
          listen_addr.sin_family = AF_INET;
          listen_addr.sin_addr.s_addr = INADDR_ANY;
          listen_addr.sin_port = htons(SERVER_PORT);
          if (bind(listen_fd, (struct sockaddr *) &listen_addr, sizeof(listen_addr)) < 0)
          err(1, "bind failed");
          if (listen(listen_fd, 5) < 0)
          err(1, "listen failed");
          if (setnonblock(listen_fd) < 0)
          err(1, "failed to set server socket to non-blocking");
          ev_io_init(&ev_accept, accept_cb, listen_fd, EV_READ);
          ev_io_start(loop, &ev_accept);
          ev_loop(loop, 0);
          return 0;
          }
          static void free_res(struct ev_loop *loop, ev_io *w) {
          client_t *client = w->data;
          if (client == NULL) {
          fprintf(stderr, "the client is NULL !!!!!!");
          return;
          }
          ev_io_stop(loop, &client->ev_read);
          ev_timer *timer = &client->timeout;
          if (timer != NULL && (timer->data != NULL)) {
          ev_timer_stop(loop, timer);
          }
          close(client->fd);
          free(client);
          }
          view raw htmlfile.c hosted with ❤ by GitHub

          posted on 2013-03-28 08:41 nieyong 閱讀(2564) 評論(0)  編輯  收藏 所屬分類: socket.io

          公告

          所有文章皆為原創,若轉載請標明出處,謝謝~

          新浪微博,歡迎關注:

          導航

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

          統計

          常用鏈接

          留言簿(58)

          隨筆分類(130)

          隨筆檔案(151)

          個人收藏

          最新隨筆

          搜索

          最新評論

          閱讀排行榜

          評論排行榜

          主站蜘蛛池模板: 天柱县| 元朗区| 莱州市| 周宁县| 洮南市| 内丘县| 阿巴嘎旗| 奈曼旗| 冷水江市| 南投县| 灵川县| 富宁县| 包头市| 农安县| 扎兰屯市| 香港 | 中卫市| 兴业县| 荥经县| 清涧县| 余庆县| 柳州市| 海宁市| 扬中市| 博客| 垦利县| 桦甸市| 壤塘县| 莆田市| 城固县| 祁阳县| 巴里| 石河子市| 铅山县| 桑日县| 玛纳斯县| 慈溪市| 兴山县| 庄浪县| 平罗县| 太康县|