2017年5月5日 星期五

[python] dpkt 處理 pcap 檔函式庫

最近因為需要了解 Tensorflow 在進行分散式學習的過程中,權重更新的頻率,

因此想透過網路封包交換資訊的大小、頻率來摸索出分散式學習的運作機制

藉此來從系統的角度來熟悉 Parameter server 的運作機制

看過了這篇 pcap python library?

依序試過了 pycapfile, pyshark, scapy 這幾個讀pcap檔的工具

pypcapfile 則是因為官方資訊不夠完全,無法讀檔,立刻放棄

pyshark 則是因為還不夠成熟

scapy 在讀pcap檔上,因為一次把資料讀取進來的方式讓讀檔速度過慢,決定放棄

因此最後決定嘗試 dpkt 這款外部函式庫( 官方 Document 在此 )


dpkt


恰如它所說的 fast, simple packet creation / parsing, with definitions for the basic TCP/IP protocols

讀檔

讀檔的部份使用的語法是 dpkt.pcap.Reader()

f = open('test.pcap')  # 開檔
pcap = dpkt.pcap.Reader(f) # 讀檔


IPv4 or IPv6

如何知道IPv4 or IPv6 ?

透過下面的loop就可以知道版本號,如果 ip.v 回傳4,就是IPv4,若是6,則是IPv6

for buf in pcap:
     # Unpack the Ethernet frame (mac src/dst, ethertype)
     eth = dpkt.ethernet.Ethernet(buf)
     ip = eth.data
     if( ip.v == 4):
        print 'IPv4'
     elif( ip.v == 6):
        print 'IPv6'

另外,如果只是單純要取得IP資訊,也可以寫成

ip = dpkt.ip.IP(data)
ip = ether.data
ip_addr = socket.inet_ntoa(ip.src|ip.dst)


更多程式 API 的細節可以參考這份文檔:  My documentation on dpkt

網路知識補充

什麼是 socket?[4]
Sockets allow communication between two different processes on the same or different machines.
To be more precise, it's a way to talk to other computers using standard Unix file descriptors[5]. 
A file descriptor is just an integer associated with an open file and it can be a network connection, a text file, a terminal, or something else. 
而網路這邊所用的 socket 屬於Stream Sockets,並且遵守 TCP(Transmission Control Protocol) 規範

參考範例

1. dpkt Tutorial #2: Parsing a PCAP File

2. Source code for examples.print_packets

3. TCP/IP

4. What is a Socket?

5. 檔案描述符

2017年5月4日 星期四

[Linux] 利用 shell script 自動餵待測程式測試資料的方法

利用 shell script 自動餵待測程式測試資料的方法

拖了一個禮拜才著手進行撰寫

我想利用 Linux shell 自動測試的這篇文章可以延伸出非常多有趣的主題

經過 培任 的推薦

決定利用資料流控制 <  和 txt 檔來作為自動餵入測試資料的工具



自動餵入測試資料指令方法


bash shell script 程式設計

方法1

有一種方法如下,在 cmd 裡面先輸入要執行的程式的指令

配合輸出資料重新導向的 < 操作符號(output redirection operator)

後面接上要輸入的測試資料檔案,就像下方的程式碼[1]

./程式執行檔  <  測試資料.txt 

(若在檔案資料夾下,可以不加 ./ )

方法2


利用 cat  讀取資料[2][4],再利用 pipe 指令 |  把 前面指令的 stdout 資料轉到|後面指令的 stdin裡
cat 測試資料 | ./程式執行檔

(若在檔案資料夾下,可以不加 ./ )

以上兩個指令,程式就會自動把測試資料輸入,並且自動執行,並把輸出結果列印在cmd裡


說明

. / 的意思[3]

這邊的 . 是指現在所在的資料夾路徑位置,兩個點 .. 代表上一層的父目錄

/ 就是指資料夾,兩個合在一起就變成現在這個資料夾底

例如 ./ file1 就是執行目前所在資料夾底下的 file 1

cat 指令


cat 指令是Linxu中相當常用的指令,它含有以下幾個功能

1. 讀檔

純讀檔

在 cmd 中輸入

cat file1

並按下Enter後,就會以全文字模式(all tex mode)印出檔案內容

讀檔並把資料寫入別的檔案

cat file1  file2

將 file 1 的內容讀出來,並寫入file 2

2. 連鎖(同時以字串顯示內容)

同時以全文字方式顯示多個檔案的內容

cat file1 file2 ... filen

當然也可以多個檔案開起來之後寫入同一份檔案中
cat file1 file2 file3 > file4

I/O Redirection 

>  or  >> ?

關於輸出重新導向的操作符號,> 不是一個便當吃不夠不會吃兩個的問題

如果在cmd裡面輸入

