libevent學習筆記【使用篇】——5.通用函數與可移植函數

翻譯自:http://www.wangafu.net/~nickm/libevent-book/Ref5_evutil.html
原文地址:http://blog.csdn.net/windeal3203/article/details/52775148

頭文件<event2/util.h> 定義了許多能夠幫助我們利用libevent 實現可移植應用程序的函數。libevent會在庫內部使用這些函數。

1 基礎類型

1.1 evutil_socket_t

  在除Windows之外的大多數地方,套接字是個整數,操作系統按照數值次序進行處理。然而,使用Windows套接字API時,socket具有類型SOCKET,它實際上是個類似指針的句柄,收到這個句柄的次序是未定義的。在Windows中,libevent定義evutil_socket_t類型爲整型指針,可以處理socket()或者accept()的輸出,而沒有指針截斷的風險。

#ifdef WIN32
#define evutil_socket_t intptr_t
#else
#define evutil_socket_t int
#endif

這個類型在2.0.1-alpha版本中引入。

1.2 標準整數類型

  落後於21世紀的C系統常常沒有實現C99標準規定的stdint.h頭文件。考慮到這種情況,libevent定義了來自於stdint.h的、位寬度確定(bit-width-specific)的整數類型:
libevent中位寬度確定的整數類型
跟C99標準一樣,這些類型都有明確的位寬度。
這些類型由1.4.0-alpha版本引入。MAX/MIN常量首次出現在2.0.4-alpha版本。

