## 準備 * 請使用 Linux 作業系統 (練習用的程式需要用到 Linux 的函式庫) * 打開終端機,輸入以下指令下載程式碼 ```shell git clone https://github.com/LuisHsu/cgi-for-string-course.git ``` --- # 字串處理、檔案操作 ## + ### 用C語言實作網站伺服器程式 #### 徐朝逸 LuisHsu --- <div> <i class="fa fa-volume-up" style="font-size:4em;"></i> &nbsp;&nbsp; <i style="font-size:4em;">abc</i>&nbsp;&nbsp; <i class="fa fa-picture-o" style="font-size:4em;"></i> </div> <i class="fa fa-arrow-down" style="font-size:2em;"></i> ## 010100110101101011101 * Data (資料):可以被電腦處理或儲存的資訊 * Bit (位元):足夠儲存 2 個數值的空間單位 ---- ### 01011==01000000001000000000000000000000==101011 <i class="fa fa-arrow-right" ></i> Object ### sign : 1, exponont : 2-8, mantissa : 9 - 23 <i class="fa fa-arrow-right" ></i> float Type ### 2.5 <i class="fa fa-arrow-right" ></i> value * Type (型別):決定一段資料表示何種意義所做的定義 * Value (數值):一段資料內容的精確意義 * Object (物件):可以表示數值的記憶體區段 ---- <table style="border:solid;border-collapse:collapse;"> <tr> <td style="border:solid;">object</td><td style="border:solid;">object</td><td style="border:solid;">object</td> </tr> </table> <i class="fa fa-arrow-right" ></i> Array <div> 0x86 <b style="border:solid"> &nbsp;object&nbsp; </b> </div> <br> <b style="border:solid"> &nbsp;object (value: 0x86)&nbsp; </b> <i class="fa fa-arrow-right" ></i> Pointer * Array(陣列):一段儲存特定物件型別的連續記憶體 * Pointer(指標):一個物件,儲存的數值可以參考到其他物件或函式 --- ## 字串定義 <table style="border:solid;border-collapse:collapse;"> <tr> <td style="border:solid;">'H'</td><td style="border:solid;">'e'</td><td style="border:solid;">'l'</td><td style="border:solid;">'l'</td><td style="border:solid;">'o'</td><td style="border:solid;">'\0'</td> </tr> </table> * __string(字串):型別是字元的陣列__ * __以'\0'結尾__ ---- ## 字串表示方式 1. String literal: ```c "aaa" ``` 2. Pointer to character ```c char *str ``` 3. Array of character ```c char str[5] ``` --- ## 字串操作 **記得要包含 string.h** ```c #include <string.h> ``` ---- ## 字串輸入/輸出 * sprintf ```c int printf(const char *format, ...); int sprintf(char *str, const char *format, ...); ``` * sscanf ```c int scanf(const char *format, ...); int sscanf(const char *str, const char *format, ...); ``` **scanf後面的參數是記憶體位置** ---- ```strlen``` : 字串長度 **從頭開始算到'\0'** ```c strlen("abc"); // -> 3 strlen("ss\0d"); // -> 2 ``` ---- ```strcat``` : 連接字串 ```c strcat("aa", "cd"); // -> "aacd" ``` ---- ```strcpy``` : 複製字串 ```c char str[5]; strcpy(str, "cd"); // -> str = "cd" ``` ---- ```strcmp``` : 比較字串 **按照字典的順序排** ```c strcmp("aa", "cd"); // -> -1 strcmp("ea", "cd"); // -> 1 strcmp("cd", "cd"); // -> 0 ``` --- ## 練習 1 1. 編譯 calltest.c 2. 在瀏覽器輸入 127.0.0.1:8000 3. 觀察結果 4. 要結束請輸入 stop,不要直接 Ctrl+C ---- ### HTTP 通訊協定 - 請求 ``` GET /index.html HTTP/1.1 Host: 127.0.0.1:8000 User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:50.0) Gecko/20100101 Firefox/50.0 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Accept-Language: en-US,en;q=0.5 Accept-Encoding: gzip, deflate Cookie: csrftoken=SOi59mFGomvEpnWboba2p5UUINP7CteALOr8PiOIi0NIOqk0GXUSbfEXAvdjv96U; loginstate=false; connect.sid=s%3AbIuobeQrrd9R4Pd2SyrQURJfO2qKdqSk.8R%2FR4903MPqeIsoy2qGvfA36%2FvtgMxQ2KISD04pjDuU Connection: keep-alive Upgrade-Insecure-Requests: 1 ``` ---- * 用 \r\n 換行 * 第1行: 請求指令 * GET : GET 操作 * /index.html : index.html 檔案名稱 * HTTP/1.1 : http 1.1 版通訊協定 * 第2行 - 空白行: 標頭 * 格式: ``` 屬性名稱: 屬性內容 ``` * 主機名稱、cookie、語言...等等其他資訊 * 空白行 - 結尾空白行: 內容 ---- ## 真。練習 1. 用 fgets 取得字串 tmp ```c= char tmp[1024]; fgets(tmp, 1024, stdin); ``` 2. tmp 是 HTTP 請求的第1行,請把操作、檔案名稱、版本分開成3行 ![](https://i.imgur.com/MhE7upQ.png) --- 3. 編譯 route.c,用 route 檢查你的程式 ![](https://i.imgur.com/ACGf03e.png) --- ## 檔案操作 FILE 指標 ```c FILE *f; ``` 一種特殊的指標,表示某個檔案 **檔案在程式裡的"化身"** * stdin : 標準輸入 * stdout : 標準輸出 * stderr : 標準錯誤輸出 ---- ### fopen 開啟檔案 ```c++ FILE *fopen(const char *path, const char *mode); ``` **回傳FILE指標** ```c= FILE *f; f = fopen("test.txt", "r"); ``` ---- * r (read) : 讀取,檔案一定要存在 * w (write) : 寫入 * 檔案不存在的話會建立檔案 * 會把舊檔案蓋掉 * a (append) : 擴展,檔案不存在的話會建立檔案 * r+ : 從頭開始讀寫,檔案一定要存在 * w+ : 從頭開始讀寫 * 檔案不存在的話會建立檔案 * 會把檔案蓋掉 * a+ : 從 **尾端** 開始讀寫,檔案不存在的話會建立檔案 ---- ### fclose 關閉檔案 ```c++ int fclose(FILE *stream); ``` ```c= FILE *f; fclose(f) ``` ---- ### remove 刪除檔案 ```c++ int remove(const char *pathname); ``` ```c= remove("test.txt"); ``` ---- ### 檔案輸入/輸出 * fprintf ```c++ int fprintf(FILE *stream, const char *format, ...); ``` ```c= FILE *f = fopen("test.txt", "w"); fprintf(f, "Hello"); ``` * fscanf ```c++ int fscanf(FILE *stream, const char *format, ...); ``` 判斷檔尾是用 EOF ---- * fgets ```c char *fgets(char *s, int size, FILE *stream); ``` 判斷檔尾是用 NULL ---- ### fseek 改變檔案讀寫的位置 ```c++ int fseek(FILE *stream, long offset, int whence); ``` ##### whence 基準點 * SEEK_SET 檔案開頭 * SEEK_CUR 目前位置 * SEEK_END 檔案結尾 --- ## 練習 2 1. 寫一個程式,用 printf 輸出以下內容: ```c++= HTTP/1.1 200 OK Content-Type: text/html Hello! ``` * 換行請用 \r\n * 中間和最後面的空白行一定要有 2. 編譯 cgiserver.c,用 cgiserver 開啟你的程式 ``` ./cgiserver ./test ``` * 把 test 換成你的程式名字,至於你的程式名字...就叫 taki 或 mitsuha 吧! ---- 3. 在瀏覽器輸入 127.0.0.1:8000,確定網頁會顯示 Hello! ![](https://i.imgur.com/WLbbEzb.png) 4. 要結束一樣請輸入 stop,不要直接 Ctrl+C ---- ### HTTP 通訊協定 - 回應 ``` HTTP/1.1 200 OK Content-Type: text/html <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>Hello</title> </head> <body> <h1>Hello! CGI</h1> </body> </html> ``` ---- * 第1行 * HTTP/1.1 : 版本 * 200 : 狀態碼 * OK : 狀態訊息 * 第2行 - 空白行: 標頭 * 格式: ``` 屬性名稱: 屬性內容 ``` * 主機名稱、cookie、語言...等等其他資訊 * 空白行 - 結尾空白行: 內容 * 瀏覽器會看到的東西 ---- ## Oo卍煞氣a練習卍oO * 用 真。練習 的方法取得 HTTP 請求的檔案名稱 * 把 練習 2 Hello! 的地方,換成 HTTP 請求檔案的檔案內容 * 就是讀檔之後輸出到螢幕上! * 換行一樣要改成 \r\n * 用 cgiserver 開啟你的程式 ---- * 在瀏覽器輸入 127.0.0.1:8000/index.html,網頁會顯示 Hello! CGI ![](https://i.imgur.com/wqGHG1H.png) * 要結束還是請輸入 stop,不要直接 Ctrl+C * 完成的人可以輸入 127.0.0.1:8000/egg.html 看看