ls > ls.txt
是把 ls 的內容覆蓋過去ls.txt 原本的內容並且寫入進去

那如果再加入一個 >

ls >> ls.txt
則是保留原始ls.txt

並把 ls 的內容,寫入到 ls.txt 原始檔案內容之後


個人心得

之前不知道Linux環境下可以用這種方式輸入測試資料

還傻傻的一筆一筆輸入測資

有時候不小心手誤,輸入錯誤還需要暫停程式並且重新輸入 

回頭仔細想來,當下貪圖一時的方便其實並沒有節省多少時間

 反而在未來替自己挖了一個消耗時間的大坑還不自知 

知識就是力量這句格言在今天的情境下完美的體現出來


參考資料 

1.  bash shell script 程式設計

2.  The cat Command

3. What does “./” mean in linux shell?

4. 13 Basic Cat Command Examples in Linux

5. I/O Redirection

6. 輸入/輸出重導向(I/O Redirection)

系統工具- ftrace

ftrace - Function Tracer document

KernelShark - trace-cmd的前端讀取工具

2017年4月8日 星期六

[C語言] I/O 常見錯誤 : scanf() 篇

scanf() 常見問題


結論: 別再使用 scanf( ),請改用或是fgets( ) 或是 getline( ),再去作字串分割。麻煩,但是你清楚輸入字串裡面有什麼

上述說法可以參考: Scanf and loops

奇怪現象


在寫演算法作業優化的時候

發現一件事情

就是我在使用scanf() 來決定我要輸入幾筆測資

while loop 它竟然會 直接跳過 下面要求我輸入字串的指令

read = getline(&amp;line, &amp;len, stdin);

而直接輸出結果

然後輸出的字串是沒有顯示在螢幕上,但是字串長度是1,結果如下兩行所表示


line =
line length = 1

以下是範例程式  (2017/4/10 已修正亂碼錯誤)

//------------------- example code --------------------
#include <stdio.h>
#include <string.h>

int main(){ 
    int i = 0;
    int p;
    int dummy = 0;

    scanf("%d",&dummy);
    while( read != -1){
        //readline
        puts("enter a line");
        read = getline(&line, &len, stdin); 
        printf("line = %s", line);
        printf("line length = %zu\n", read); //%zd z coresponding to size_t type variable
        puts(line);

        if(i == dummy-1){ 
            printf("dummy = %d\n",dummy);
            break;
        }
        i++;
    }
    return 0;
}
//------------------- example code --------------------

後來在paslab的大家給予專業建議 外加 茫茫的stackoverflow的問題海中找到了答案

背後原因


Scanf skips every other while loop in C
When you read keyboard input with scanf(), the input is read after enter is pressed but the newline generated by the enter key which is not consumed by the call to scanf(). 
That means the next time you read from standard input there will be a newline waiting for you (which will make the next scanf() call return instantly with no data).

上面提到一件重要的事情,雖然 scanf("%d",&var1) 這行

會抓取標準輸入(standard input, 縮寫為stdin)的正整數來指定給變數 var1

如下面所說明的:
The standard I/O library provides a simple and efficient buffered stream I/O interface.
At program start-up, three streams shall be predefined and need not be opened explicitly: standard input (for reading conventional input), ...
--- STDIO(3) Linux Programmer's Manual

但是按下 Enter 鍵所產生的換行符號卻會留在緩衝區內(buffer)

所以 getline() 中負責抓取字元的 pointer

在緩衝區抓到的殘留字元就是按下 Enter鍵所輸入進的換行字元 

因此就產生換行字元直接餵給 getline(),執行時直接跳過的現象

這邊有鄉民驗證 ASCII code 的值為10,對應的按鍵不意外的就是換行的Enter鍵


還沒有概念? 可以看下面的圖解說明

圖解說明


如果用圖片來說明的話,以下圖片引用自 linux C标准库IO缓冲区--行缓冲实现 图解

如下圖一所示,當兩個指標 _IO_read_ptr  還沒與 _IO_read_end 重合時

表示緩衝區內還有內容可以讀取


圖一

後續的輸入會直接從緩存區裡面抓


圖二

之後兩者指向同一個位置的時候,才會停止自動抓取

解決方案


在不使用 getline()、fgets() 的情況下

要解決這個問題有兩個方法,但是概念一樣都是清空緩衝區

1. 在scanf() 後面加入一行 getchar() 把換行字元抓出來到其他地方

(注意,方法2 非正式規範!)

2.  用 fflush 清空輸入 input 的緩衝區( 即stdin的緩衝區)
 
一行可以處理  fflush(stdin);

補充知識


fflush 錯誤觀念澄清  : 正式規範是定義給  stdout 使用,不是 stdin 

