Libevent R5: Helper functions and types for Libevent

From 탱이의 잡동사니
Jump to navigation Jump to search

Overview

원문은 이곳<ref>http://www.wangafu.net/~nickm/libevent-book/Ref5_evutil.html</ref>에서 확인할 수 있다.

<event2/util.h> 헤더 파일에는 Libevent 사용에 유용한 여러가지 기능들이 담겨있다. Libevent 역시 내부적으로 이 기능들을 사용하고 있다.

Basic types

evutil_socket_t

윈도우즈를 제외한 대부분의 OS 에서, 소켓은 Int 타입 숫자로 나타난다. 하지만 윈도우즈 소켓 API 에서는 SOCKET 타입으로 나타난다. OS 에서 관리되는 포인터 타입이다. 그리고 Libevent 에서 사용되는 소켓은 evutil_socket_t 타입으로 표현되며 socket()혹은 accept() 의 결과값 처럼 사용될 수도 있다.

  • Definition

<source lang=c>

  1. ifdef WIN32
  2. define evutil_socket_t intptr_t
  3. else
  4. define evutil_socket_t int
  5. endif

</source>

Satandard intefer types

PAss..

Miscellaneous compatibility types

ev_ssize_t 타입은 ssize_t (signed size_t) 타입으로 설정된다. 만약 해당 타입이 없는 플랫폼이라면 그에 준하는 타입으로 설정된다. ev_ssize_t 로 설정 가능한 최대 값은 EV_SSIZE_MAX 로 지정되어 있다. 최소 값은 EV_SSIZE_MIN 이다. (만약 사용 플랫폼에서 SIZE_MAX 가 정의되어 있지 않다면, EV_SIZE_MAX 를 대신 사용해도 괜찮다.)

ev_off_t 타입은 offset 혹은 chunk memory 를 나타낼 때 사용된다. 플랫폼 내부적으로는 off_t 로 표현되고, 윈도우즈에서는 ev_int64 로 표현된다.

소켓 API 에서 Length와 관련된 부분은 socklen_t 로 나타난다. 이런 타입들은 ev_socklen_t 타입으로 구현되어 있다.

The ev_intptr_t type is a signed integer that is large enough to hold a pointer without loss of bits. The ev_uintptr_t type is an unsigned integer large enough to hold a pointer without loss of bits.

Timer portability functions

모든 플랫폼에서 standard timeval 관련 함수를 제공하진 않는다. 그래서 Libevent 에서는 자체적인 함수를 제공한다.

  • Interface

<source lang=c>

  1. define evutil_timeradd(tvp, uvp, vvp) /* ... */
  2. define evutil_timersub(tvp, uvp, vvp) /* ... */

</source> 위의 매크로들은 각각 첫번째와 두번째 인자들을 추가/삭제 하여 세번째 인자에 저장하는 매크로이다.

  • Interface

<source lang=c>

  1. define evutil_timerclear(tvp) /* ... */
  2. define evutil_timerisset(tvp) /* ... */

</source> 위의 매크로들은 입력된 timeval 를 0으로 설정하는 매크로들이다. 성공시 true, 실패시 false 를 리턴한다.

  • Interface

<source lang=c>

  1. define evutil_timercmp(tvp, uvp, cmp)

</source> 입력된 두 개의 timeval 을 비교하는 하매크로이다. 주어진 timeval tvp, uvp 를 입력된 cmp 구문에 따라 참이면 true, 거짓이면 false 를 리턴한다. cmp에는 C 관계 명령어(<, >, ==, !=, <=, >=)을 입력하면 된다.

  • Interface

<source lang=c> int evutil_gettimeofday(struct timeval *tv, struct timezone *tz); </source> 현재 시각을 tv 에 입력해준다. tz는 사용하지 않는다.

  • Example

<source lang=c> struct timeval tv1, tv2, tv3;

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

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

/* Set tv3 = 5.5 seconds in the future */ 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.");

</source>

Socket API compatibility

역사적인 이유로, 윈도우즈와 Berkley 소켓은 서로 다른 방법으로 소켓을 구현해왔다. Libevent 에서는 두 플랫폼 모두에서 사용할 수 있는 함수를 제공한다.

  • Interface

<source lang=c> int evutil_closesocket(evutil_socket_t s);

  1. define EVUTIL_CLOSESOCKET(s) evutil_closesocket(s)