1.3 各種兼容性類型

  在有ssize_t(有符號的size_t)類型的平臺上,ev_ssize_t定義爲ssize_t;而在沒有的平臺上,則定義爲某合理的默認類型。ev_ssize_t類型的最大可能值是EV_SSIZE_MAX;最小可能值是EV_SSIZE_MIN。(在平臺沒有定義SIZE_MAX的時候,size_t類型的最大可能值是EV_SIZE_MAX
  ev_off_t用於代表文件或者內存塊中的偏移量。在有合理off_t類型定義的平臺,它被定義爲off_t;在Windows上則定義爲ev_int64_t
  某些套接字API定義了socklen_t長度類型,有些則沒有定義。在有這個類型定義的平臺中,ev_socklen_t定義爲socklen_t,在沒有的平臺上則定義爲合理的默認類型。
  ev_intptr_t是一個有符號整數類型,足夠容納指針類型而不會產生截斷;而ev_uintptr_t則是相應的無符號類型。
  ev_ssize_t類型由2.0.2-alpha版本加入。ev_socklen_t類型由2.0.3-alpha版本加入。ev_intptr_t與ev_uintptr_t類型,以及EV_SSIZE_MAX/MIN宏定義由2.0.4-alpha版本加入。ev_off_t類型首次出現在2.0.9-rc版本。

2 可移植的定時器函數

  不是每個平臺都定義了標準timeval操作函數,所以libevent也提供了自己的實現。

#define evutil_timeradd(tvp, uvp, vvp) /*  */
#define evutil_timersub(tvp, uvp, vvp) /*  */

這些宏分別對前兩個參數進行加或者減運算,將結果存放到第三個參數中。

#define evutil_timerclear(tvp) /*  */
#define evutil_timerisset(tvp) /*  */

清除timeval會將其值設置爲0。evutil_timerisset宏檢查timeval是否已經設置,如果已經設置爲非零值,返回ture,否則返回false

#define evutil_timercmp(tvp, uvp, cmp)
evutil_timercmp宏比較兩個timeval,如果其關係滿足cmp關係運算符,返回true。比如說,evutil_timercmp(t1,t2,<=)的意思是“是否t1<=t2?”。注意:與某些操作系統版本不同的是,libevent的時間比較支持所有C關係運算符(也就是<、>、==、!=、<=和>=)

int evutil_gettimeofday(struct timeval *tv, struct timezone *tz);
evutil_gettimeofdy()函數設置tv爲當前時間,tz參數未使用。

示例

struct timeval tv1, tv2, tv3;

/* Set tv1 = 5.5 seconds */
tv1.tv_sec = 5; tv1.tv_usec = 500*1000;

/* Set tv2 = now */
evutil_gettimeofday(&tv2, NULL);

/* Set tv3 = 5.5 seconds in the future */
evutil_timeradd(&tv1, &tv2, &tv3);

/* all 3 should print true */
if (evutil_timercmp(&tv1, &tv1, ==))  /* == "If tv1 == tv1" */
   puts("5.5 sec == 5.5 sec");
if (evutil_timercmp(&tv3, &tv2, >=))  /* == "If tv3 >= tv2" */
   puts("The future is after the present.");
if (evutil_timercmp(&tv1, &tv2, <))   /* == "If tv1 < tv2" */
   puts("It is no longer the past.");

evutil_gettimeofday()由2.0版本引入外,這些函數由1.4.0-beta版本引入。
注意:在1.4.4之前的版本中使用<=或者>=是不安全的。

3 套接字API兼容性

本節由於歷史原因而存在:Windows從來沒有以良好兼容的方式實現Berkeley套接字API。 下面是一些用戶使其兼容的函數接口

int evutil_closesocket(evutil_socket_t s);
#define EVUTIL_CLOSESOCKET(s) evutil_closesocket(s)

這個接口用於關閉socket套接字。在Unix中,它是close()的別名;在Windows中,它調用closesocket()。(在Windows中不能將close()用於套接字,也沒有其他系統定義了closesocket()
evutil_closesocket()函數在2.0.5-alpha版本引入。在此之前,需要使用EVUTIL_CLOSESOCKET宏。

#define EVUTIL_SOCKET_ERROR()
#define EVUTIL_SET_SOCKET_ERROR(errcode)
#define evutil_socket_geterror(sock)
#define evutil_socket_error_to_string(errcode)

這些宏用於訪問和操作套接字錯誤代碼。EVUTIL_SOCKET_ERROR()返回本線程最後一次套接字操作的全局錯誤號,evutil_socket_geterror()則返回某特定套接字的錯誤號。(在類Unix系統中都是errnoEVUTIL_SET_SOCKET_ERROR()修改當前套接字錯誤號(與設置Unix中的errno類似),evutil_socket_error_to_string()返回代表某給定套接字錯誤號的字符串(與Unix中的strerror()類似)。
(因爲對於來自套接字函數的錯誤,Windows不使用errno,而是使用WSAGetLastError(),所以需要這些函數。)
注意:Windows套接字錯誤與從errno看到的標準C錯誤是不同的。

int evutil_make_socket_nonblocking(evutil_socket_t sock);

這個函數確保關閉監聽套接字後,它使用的地址可以立即被另一個套接字使用。(在Unix中它設置SO_REUSEADDR標誌,在Windows中則不做任何操作。不能在Windows中使用SO_REUSEADDR標誌:它有另外不同的含義(譯者注:多個套接字綁定到相同地址))

int evutil_make_socket_closeonexec(evutil_socket_t sock);

這個函數告訴操作系統,如果調用了exec(),應該關閉指定的套接字。在Unix中函數設置FD_CLOEXEC標誌,在Windows上則沒有操作。

int evutil_socketpair(int family, int type, int protocol,
        evutil_socket_t sv[2]);

這個函數的行爲跟Unix的socketpair()調用相同:創建兩個相互連接起來的套接字,可對其使用普通套接字IO調用。函數將兩個套接字存儲在sv[0]sv[1]中,成功時返回0,失敗時返回-1。
在Windows中,這個函數僅能支持AF_INET協議族、SOCK_STREAM類型和0協議的套接字。注意:在防火牆軟件明確阻止127.0.0.1,禁止主機與自身通話的情況下,函數可能失敗。
除了evutil_make_socket_closeonexec()由2.0.4-alpha版本引入外,這些函數都由1.4.0-alpha版本引入。

4 可移植的字符串函數

ev_int64_t evutil_strtoll(const char *s, char **endptr, int base);

這個函數與strtol行爲相同,只是用於64位整數。在某些平臺上,僅支持十進制。

int evutil_snprintf(char *buf, size_t buflen, const char *format, );
int evutil_vsnprintf(char *buf, size_t buflen, const char *format, va_list ap);

這些snprintf替代函數的行爲與標準snprintfvsnprintf接口相同。函數返回在緩衝區足夠長的情況下將寫入的字節數,不包括結尾的NULL字節。(這個行爲遵循C99snprintf()標準,但與Windows的_snprintf()相反:如果字符串無法放入緩衝區,_snprintf()會返回負數)

evutil_strtoll()從1.4.2-rc版本就存在了,其他函數首次出現在1.4.5版本中。

5 區域無關的字符串操作函數

實現基於ASCII的協議時,可能想要根據字符類型的ASCII記號來操作字符串,而不管當前的區域設置。libevent爲此提供了一些函數:

int evutil_ascii_strcasecmp(const char *str1, const char *str2);
int evutil_ascii_strncasecmp(const char *str1, const char *str2, size_t n);

這些函數與strcasecmp()strncasecmp()的行爲類似,只是它們總是使用ASCII字符集進行比較,而不管當前的區域設置。這兩個函數首次在2.0.3-alpha版本出現。

6 IPv6輔助和兼容性函數

const char *evutil_inet_ntop(int af, const void *src, char *dst, size_t len);
int evutil_inet_pton(int af, const char *src, void *dst);

這些函數根據RFC 3493的規定解析和格式化IPv4與IPv6地址,與標準inet_ntop()inet_pton()函數行爲相同。要格式化IPv4地址,調用evutil_inet_ntop(),設置afAF_INETsrc指向in_addr結構體,dst指向大小爲len的字符緩衝區。對於IPv6地址,af應該是AF_INET6src則指向in6_addr結構體。要解析IP地址,調用evutil_inet_pton(),設置afAF_INET或者AF_INET6src指向要解析的字符串,dst指向一個in_addr或者in_addr6結構體。
失敗時evutil_inet_ntop()返回NULL,成功時返回到dst的指針。成功時evutil_inet_pton()返回0,失敗時返回-1。

int evutil_parse_sockaddr_port(const char *str, struct sockaddr *out,
    int *outlen);

這個接口解析來自str的地址,將結果寫入到out中。outlen參數應該指向一個表示out中可用字節數的整數;函數返回時這個整數將表示實際使用了的字節數。成功時函數返回0,失敗時返回-1。函數識別下列地址格式:

+ [ipv6]:端口號(如[ffff::]:80)
+ ipv6(如ffff::)
+ [ipv6](如[ffff::])
+ ipv4:端口號(如1.2.3.4:80)
+ ipv4(如1.2.3.4

如果沒有給出端口號,結果中的端口號將被設置爲0。

int evutil_sockaddr_cmp(const struct sockaddr *sa1,
    const struct sockaddr *sa2, int include_port);

evutil_sockaddr_cmp()函數比較兩個地址,如果sa1sa2前面,返回負數;如果二者相等,則返回0;如果sa2sa1前面,則返回正數。函數可用於AF_INETAF_INET6地址;對於其他地址,返回值未定義。函數確保考慮地址的完整次序,但是不同版本中的次序可能不同。
  如果兩個地址只有端口號不同, z只有在include_port參數爲false,它們被認爲是相等的。否則,具有不同端口號的地址被認爲是不等的。
  除evutil_sockaddr_cmp()在2.0.3-alpha版本引入外,這些函數在2.0.1-alpha版本中引入。

7 結構體可移植性函數

#define evutil_offsetof(type, field) /*  */

跟標準offsetof宏一樣,這個宏返回從type類型開始處到field字段的字節數。
這個宏由2.0.1-alpha版本引入,但2.0.3-alpha版本之前是有bug的。

8 安全隨機數生成器

很多應用(包括evdns)爲了安全考慮需要很難預測的隨機數。

void evutil_secure_rng_get_bytes(void *buf, size_t n);

這個函數用隨機數據填充buf處的n個字節。
如果所在平臺提供了arc4random(),libevent會使用這個函數。否則,libevent會使用自己的arc4random()實現,種子則來自操作系統的熵池(entropy pool)(Windows中的CryptGenRandom,其他平臺中的/dev/urandom

int evutil_secure_rng_init(void);
void evutil_secure_rng_add_bytes(const char *dat, size_t datlen);

  不需要手動初始化安全隨機數發生器,但是如果要確認已經成功初始化,可以調用evutil_secure_rng_init()。函數會播種RNG(如果沒有播種過),並在成功時返回0。函數返回-1則表示libevent無法在操作系統中找到合適的熵源(source of entropy),如果不自己初始化RNG,就無法安全使用RNG了。
  如果程序運行在可能會放棄權限的環境中(比如說,通過執行chroot()),在放棄權限前應該調用evutil_secure_rng_init()
  可以調用evutil_secure_rng_add_bytes()向熵池加入更多隨機字節,但通常不需要這麼做。
這些函數是2.0.4-alpha版本引入的。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章