fflush: flush a stream
In cases where a large amount of computation is done after printing part of a line on an output terminal, it is necessary to fflush(3) the standard output before going off and computing so that the output will appear.

觀念澄清1

 Q: How can I flush pending input so that a user's typeahead isn't read at the next prompt? Will fflush(stdin) work?

A: fflush is defined only for output streams

觀念澄清2

关于fflush(stdin)清空输入缓存流(C/C++) 新手必看!!
也许有人会说:“居然这样,那么在 scanf 函数后面加上‘fflush(stdin);’,把输入缓冲清空掉不就行了?” 然而这是错的! C和C++的标准里从来没有定义过 fflush(stdin)。 也许有人会说:“可是我用 fflush(stdin) 解决了这个问题,你怎么能说是错的呢?” 的确,某些编译器(如VC6)支持用 fflush(stdin) 来清空输入缓冲, 但是并非所有编译器都要支持这个功能(linux 下的 gcc 就不支持), 因为标准中根本没有定义 fflush(stdin)。

關於fflush的使用

Q: If fflush won't work, what can I use to flush input?


關於記憶體 - 緩衝區(buffer),資料是存在電腦的RAM (Random Access Memory) 中


關於換行符號

What is the newline character in the C language: \r or \n?

心得


從上次的演算法作業一學到一件事情,就是程式永遠都會按照目前編譯規範執行

如果發生預期以外的輸出結果,通常是因為疏忽使用規則而造成意外的結果

所以建議往後撰寫完時,務必一段一段的測試,並且有意外情況的時候,先看C使用手冊

The GNU C Reference Manual

再發問

---------------------------------------------
ps. 以上說明文件屬於 C89 規範

2017年4月6日 星期四

[C語言] 指標(pointer)觀念複習

指標,相當於告訴我們某個變數它的住址在哪,而該變數就住在標有那個住址的記憶體空間

(在 指標與記憶體位址 中提及 :變數(Variable)提供具名稱的記憶體儲存空間,(儲存資訊

包含)一個變數關聯(的)一個資料型態、儲存的值與儲存空間的位址值。所以我們常見的變數

其實也是有包含儲存位址的資訊)

常用符號


符號&,叫作取址運算子(Address-of operator),它可以用來取得變數所在的"記憶體位址"

符號*  ,叫作取值運算子(Dereference operator),它可以用來取得變數儲存的"數值"

宣告方式


根據指標的宣告方式,不同的資料型別的指標,宣告方式有些微不同,像是:

int *ptr1;  or int* ptr;
float *ptr2;
char *ptr3;

備註:這邊的*號是指宣告一個指標變數,*靠左靠右都可以,但是

int* ptr1, ptr2;

只有ptr1是指標變數,ptr2是整數變數。若都要宣告,則可以使用

int *ptr1,*ptr2;


使用細節


不像我們可以直接對變數進行操作,因為指標儲存的資訊是變數的位址資訊

如果我們想透過指標對變數進行操作,我們必須透過別的方式才行

這時就要提到指標的另一個特性:Dereference 提取

透過 * (dereference operator) 這個取值運算子,可以用來提取變數資訊

例如

int *ptr1 = &var1;

意思是,原本 ptr 這個指標變數所存的是變數 var1 的位址

*ptr


在前面加了*符號,則能夠將var1的數值給取出來


指標與陣列


觀念澄清: char a[] 跟 char *a 是一樣的嗎??? 


答案: 是完全不同! 指標跟陣列雖然有相似的地方,但是本質上是不一樣的東西

char a[] = "hello";
char *p = "world";

一張圖勝過千言萬語,看下面的圖就能快速明白兩者的差別




Q: But I heard that char a[] was identical to char *a.

Q: If they're so different, then why are array and pointer declarations interchangeable as function formal parameters?


指標與字串


演算法作業2需要同時處理指標與字串

來紀錄一下實作的細節部份

前面是getline()所需要使用的基本參數

char *line = NULL; //string pointer
size_t len = 0;    //unsigned
ssize_t read = 0;  //signed

line 儲存的是 line[] 的起始位址

這邊則是由 getline() 將字串讀入

read = getline(&line, &len, stdin);

完整的程式碼

//---2017/4/6  #c getline() 使用方法---
#include <string.h>

char *line = NULL; //string pointer
size_t len = 0;    //unsigned
ssize_t read = 0;  //signed

int main(){ 

    read = getline(&line, &len, stdin); 
    while( read != -1){
        for(int i=0;i<read-1;i++){
     printf("line+%d = %c\n",i,*(line+i));  //一個個的印出line指標指到的字串字元
        };
    }
free(line); //釋放記憶體
}

如果需要一個個的把字元指針 line 所指的字串字元給印出來,這邊需要這樣寫