</source> 위의 함수는 소켓 종료 함수이다. 유닉스에서는 close()로, 윈도우즈에서는 closesocket() 으로 구현되어있다.(즉, 윈도우즈에서는 소켓 종료 함수로 close() 를 사용할 수 없다.)

  • Interface

<source lang=c>

  1. define EVUTIL_SOCKET_ERROR()
  2. define EVUTIL_SET_SOCKET_ERROR(errcode)
  3. define evutil_socket_geterror(sock)
  4. define evutil_socket_error_to_string(errcode)

</source> 위 매크로들은 소켓 에러를 다루는 매크로들이다. EVUTIL_SOCKET_ERROR() 는 현재 스레드에서의 마지막 소켓 명령어의 global error code 를 리턴해주며, evutil_socket_geterror() 함수는 지정된 소켓에서의 마지막 소켓 명령어의 에러코드를 리턴해준다(유닉스 시스템에서의 errno). 그리고 EVUTIL_SET_SOKCET_ERROR() 는 소켓 에러 코드를 바꿀때 사용하고, evutil_socket_error_to_string() 은 소켓 에러 코드에 맞는 string 문자열을 리턴해준다.

윈도우즈에서는 errno 를 사용하지 않으므로, 이런 함수들이 필요하다.. 윈도우즈에서 사용하는 소켓 에러들은 standard-C 에러와는 다르다. 조심하자.

  • Interface

<source lang=c> int evutil_make_socket_nonblocking(evutil_socket_t sock); </source> evutil_make_socket_nonblocking() 함수는 소켓을 인자로 받아서 nonblocking 소켓으로 변경하여 리턴한다.(유닉스에서의 O_NONBLOCK, 윈도우즈에서의 FIONBIO 플래그와 같다)

  • Interface

<source lang=c> int evutil_make_listen_socket_reuseable(evutil_socket_t sock); </source> 위 함수는 입력된 소켓이 종료될 경우, 즉시, 재사용이 가능하도록 해주는 함수이다.(유닉스에서의 SO_REUSEADDR 와 같다)

  • Interface

<source lang=c> int evutil_make_socket_closeonexec(evutil_socket_t sock); </source> 위 함수는 exec() 관련 함수가 호출될 경우, 해당 소켓을 종료하라는 뜻의 함수이다. 유닉스에서의 FD_CLOEXEC 플래그와 같다.

  • Interface

<source lang=c> int evutil_socketpair(int family, int type, int protocol, evutil_socket_t sv[2]); </source> 유닉스의 socketpair() 함수와 같다. 주어진 두개의 소켓들이 서로 연결되게 해주는 함수이다. 연결하고자 하는 두개의 소켓들은 sv[0], sv[1] 에 지정되어야 하며, 성공시 0, 실패시 -1을 리턴한다.

윈도우즈에서는 family AF_INET, type SOCK_STREAM, protocol 0 만을 지원한다. 어떤 윈도우즈 환경에서는 방화벽 프로그램 때문에 127.0.0.1 사용이 안되는 경우도 있다.

Portable string manipulation functions

  • Interface

<source lang=c> ev_int64_t evutil_strtoll(const char *s, char **endptr, int base); </source> strtol 과 같은 기능이다. 64-bit integer 타입을 리턴한다는 것이 다르다. 10 진수만 지원한다.

  • Interface

<source lang=c> 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); </source> 위 snprint 대체 함수들는 각각 표준 snprintf와 vsnprintf 와 같은 역할을 한다. 리턴값으로 write 한 바이트 크기를 돌려주며, 종료문자 NULL 은 포함하지 않는다.

Locale-independent string manipulation functions

때때로, ASCII-based 프로토콜을 구현할 때, 현재 시스템 로케일에 상관없이 문자열은 ASCII 타입을 다루고 싶을 때가 있다. 이런 경우 다음을 사용하면 된다.

  • Interface

<source lang=c> int evutil_ascii_strcasecmp(const char *str1, const char *str2); int evutil_ascii_strncasecmp(const char *str1, const char *str2, size_t n); </source> 위 함수들은 strcasecmp 와 strncasecmp()의 기능들을 한다. 단, 현재 로케일 설정과는 상관없이 ASCII 케릭터 셋을 이용한다는 것이 특징이다.

IPv6 helper and portability functions

  • Interface

