2018年3月8日 星期四

3 進階技術-封裝資料&廣播封包&fast-forward

  • 廣播封包


用 UDP[只能用 UDP,TCP 不行]與標準 IPv4,可以透過一種叫作廣播(broadcasting)的機制達成。IPv6 不支援廣播,所以你必須要採用比較高級的技術-群播(multicasting).

如何指定廣播訊息的目地位址呢?

有兩種常見的方法:

1. 將資料送給子網路(subnet)的廣播位址,就是將 subnet's network(子網路網段)的 host(主機)那部分全部填 1,舉例來說,我家裡的網路是 192.168.1.0,而我的 netmask(網路遮罩)是 255.255.255.0,所以位址的最後一個 byte 就是我的 host number[因為依據 netmask,前三個 bytes 是 network number]。所以我的廣播位址就是 192.168.1.255。

2. 將資料送給 "global(全域的)"廣播位址,255.255.255.255,又稱為 INADDR_BROADCAST,很多機器會自動將它與你的 network number 進行 AND 位元運算,以轉換為網路廣播位址,但是有些機器不會這樣做。Routers 不會將這類的廣播封包轉送(forward)出你的區域網路。


使用廣播封包一定要小心,因為 LAN 上面的每台電腦都會被迫處理這類封包,無論它們有沒有用 recvfrom() 接收,這類封包會造成整個電腦網路相當大的負擔,所以一定要謹慎、適當地使用廣播。


  • 封裝資料

不過如果你想要送一些 "二進制" 的資料,非文字資料, 下面常用三種方法:

1. 將數字轉換為文字,使用如 sprintf() 的函式,接著傳送文字。接收者會使用如 strtol() 函式解析文字,並轉換為數字。

2. 直接以原始資料傳送,將指向資料的指標傳遞給 send()

3. 將數字編碼(encode)為可移植的二進制格式,接收者會將它解碼(decode)。


第二個方法:傳送原始資料(raw data),這個方法相當簡單[但是危險!]:只要將資料指標提供給 send(),不具可移植性
第三種方法為封裝資料,




  • socket struct 傳送(struct 用指標指向開始位址 傳送)(公司header)



c++ struct 中如果有 char 變數: 4 表示4bit
socket 在傳送時,如果非1byte 送出, 透過上述的struct 有猜4bit 4 bit 

則可能會因為little ending 掉換過來

例如v3 header

typedef struct Image_header_V3 

 unsigned int nStartCode;   //FFFFFFAA
 time_t      tSec;            //時間
 unsigned int tuSec;
 unsigned int nLen;    //Frame 的資料長度
 unsigned long long nRec_ch;   //紀錄錄影當時的頻道
 unsigned short  nChA;          //Channel number
 unsigned short  nChB:4;          // For Video:Reaolution, For Audio:Audio Type
 unsigned short  nFrameType:2;   // 0: MPEG4 , 1: H264 , 2: ALL_AUDIO , 3: MJPEG
 unsigned short  nKeyframe:1;     // 0: P-Frame , 1: I-Frame
 unsigned short  nVideosystem:1; // 0: NTSC , 1: PAL

 unsigned short  Capture:1;   // 0: Analog , 1: IPCAM
 unsigned short  nRes:1;           // 不使用,預設為 0
 unsigned short  field:2;   // 不使用,預設為 3
 unsigned short  Reserver:4;

}__attribute__((packed)) Image_header_V3;

------------------------------------------------

int time_t int int (long long) short short
4      4        4      4         8            2        2     //占的位子大小(單位btye)

最後那個short為2byte,包含
 nchB , nFrameType,nKeyframe,nVideosystem, //總和1byte
Capture,nRes,field,Reserver   //總和1byte

以上是送
---------------------------------------
以下是收

如到因為little ending  所以會反過來,
如果為getshort(26)//26為起始位置

收到順序Reserver,field,nRes,Capture,
nVideosystem,nKeyframe,nFrameType:2,nchB:4