printf("line+%d = %c\n",i,*(line+i));

這邊的

*(line+i)

取值運算子*取出的是字元指針 line 指向第 i 個元素位址儲存的字元

printf() 的format specifier 需要選用c,寫成c code就變成

%c

如果是%s,它會直接印出一整行,而且後面需要放的是指向字串的指針 line

不是*(line+i)

可以參考這篇 Can a pointer to a string be used in a printf?

底下也有鄉民提到%s接受的格式是字元指針(char *)形式

The "%s" format specifier for printf always expects a char* argument.


segmentation fault(core dump)

如果這樣寫

printf("line+%d = %s\n",i,*(line+i));

gcc編譯過的話,執行到這行時會得到錯誤資訊 segmentation fault(core dump)

原因是因為,%s這邊想要讀取的資料格式是資料的位址

所以我們這邊要放入的變數應該是字元指針變數line

可是今天我們輸入的資料是另一個數值*(line+i),當作字串位址丟進去

程式會訪問到其他不該訪問的程式記憶體位置

於是就發生了segmentation fault(core dump)

----------------------------------------------------------------------------
思考題 Q: *line+(i-1) vs *( line+(i-1) ) 差別在哪?

2017年4月4日 星期二

[ORID 筆記法] 演算法作業2_找出最長迴文_C語言實作議題

Objective 客觀來說

繼上次只能夠處理演算法改良,這次時間上稍微充裕一些,多花一些時間研究I/O 和 pointer 的議題。

I/O 的部份:
認識到新的I/O的分別是:
輸入:比 scanf()更快的 getline() (API 說明文件: getline(3) - Linux man page
輸出:比 printf()更快的 putchar()

Pointer的部份:
將指標傳入遞迴函式的時候,發生錯誤怎辦?
解決方法:  c passing pointer to recursive function

怎麽直接讀取一行: getline()的部份:
How to read a line from the console in C?

注意:
getline()
ssize_t getline(char **lineptr, size_t *n, FILE *stream);

文件中提到
If *lineptr is NULL, then getline() will allocate a buffer for storing the line, which should be freed by the user program. (In this case, the value in *n is ignored.)
也就是說放入 getline() 的 *lineptr 為空的情況底下,getline()會自動幫程式動態配置一個緩存的空間

Alternatively, before calling getline(), *lineptr can contain a pointer to a malloc(3)-allocated buffer *n bytes in size. If the buffer is not large enough to hold the line, getline() resizes it with realloc(3), updating *lineptr and *n as necessary.
如果緩存不夠大,有必要的話,getline()會使用 realloc() 來重新分配緩存資源

最後一項 FILE *stream 是 input file handle

速度上的比較可以參考這篇 Fastest way to output a 2D array of char in C
Elapsed times are: 
0:09.46 with printf,
0:07.75 with putchar,
0:05.06 with putchar_unlocked.

Reflective 感受

你要如何形容今天的情绪:
自己獨立完成作業並通過,比起上次提交多次還不過的心情完全不同


Interpretive 解釋反思

發現上次可以改進的地方:
1. 由於上次過於想要一次就把架構在腦袋中實現,但是程式實做能力還跟不上,加上時間壓力讓自己的恐懼感壓垮理智。恐慌的時候讓我嚴重降低思考判斷能力。像是YEF的一階淘汰、分手失戀的時候那種腦袋瞬間當機的感覺,我想自己痛到骨髓的那種感覺,比別人都清楚

這次改採用一個步驟一個步驟的測試,每個步驟的輸出都完全正確之後,再前進到下一步,雖然笨,但是有效。

額外發現,再思考問題的時候,有注意到系統一(詳參快思慢想)潛意識的思維幫助我檢查了一些問題,並告訴我答案,當下我的系統二並沒有四思考,覺得有點特別,想要讓自己的系統二能夠跟上系統一個思考速度(另一個時刻是閱讀的時候,突然感覺自己的理解力視窗突然放大,但是幾秒候又回復平常的速度)。

2. 上次忘記補上程式碼的解說,這次有記得,避免疏忽冤枉失分(莫非定律,"以為"不會是大問題,結果真的發生的慘痛經歷)

3.  太急著想要完成一件需要深入思考的任務,往往都是失敗收場。所以,再解決一個問題以及嘗試實做的過程,耐著性子把說明文件、網路上的資料吸收,基礎觀念搞懂(ex pointer ),下次就不用還要重新理解一次


Decisional 修正行動

有哪些工作需要明天继续努力:
將基本功 I/O、pointer、array這些打磨清楚,比起這次將作業做出來,但有些實作細節還停留在知其然而不知其所以然的階段。

這個階段如果已經搞清楚,有助於降低日後的恐慌感


/* 載入prettify的autoloader */ /* 載入JQuery */