<source lang=c> 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); </source> 표준 inet_ntop() 과 inet_pton() 과 같은 기능을한다. 단, RFC 3439 에 정의된 IPv4 와 IPv6를 지원한다는 것이 다르다. IPv4 포멧으로 사용한다면, evutil_inet_ntop() 의 경우 af 에는 AF_INET, src 에는 struct in_addr, dst와 len 에는 목적지 주소의 버퍼와 길이 값을 입력해야 한다. IPv6 를 사용한다면, src 에는 struct in6_addr를 입력하면 된다.

evutil_inet_pton() 에서 IPv4를 사용한다면 af 에는 AF_INET 혹은 AF_INET6 를 설정하고 src, dst에는 내용에 맞는 in_addr 혹은 in_add6 값을 설정하면 된다.

evutil_inet_ntop()는 성공시 목적지 주소 포인터, 실패시 NULL을 리턴한다. evutil_inet_pton() 의 경우, 성공시 0, 실패시 -1을 리턴한다.

  • Interface

<source lang=c> int evutil_parse_sockaddr_port(const char *str, struct sockaddr *out, int *outlen); </source> 위의 함수는 str의 주소를 파싱하여 out 으로 입력하는 함수이다. outlen에는 out 에는 실제로 사용가능한 out 버퍼의 length 를 입력해야 한다. 함수 호출 후, outlen 에는 사용한 바이트 크기가 입력된다. 성공시 0, 실패시 -1을 리턴한다. 다음과 같은 주소 체계 사용이 가능하다.

[ipv6]:port (as in "[ffff::]:80")
ipv6 (as in "ffff:")
[ipv6] (as in "[ffff:]")
ipv4:port(as in "1.2.3.4:80")
ipv4 (as in "1.2.3.4")

만약 주어진 포트번호가 없다면, 0으로 설정된다.

  • Interface

<source lang=c> int evutil_sockaddr_cmp(const struct sockaddr *sal, const struct sockaddr *sa2, int include_port); </source> evutil_sockaddr_cmp() 함수는 두개의 주소를 비교하는데, sa1 이 sa2 보다 빠른 주소라면 음수 값을, 같다면 0, 크다면 양수 값을 리턴한다. AF_INET, AF_INET6 에서 작동하며 다른 타입 사용시, 오작동을 한다.

만약 include_port 인자가 false 라면, 입력된 두 주소의 포트 번호 값들은 같다고 간주된다.

Structure macro portablity functions

  • Interface

<source lang=c>

  1. define evutil_offset(type, field) /* ... */

</source> As the standard offsetof macro, this macro yields the number of bytes from the start of type at which field occurs.

This macro was introduced in Libevent 2.0.1-alpha. It was buggy in every version before Libevent 2.0.3-alpha.

Secure random number generator

많은 수의 어플리케이션들이 에측하기 어려운 난수 발생기를 필요로 한다.

  • Interface

<source lang=c> void evutil_secure_rng_get_bytes(void *buf, size_t n); </source> 입력된 버퍼 buf에 주어진 크기 n 사이즈만큼 랜덤 데이터를 채워 넣는다.

만약 플랫폼에서 arc4random() 함수를 지원한다면 Libevent 내부적으로 그 함수를 사용한다. 만약 없다면 Libevent 자체적으로 구현한 arc4random() 함수를 사용한다. 시드 값은 해당 OS의 entropy pool 로부터 가져온다. (윈도우즈에서의 CryptGenRandom, 유닉스에서의 /dev/urandom 등..)

  • Interface

<source lang=c> int evutil_secure_rng_init(void); void evutil_secure_rng_add_bytes(const char *dat, size_t datlen); </source> 일부러 난수 발생기를 초기화할 필요는 없다. 하지만 굳이 정상적으로 초기화 되었는지 확인하고 싶다면 evutil_secure_rng_init() 를 호출하면 된다. 성공시 0, 실패시 -1을 리턴한다.

You do not need to manually initialize the secure random number generator, but if you want to make sure it is successfully initialized, you can do so by calling evutil_secure_rng_init(). It seeds the RNG (if it was not already seeded) and returns 0 on success. If it returns -1, Libevent wasn’t able to find a good source of entropy on your OS, and you can’t use the RNG safely without initializing it yourself.

If you are running in an environment where your program is likely to drop privileges (for example, by running chroot()), you should call evutil_secure_rng_init() before you do so.

You can add more random bytes to the entropy pool yourself by calling evutil_secure_rng_add_bytes(); this shouldn’t be necessary in typical use.

References

<references />