所以: nFrameType 取為& 00 30 
//30表示 0011(nVideosystem,nKeyframe,nFrameType:2
0000(nchB)


----------------------------------------------
-----------------------------------------------
typedef struct Image_Header_V2 {
    unsigned int  nStartCode;        //FFFFFFAA
    /*time_t*/ unsigned int  tSec;   //時間 unsigned int for 64-bit issue
    //unsigned long long tSec;     //時間 unsigned int for 64-bit issue
    unsigned int    tuSec;
    unsigned int    nLen;            //Frame 的資料長度
    unsigned int    nRec_ch;         //
    unsigned short  nChA;            //Channel number
    unsigned short  nChB:4;          // For Video:Reaolution, For Audio:Audio Type
    unsigned short  nFrameType:2;    // 0: MPEG4 , 1: H264 , 2: ALL_AUDIO , 3: MJPEG
    unsigned short  nKeyframe:1;     // 0: P-Frame , 1: I-Frame
    unsigned short  nVideosystem:1;  // 0: NTSC , 1: PAL
    
    unsigned short  Capture:1;       // 0: Analog , 1: IPCAM
    unsigned short  nRes:1;          // 
    unsigned short  field:2;         //
    unsigned short  Reserver:4;
    
} Image_Header_V2;


  • H264 decode fast-forward



屬於多媒體播放,已知有兩種方法
1.  尋找 I frame  decode (影像不順, cpu不耗效能)

2.  1秒鐘內decode 多張不同timestamp (cpu耗效能,影像順)

注意聲音&影像同步問題

2017年10月6日 星期五

5 RTSP

  • 實時流協定(Real Time Streaming Protocol,RTSP


下列為交握步驟

 OPTIONS
 DESCRIBE
 ANNOUNCE (ignore)
 SETUP
 PLAY
 PAUSE (ignore)
 TEARDOWN
 GET_PARAMETER
 SET_PARAMETER
 REDIRECT (ignore)
 RECORD (ignore)







OPTIONS 請求
OPTIONS請求返回server 支援的method (上面那些步驟)。

C -> S :
OPTIONS rtsp://192.168.0.23:554/live.sdp RTSP/1.0
CSeq: 1 User-Agent: HUNT

S -> C :
      RTSP/1.0 200 OK
CSeq: 1
Date: Mon, 6 Mar 2000 1:16:50 GMT
Public: OPTIONS, DESCRIBE, PLAY, SETUP, GET_PARAMETER, SET_PARAMETER, TEARDOWN


DESCRIBE 請求
      該回覆包括呈現描述,通常以對談描述協定(SDP)格式SDP 訊息包含 Session description、Time description、 Media description,主要需要得知Media description內的video , audio 編碼方式以及其他資訊(如h264 的 sps,pps等)


SDP 格式可參考 : Albert 筆記


C -> S :
DESCRIBE rtsp://192.168.0.23:554/live.sdp RTSP/1.0
CSeq: 2
User-Agent: HUNT
S -> C :
RTSP/1.0 200 OK
CSeq: 2
Date: Mon, 6 Mar 2000 1:16:50 GMT
Content-Base: rtsp://192.168.0.23/live.sdp/
Content-Type: application/sdp
Content-Length: 461

 o=RTSP 952305410 424 IN IP4 0.0.0.0
s=RTSP server
c=IN IP4 0.0.0.0
t=0 0
a=charset:Shift_JIS
a=range:npt=0-
a=control:*
a=etag:1234567890
m=video 0 RTP/AVP 98  //m表示media為video ,RTP傳輸 
b=AS:0
a=rtpmap:98 H264/90000 //H264為Decode , 90000: timestamp 單位
a=control:trackID=1  //為SETUP method url為 trackID=1 
a=fmtp:\
98 packetization-mode=1;\
profile-level-id=4d0028;\
sprop-parameter-sets=Z00AKNoB4AiflQ==,aO48gA== 
       //以本例(H264)而言
       //主要使用sps, pps來記錄
       //sps為 Z00AKNoB4AiflQ==
       //Pps為 aO48gA==

m=audio 0 RTP/AVP 97 b=AS:13 //m表示media為audio ,RTP傳輸 
a=rtpmap:97 AMR/8000
a=control:trackID=6
a=maxptime:200
a=fmtp:97 decode_buf=400;octet-align=1


SETUP 請求

SETUP請求指定如何傳輸單個媒體流。這必須在傳送PLAY請求之前完成。

C -> S : 
SETUP rtsp://192.168.0.23/live.sdp/trackID=6 
RTSP/1.0 Transport: RTP/AVP/TCP;unicast;interleaved=2-3
CSeq: 3 
User-Agent: HUNT 
  
S -> C :
RTSP/1.0 200 OK
CSeq: 3
Date: Mon, 6 Mar 2000 1:16:50 GMT
Session: 182264145;timeout=80

Transport: RTP/AVP/TCP;interleaved=2-3;unicast;mode=play





Play 播放請求
Play 播放請求 將導致播放一個或所有媒體流。可以通過傳送多個播放請求來堆疊播放請求。
      在此指定撥放時間, 我們的case是不斷收stream, 因此 npt-0.000-  (不限時間)
      可以指定範圍。如果沒有指定範圍,流將從頭開始播放,並播放到最後,或者如果流暫停,則在暫停點恢復播放。

C -> S :
PLAY rtsp://192.168.0.23/live.sdp/ RTSP/1.0
Range: npt=0.000-
CSeq: 4
User-Agent: HUNT
Session: 182264145

S -> C :
RTSP/1.0 200 OK
CSeq: 4
Date: Mon, 6 Mar 2000 1:16:50 GMT
Session: 182264145;timeout=80
RTP-Info: url=rtsp://192.168.0.23/live.sdp/trackID=6;seq=0;rtptime=0





TEARDOWN 停止發布流請求
TEARDOWN 請求用於終止對談。它停止所有媒體流,並釋放所有與對談相關的資料在伺服器上。


C->S: TEARDOWN rtsp://example.com/media.mp4 RTSP/1.0
      CSeq: 8
      Session: 12345678

S->C: RTSP/1.0 200 OK
      CSeq: 8

面試:

Timestamp 用途?


open path:

rtsp://195.200.199.8/mpeg4/media.amp


參考:
https://zh.wikipedia.org/wiki/%E5%8D%B3%E6%99%82%E4%B8%B2%E6%B5%81%E5%8D%94%E5%AE%9A

2017年10月3日 星期二

3+1 H.264 基礎

https://goo.gl/nsHhUc
https://www.ptt.cc/bbs/C_and_CPP/M.1332759347.A.4D0.html
來源 : 工研院


  • H264基本

1. 前言


且H.264/AVC也首次將視訊編碼層(Video Coding Layer,VCL)與網路提取層(Network Abstraction Layer,NAL)的概念涵蓋進來,H.264/AVC根據使用的編碼工具種類來提供三種編碼規模(Profile),如表1所示分別為Baseline Profile、Main Profile、Extension Profile,而相對應的影片尺寸與位元率等級由Level 1至Level 5.1,涵蓋小畫面與高解析度畫面的應用範圍。Baseline Profile主要是著眼於低位元率的應用(例如:影像通訊),而且其運算複雜度低,所以也適合應用於個人隨身的多媒體撥放機;Main Profile因為有支援交錯式影片(interlaced content)的編碼,所以適合應用於HDTV數位電視廣播,而且非常容易整合在傳統的MPEG-2 Transport/Program Stream上來傳送H.264/AVC位元流;Extension Profile是對於IP-TV或是MOD (Multimedia On Demand)等應用,使用包含高抗錯性編碼工具(error resilient tools).


  • 總結: 

1 .Baseline Profile :  只使用 I-Frame 與 P-Frame 的 CAVLC 編碼
CAVLC (Context-Adaptive Variable-Length Coding) 上下文自適應可變長度編碼
(簡單說:p 為前一張的動態資訊圖,非完整圖片)

2.Main  Profile:
相較於Baseline類型,加入交錯式(interlaced)影像(B-Frame)、及CABAC等工具.

3. High  Profile:
High類型提高Main類型使用位階的上限,放寬採用8×8的DCT轉換,並提供較好的量化步階調整模式。High類型已經逐漸取代Main類型.

公司只用過base.


[H264 Encoder]



2. 網路提取層 (Network Abstraction Layer,NAL)

亦即以NAL封包為單位的方式來做為VCL編解碼的運算單位,這樣傳輸層拿到NAL封包之後不需要再進行切割,只需附加該傳輸協定的檔頭資訊(adding header only)就可以交由底層傳送出去,如圖1所示,可以將NAL當成是一個專作封裝(packaging)的模組,用來將VCL壓縮過的bitstream(NAL unit stream)封裝成適當大小的封包單位(NAL-unit),並在NAL-unit Header中的NAL-unit Type欄位記載此封包的型式,每種型式分別對應到VCL中不同的編解碼工具。





一個完整的H.264/AVC bitstream是由多個NAL-units所組成的,所以此bitstream也稱之為NAL unit stream,一個NAL unit stream內可以包含多個壓縮視訊序列(coded video sequence)一個單獨的(coded video sequence)代表一部視訊影片,而壓縮視訊序列又是由多個access units所組成,當接收端收到一個access unit後,可以完整地解碼成單張的畫面,而每個壓縮視訊序列的第一個access unit必須為Instantaneous Decoding Refresh (IDR) access unit,IDR access unit的內容全是採用intra-prediction編碼,所以自己本身即可完全解碼,不用參考其他access unit的資料。access unit亦是由多個NAL-units所組成,標準中總共規範12種的NAL-unit型式,這些可以進一步分類成VCL NAL-unit及non-VCL NAL-unit,所謂的VCL NAL-unit純粹是壓縮影像的內容,而所謂的non-VCL NAL-unit則有兩種:Parameter Sets與Supplemental Enhancement Information (SEI),SEI可以存放影片簡介、版權宣告、使用者自行定義的資料…等;Parameter Sets主要是描述整個壓縮視訊序列的參數,例如:長寬比例、影像顯現的時間點(timestamp)、相關解碼所需的參數…等,以往像MPEG-2/-4都把這些資訊放在一般的packet header,所以很容易隨著packet loss而消失,現在H.264/AVC將這些資訊獨立出來成為特殊的parameter set,可以採用所謂的out-of-band的方式來傳送,以便將out-of-band channel用最高層級的通道編碼(channel coding)保護機制,來保證傳輸的正確性





3. 視訊編碼層 (Video Coding Layer,VCL)


如圖1所示,H.264/AVC的視訊編碼機制是以圖塊(block-based)為基礎單元,也就是說先將整張影像分割成許多矩形的小區域,稱之為巨圖塊(macroblock,MB),再將這些巨圖塊進行編碼,先使用畫面內預測(intra-prediction)與畫面間預測(inter-prediction)技術,以去除影像之間的相似性來得到所謂的差餘影像(residual),再將差餘影像施以空間轉換(transform)與量化(quantize)來去除視覺冗餘,最後視訊編碼層會輸出編碼過的位元流(bitstream),之後再包裝成網路提取層的單元封包(NAL-unit),經由網路傳送到遠端或儲存在儲存媒體中。
H.264/AVC允許視訊影片以frame或是以filed的方式來進行編碼,兩者可以共存,而frame可以是progress或是interlace形式,對同一段影片來說也可使用兩者來混合編碼,這個特性與MPEG-2相同。而在影像色彩格式的支援上,H.264/AVC第一版的標準只支援YCrCb 4:2:0 (又稱YUV420)取樣的方式,而在增修的第二版標準中增加4:2:2與4:4:4取樣格式,通常這些格式會被數位電影或HDTV影片所採用。



3.1 H.264/AVC影像格式階層架構
H.264/AVC的階層架構由小到大依序是sub-block、block、macroblock(MB)、slice、slice group、frame/field-picture、sequence。


對一個採用4:2:0取樣的MB而言,它是由16x16點的Luma與相對應的2個8x8點Chroma來組成,而在H.264/AVC的規範中,MB可再分割成多個16x8、8x16、8x8、8x4、4x8、4x4格式的sub-blocks。所謂的slice是許多MB的集合,而一張影像是由許多slice所組成(圖3),slice為H.264/AVC格式中的最小可解碼單位(self-decodable unit),也就是說一個slice單靠本身的壓縮資料就能解碼,而不必依靠其他slice,這樣的好處是當傳送到遠端時,每接收完一筆slice的壓縮資料就能馬上解碼,不用等待整張的資料接收完後才能開始,而且萬一傳送的過程中發生資料遺失或錯誤,也只是影響該筆slice,不會對其他slice有所影響,但跟MPEG-2的slice不同處在於它允許slice的範圍可以超過一行MB,也就是說H.264/AVC允許整張影像只由單一個slice組成。H.264/AVC的slice架構還有一項特性稱為Flexible Macroblock Ordering (FMO),也就是說組成slice的MB可以不必侷限於循序掃描(raster scan)的排列方式,例如:圖3最右側的排法就非常適用於多個前景(foreground) slice groups與一個獨自的背景(background) slice group,好處是對不同的slice group可以用不同品質的壓縮參數,例如:對於前景物件通常是人眼較感興趣的區域,可以用較小的壓縮率來維持較好的品質。

3.2 Slice的編碼模式

H.264/AVC的slice依照編碼的類型可以分成下列種類:(1) I-slice:(2) P-slice:(3) B-slice: 與P-slice類.
H.264/AVC另外增加兩種特殊slice類型:(1) SP-slice: 即所謂的Switching P slice,為P-slice的一種特殊類型,用來串接兩個不同bitrate的bitstream;(2) SI-slice: 即所謂的Switching I slice,為I-slice的一種特殊類型,除了用來串接兩個不同content的bitstream外,也可用來執行隨機存取(random access)來達到網路VCR的功能。
這兩種特殊的slice主要是為了讓相同content但不同bitrate的bitstream可以較平順地串接,對同一個視訊內容的影片來說,server會預先存放不同bitrate的壓縮影片,而當頻寬改變時,server就會送出適合當時頻寬位元率的影片,傳統的做法是需要等到適當的時間點來傳送新的I-slice (容量較P-slice大上許多),但因為頻寬變小導致需要較多的時間來傳送I-slice,如此會讓client端的影像有所延遲,使用SP-slice會很容易來達成.





--------------------------------------------------------------------------

由下圖得知NAL Unit 組成 Access Unit 組成 RTP Packet(coded video sequence?)

[RTP Packet]

NAL Unit
一張會動畫面被H.264分成需多NALU(NAL Unit)
每個NAL Unit如下圖, 圖片來源: Exploring H.264. Part 2: H.264 Bitstream format

[NAL Structure]



  • Single NAL Unit Packet
nal unit type為1-23的padload格式如下 (即在rtp padload第12byte開始)[Single NAL Unit Packet]

nal header
ex. 0x67 (0110 0111)
b[0]: forbidden_zero_bit = 0,
b[1-2]: nal_ref_idc = 3,
b[3-7]: nal_unit_type = 7


根據RFC3984以RTP 封裝H.264 raw data來作video streaming.

1.H.264 raw data
以00 00 01 或 00 00 00 01作為開頭(Start Code),接著是8 bit NALU 
NALU的format


#define NALU_TYPE_SLICE 1( P-Frame?)
#define NALU_TYPE_IDR 5 (I-frame)
#define NALU_TYPE_SEI 6
#define NALU_TYPE_SPS 7
#define NALU_TYPE_PPS 8

正常的H264 NALU順序:
SPS(NAL type = 0x07) -> PPS(0x8) -> I-Frame(0X5), P-Frame(0x1)
把[SPS,PPS,IDR(即IFRAME)]當成一個Frame送到decoder


p.s. RTP?NALU定義?
我們header of rtp header?

參考來源:
https://erwinchang.github.io/2017/03/14/h264/
深入淺出

2020/01/16 新增 知呼(來源)

*1 frame 可以有1~多個slice. (每個slice可放於NAL包中)

*公司中使用 I frame , P frame(依賴於I frame) ; (沒用B frame)(依賴於I,P,B frame 前後倒快轉用)

網絡抽象層單元類型 (NALU)

NALU header 由1byte組成;  F ( forbidden_zero_bit)(1bit) ,NRI ( nal_ref_idc)(2 bit), type ( nal_unit_type)(5bit)=>用於區分SPS(0x67) ;PPS(0x68);IDR_SLICE(0x65);SLICE(0x41)

forbidden_​​zero_bit. 在 H.264 規範中規定了這一位必須為 0.
nal_ref_idc. 取 00 ~ 11, 似乎指示這個 NALU 的重要性. 00表示NALU解碼器可以丟棄.


+---------------+
|0|1|2|3|4|5|6|7|
+-+-+-+-+-+-+-+-+
|F|NRI| Type  |
+---------------+

如果一個H.264的NALU是這樣的:

[00 00 00 01 67 42 A0 1E 23 56 0E 2F ...]

這是一個序列參數集 NAL 單元. [00 00 00 01] 是四個字節的開始碼, 0x67 是 NALU header, 0x42 開始的數據是 NALU 內容.

封裝成 RTP 包將如下:

[ RTP Header ] [ 67 42 A0 1E 23 56 0E 2F ]

即只要去掉 4 個字節的開始碼就可以了.



  • 2017 11/15=> ffmpeg會破圖

可用Elecard解析frame

已前攝影機有 I-720p  or  I-1080p
所以I,P frame 來可能為 intersect(交錯送)

海思decode 分為frame mode & stream mode

正常frame mode I , p , p , p.....這樣收frame
所以餵進去decode 沒問題

但是stream(intersect) mode可能是I(半張),I(half),p(half#1),p(half#1),p(half#2),p(half#2)..... 
半張,半張,或一部分一部分 分開送
所以收入會破圖, 以前頻寬不夠,所以採此做法


2017年9月30日 星期六

2 進階技術 select

7.1. Blocking(阻塞)


很多函式都會 block,accept() 會 block,全部的 recv() 函式都會 block。
socket() 建立 socket descriptor 時,kernel(核心)會將它設定為 blocking。若你不想要 blocking socket,你必須呼叫 fcntl().

#include <unistd.h> #include <fcntl.h> sockfd = socket(PF_INET, SOCK_STREAM, 0); fcntl(sockfd, F_SETFL, O_NONBLOCK);
如果你試著讀取 non-blocking socket,而 socket 沒有資料時,函式就不會發生 block,而是傳回 -1,並將 errno 設定為 EWOULDBLOCK。



7.2. select():同步 I/O 多工


select() 授予你同時監視多個 sockets 的權力,它會告訴你哪些 sockets 已經有資料可以讀取、哪些 sockets 已經可以寫入,如果你真的想知道,還可以告訴你哪些 sockets 觸發了例外。

即使 select() 有相當好的可移植性,不過卻是最慢的監視 sockets 方法。一個比較可行的替代方案是 libevent ,將全部的系統相依要素封裝起來,用在取得 socket 的通知。


#include <sys/select.h>

int select(int n, fd_set *readfds, fd_set *writefds, fd_set *exceptfds,
           struct timeval *timeout);

FD_SET(int fd, fd_set *set);
FD_CLR(int fd, fd_set *set);
FD_ISSET(int fd, fd_set *set);
FD_ZERO(fd_set *set);


第一個參數 n 的值是最大的那個 socket descriptor 數值在加上 1。

struct timeval 可以設定 timeout 的時間, 如果timeout為0則一個一個檢查不stop

struct timeval {
  int tv_sec; // 秒(second)
  int tv_usec; // 微秒(microseconds)
};




p.s. 怎決定放readfds or writefds or exceptfds



  • poll vs select


??

  • Socket Option 設定

修改socket 設定技術



int getsockopt(int s, int level, int optname, void *optval,
               socklen_t *optlen);
int setsockopt(int s, int level, int optname, const void *optval,
               socklen_t optlen);



level通常為SOL_SOCKET
optname 可以有很多,細看socket man page , 如修改receive buffer , SO_REVBUF

int rcvBufferSize; 
int sockOptSize = sizeof(rcvBufferSize); 

// Retrieve and print the default buffer size 
if (getsockopt(sock, SOL_SOCKET, SO_RCVBUF, &rcvBufferSize, &sockOptSize) < 0)       DieWithSystemMessage("getsockopt() failed"); 
     printf("Initial Receive Buffer Size: %d\n", rcvBufferSize);

// Double the buffer size rcvBufferSize *= 2;

 if (setsockopt(sock, SOL_SOCKET, SO_RCVBUF, &rcvBufferSize, sizeof(rcvBufferSize)) < 0) 
     DieWithSystemMessage("setsockopt() failed");


receive buffer default=>/proc/sys/net/core/rmem_default
min size=> doubled (256)
max size=>/proc/sys/net/core/rmem_max 






2017年8月7日 星期一

1 基本介紹及參考資料

參考:
http://kezeodsnx.pixnet.net/blog/post/27462696-socket-programming-%E7%AD%86%E8%A8%98
http://beej-zhtw.netdpi.net/05-system-call-or-bust/5-2-socket--get-file-descriptor
http://www.tenouk.com/cnlinuxsockettutorials.html


TCP&UDP 基本區別

TCP
優點:
可靠性、順序性

缺點:
建立連線 速度比較慢。

tcp 可靠性確保=>

UDP
優點:
傳輸速度快

缺點:
不可靠 可能資料lost 看網卡buffer

TCP:

UDP



基本指令


  • socket:


int socket(int domain, int type, int protocol);

domain所以這裡只會用到 IPv4 的 PF_INET 及 IPv6 的 PF_INET6。
type可靠 TCP socket 的 SOCK_STREAM(send()recv()
不可靠快速 UDP socket 之 SOCK_DGRAM(sendto()recvfrom()
[另一個有趣的 socket type 是 SOCK_RAW,這個可以用來手動建構封包表頭]
protocolprotocol 參數指定在特定的 socket type 要用的 protocol(通訊協定),正如我所說的,比如:SOCK_STREAM 使用 TCP。在使用 SOCK_STREAM 或 SOCK_DGRAM 時,你可以直接將 protocol 設定為zero,這樣它會自動使用適合的 protocol,要不然你可以用 getprotobyname() 查詢適合的協定編號(protocol number)。


struct addrinfo 
{
       int              ai_flags;
       int              ai_family;
       int              ai_socktype;
       int              ai_protocol;
       socklen_t        ai_addrlen;
       struct sockaddr *ai_addr;
       char            *ai_canonname;
       struct addrinfo *ai_next;
};




参数
取值
说明
ai_family
AF_INET
2
IPv4
AF_INET6
23
IPv6
AF_UNSPEC
0
协议无关
ai_protocol
IPPROTO_IP
0
IP协议
IPPROTO_IPV4
4
IPv4
IPPROTO_IPV6
41
IPv6
IPPROTO_UDP
17
UDP
IPPROTO_TCP
6
TCP
ai_socktype
SOCK_STREAM
1
SOCK_DGRAM
2
ai_flags
AI_PASSIVE
1
被动的,用于bind,通常用于server socket
AI_CANONNAME
2
用于返回主机的规范名称
  
AI_NUMERICHOST
4
地址为数字串



程式碼:
getaddrinfo(NULL, "3490", &hints, &res);

sockfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);

getaddrinfo=> 如www.google.com =>172.217.27.132-443

source path=>google drive/Book CS/Socket/SocketGetaddrinfo
https://drive.google.com/file/d/1WB1L05am7DGPxLLxHhYW-Nwptg60p4zI/view?usp=sharing

  • Bind


int bind(int socketfd, struct sockaddr *my_addr, socklen_t addrlen);


struct sockaddr是通用的套接字地址,而struct sockaddr_in则是internet环境下套接字的地址形式,二者长度一样,都是16个字节。
二者是并列结构,指向sockaddr_in结构的指针也可以指向sockaddr。一般情况下,需要把sockaddr_in结构强制转换成sockaddr结构再传入系统调用函数中。

return:
On success, zero is returned. On error, -1 is returned

程式碼:

bind(sockfd, res->ai_addr, res->ai_addrlen);

p.s. 不要bind same port

  • Listen


int listen(int socketfd, int backlog);

參數 backlog 代表在 kernel 開始拒絕新連線以前,你可以有多少的連線在等待。所以當新的連線進入時,你應該盡快的用 accept() 來處理,讓 backlog 不會滿出來。

return:

On success, zero is returned. On error, -1 is returned

程式碼:

listen(sockfd, 10);

  • Accept

int accept(int socketfd, struct sockaddr *addr, socklen_t *addrlen);

原本用來 listen 的 socket 仍然還是會留著,當有新連線進來時,一樣是用 accept() call 來接受新的連線。

addr這裡會填入連線到server這裡的 client 位址。
addrlen這裡會填入 addr 參數中傳回的資料結構大小。

程式碼:

struct sockaddr_storage their_addr;
int addr_size=sizeof(struct sockaddr_in);
int  new_fd = accept(sockfd, (struct sockaddr *)&their_addr, (socklen_t*)&addr_size);

return:
On success, these system calls return a file descriptor for the accepted socket. On error, -1 is returned,

  • Connect

int connect(int sockfd, const struct sockaddr *serv_addr, socklen_t addrlen);


填入 serv_addr , 要連接的server



return:

On success, zero is returned. On error, -1 is returned

  • recv

ssize_t recv(int s, void *buf, size_t len, int flags);
ssize_t recvfrom(int s, void *buf, size_t len, int flags,
                 struct sockaddr *from, socklen_t *fromlen);

TCP SOCK_STREAM socket 使用 recv() 、UDP SOCK_DGRAM socket 使用 recvfrom()。


recv(sockfd, buf, len, flags);
is equivalent to
recvfrom(sockfd, buf, len, flags, NULL, NULL);


flags is zero 表示 不包含任何flag


p.s. recv vs read

  • send

ssize_t send(int s, const void *buf, size_t len, int flags);
ssize_t sendto(int s, const void *buf, size_t len,
               int flags, const struct sockaddr *to,
               socklen_t tolen);

send() 用在需連線的 TCP SOCK_STREAM socket,而 sendto() 用在免連線的 UDP SOCK_DGRAM socket。



flags is zero 表示 不包含任何flag

p.s. send vs write



flag可參考:
http://www.mit.edu/afs.new/athena/system/i386_deb50/os-ubuntu-9.04/usr/include/bits/socket.h


  • recv vs read

With a zero flags argument, recv() is generally equivalent to read().
只有在datagram 時 ,收到長度為0的pending,recv 會drop,read會保留.




  • send vs write

write():
On success, the number of bytes written are returned (zero indicates nothing was written). On error, -1 is returned, and errno is set appropriately. If count is zero and the file descriptor refers to a regular file, 0 will be returned without causing any other effect. For a special file, the results are not portable.
send():
The calls return the number of characters sent, or -1 if an error occurred.

因為send 可能長度太長失敗
If the message is too long to pass atomically through the underlying protocol, 
the error EMSGSIZE is returned, and the message is not transmitted.

https://stackoverflow.com/questions/9048959/write-and-send-solving-errors-difference


*另外注意

"recv byte & 次數"  與  "send &次數" 不一定對等舉例,
send 3000byte 一次, recv buffer 調5000byte (也並非一次一定收到3000byte)
send 100byte 3次 , recv 可能一次收到300byte.