阅读Sofia-SIP源码 - su模块 - su.h

来源:互联网 发布:淘宝女装logo图片 编辑:程序博客网 时间:2024/05/16 01:46

此头文件最开始部分都是约定俗成的内容,这里就不再多啰嗦了。然后是包含自身软件包内的头文件,以及针对BSD平台或WINDOWS平台包含不同的头文件。这些都很好理解。


然后是常量定义。首先,为BSD平台定义INVALID_SOCKET和SOCKET_ERROR这两个windows平台下特有的常量,以及定义软件包特有的两个常量:su_sucess和su_failure。再为上述最后两个常量各定义一个别名:SU_SUCESS和SU_FAILURE。以及其他一些常用的长度常量。

/* ---------------------------------------------------------------------- *//* Constant definitions */#if SU_HAVE_BSDSOCK || DOCUMENTATION_ONLYenum {  /** Invalid socket descriptor, error from socket() or accept() */  INVALID_SOCKET = -1,#define INVALID_SOCKET ((su_socket_t)INVALID_SOCKET)  /** Error from other socket calls */  SOCKET_ERROR = -1,#define SOCKET_ERROR SOCKET_ERROR  /** Return code for a successful call */  su_success = 0,  /** Return code for an unsuccessful call */  su_failure = -1};#if SYMBIAN && !defined(MSG_NOSIGNAL)#define MSG_NOSIGNAL (0)#endif#elif SU_HAVE_WINSOCKenum {  su_success = 0,  su_failure = 0xffffffffUL};#define MSG_NOSIGNAL (0)#endif/**@HI Maximum size of host name. */#define SU_MAXHOST (1025)/**@HI Maximum size of service name. */#define SU_MAXSERV (25)/**@HI Maximum size of address in text format. */#define SU_ADDRSIZE (48)/**@HI Maximum size of port number in text format. */#define SU_SERVSIZE (16)#define SU_SUCCESS su_success#define SU_FAILURE su_failure

紧接着是socket描述符类型。

#if SU_HAVE_BSDSOCK || DOCUMENTATION_ONLYtypedef int su_socket_t;#elif SU_HAVE_WINSOCKtypedef SOCKET su_socket_t;#endif
我们都知道windows平台和其他平台下的socket描述符不一致。windows平台的描述符类型是SOCKET,而其他平台是int。为了抹平不同平台下的差异,Sofia软件包又定义了一个su_socket_t类型作为socket描述符类型。

之前在阅读su_configure.h头文件时,遇到过SU_HAVE_SOCKADDR_STORAGE宏,不知道它的具体含义。这次在这个头文件中看到了使用它的地方。当初在头文件中我是这么注释的:

/** Define as 1 if you have struct sockaddr_storage */  #define SU_HAVE_SOCKADDR_STORAGE 1        widnows平台下看到有这个结构体,因此为1。  
含义很明显,因为有这个结构体所以这个宏为1。但这个结构体有什么作用呢?因此我上网仔细查了查。找到了MSDN网页:https://msdn.microsoft.com/en-us/library/windows/desktop/ms740504(v=vs.85).aspx。网页上第一段就是对这个结构体的描述:

The SOCKADDR_STORAGE structure stores socket address information. Since the SOCKADDR_STORAGE structure is sufficiently large to store address information for IPv4, IPv6, or other address families, its use promotes protocol-family and protocol-version independence and simplifies cross-platform development. Use the SOCKADDR_STORAGE structure in place of the sockaddr structure.

typedef struct sockaddr_storage {  short   ss_family;  char    __ss_pad1[_SS_PAD1SIZE];  __int64 __ss_align;  char    __ss_pad2[_SS_PAD2SIZE];} SOCKADDR_STORAGE, *PSOCKADDR_STORAGE;
说的很明白了,引入这个结构体是为了跨平台而用。而且它空间足够大,能够容纳任何地址家族的地址信息。再来看su.h头文件中接下来的代码:

#if !SU_HAVE_SOCKADDR_STORAGE/* * RFC 2553: protocol-independent placeholder for socket addresses */#define _SS_MAXSIZE128#define _SS_ALIGNSIZE(sizeof(int64_t))#define _SS_PAD1SIZE(_SS_ALIGNSIZE - sizeof(u_char) * 2)#define _SS_PAD2SIZE(_SS_MAXSIZE - sizeof(u_char) * 2 - \_SS_PAD1SIZE - _SS_ALIGNSIZE)struct sockaddr_storage {#if SU_HAVE_SOCKADDR_SA_LENunsigned char ss_len;/* address length */unsigned char ss_family;/* address family */#elseunsigned short ss_family;/* address family */#endifchar__ss_pad1[_SS_PAD1SIZE];int64_t __ss_align;/* force desired structure storage alignment */char__ss_pad2[_SS_PAD2SIZE];};#endif
目的很明确,如果系统中没有sockaddr_storage结构体,那么Sofia就自己定义一个。在这之前定义了一些结构体会用到的宏。关于sockaddr结构体的其他一些信息,这里有一篇文章可以参考下:http://blog.chinaunix.net/uid-7596647-id-2607376.html。

然后是定义了一个大的union类型。

/** Common socket address structure. */union su_sockaddr_u {#ifdef DOCUMENTATION_ONLY  uint8_t             su_len;         /**< Length of structure */  uint8_t             su_family;      /**< Address family. */  uint16_t            su_port;        /**< Port number. */#else  short               su_dummy;      /**< Dummy member to initialize */#if SU_HAVE_SOCKADDR_SA_LEN#define               su_len          su_sa.sa_len#else#define               su_len          su_array[0]#endif#define               su_family       su_sa.sa_family#define               su_port         su_sin.sin_port#endif  char                su_array[32];   /**< Presented as chars */  uint16_t            su_array16[16]; /**< Presented as 16-bit ints */  uint32_t            su_array32[8];  /**< Presented as 32-bit ints */  struct sockaddr     su_sa;          /**< Address in struct sockaddr format */  struct sockaddr_in  su_sin;         /**< Address in IPv4 format */#if SU_HAVE_IN6  struct sockaddr_in6 su_sin6;        /**< Address in IPv6 format */#endif#ifdef DOCUMENTATION_ONLY  uint32_t            su_scope_id;    /**< Scope ID. */#else#define               su_scope_id     su_array32[6]#endif};
注释文字显示这是一个通用的socket地址结构体。看到union内放入了很多其他的结构体类型:sockaddr、sockaddr_in和sockaddr_in6。不知为何要放这么多结构体类在一个union内。

接下来是:I/O vector for scatter-gather I/O。关于scatter-gather I/O是什么见后面的附录。应该就是一个针对scatter-gather I/O DMA模式的结构体。它在windows平台下的定义不同于其他平台。因为scatter-gather I/O DMA模式使用了不连续的空间,所以我认为这里的vector解释成向量也行,这个单词也有载体的意思。这个结构体将会被su_vsend()和su_vrecv()两个函数使用。

typedef u_long su_ioveclen_t;

typedef struct su_iovec_s {  su_ioveclen_t  siv_len;  void   *siv_base;} su_iovec_t;
typedef struct __WSABUF {   u_long len;   char FAR* buf; } WSABUF, *LPWSABUF;
这个结构体在windows平台下被定义成上面第二张图。仔细观察会发现它与WSABUF结构体一致。
typedef size_t su_ioveclen_t;
typedef struct su_iovec_s {  void  *siv_base;/**< Pointer to buffer. */  su_ioveclen_t siv_len;/**< Size of buffer.  */} su_iovec_t;
 struct iovec {    void *iov_base;     // Pointer to data.    size_t iov_len;     // Length of data. };
这个结构体在非windows平台下被定义成上面第二张图。仔细观察会发现它与POSIX sockets下的iovec结构体一致。

最后各自又定义了SU_IOVECLEN_MAX:

/*non widows*/#define SU_IOVECLEN_MAX SIZE_MAX/*windows*/#define SU_IOVECLEN_MAX ULONG_MAX
还有一点不同之处在于windows平台下结构体的成员顺序是颠倒的。长度成员在前,数据成员在后。注释里并没有给出具体的原因,但注释指出了一点:不要整体初始化这个结构体,而是分别给各个成员赋值。

最后一部分,是为了跨平台用又定义了一套socket相关的api。这里有初始化(su_init),有退出时调用的清理函数(su_deinit)等等。具体如何实现跨平台的,得看su.c文件了。除了这些引起我关注的是这个函数:

SOFIAPUBFUN int su_is_blocking(int errcode);
申明它并没有在任何平台相关的宏判断下,也就是说所有平台都会申明这个函数。在这之后,又看到了这样一些代码:
#if SU_HAVE_BSDSOCK#define su_ioctl  ioctl/* * Note: before 1.12.2, there was su_isblocking() which did not take argument * and which was missing from WINSOCK */#define su_is_blocking(e) \  ((e) == EINPROGRESS || (e) == EAGAIN || (e) == EWOULDBLOCK)#endif

这段代码的意思是如果这是在非windows平台下,再定义这两个宏。综合这两处代码,是否可以得出:在非windows平台下,先申明一个函数,然后还可以用一个宏来覆盖之前这个函数申明。特意去su.c文件内查看了下,su_is_blocking函数定义确实是在特定条件下才出现,即在windows平台下编译su_is_blocking函数定义才会出现。


#if SU_HAVE_WINSOCKSOFIAPUBFUN int su_inet_pton(int af, char const *src, void *dst);SOFIAPUBFUN const char *su_inet_ntop(int af, void const *src,  char *dst, size_t size);SOFIAPUBFUN ssize_t  su_send(su_socket_t s, void *buffer, size_t length, int flags),  su_sendto(su_socket_t s, void *buffer, size_t length, int flags,    su_sockaddr_t const *to, socklen_t tolen),  su_recv(su_socket_t s, void *buffer, size_t length, int flags),  su_recvfrom(su_socket_t s, void *buffer, size_t length, int flags,      su_sockaddr_t *from, socklen_t *fromlen);static __inlineuint16_t su_ntohs(uint16_t s){  return (uint16_t)(((s & 255) << 8) | ((s & 0xff00) >> 8));}static __inlineuint32_t su_ntohl(uint32_t l){  return ((l & 0xff) << 24) | ((l & 0xff00) << 8)       | ((l & 0xff0000) >> 8) | ((l & 0xff000000U) >> 24);}#define ntohs su_ntohs#define htons su_ntohs#define ntohl su_ntohl#define htonl su_ntohl#else#define su_inet_pton inet_pton#define su_inet_ntop inet_ntop#define su_send(s,b,l,f) send((s),(b),(l),(f))#define su_sendto(s,b,l,f,a,L) sendto((s),(b),(l),(f),(void const*)(a),(L))#define su_recv(s,b,l,f) recv((s),(b),(l),(f))#define su_recvfrom(s,b,l,f,a,L) recvfrom((s),(b),(l),(f),(void *)(a),(L))#endif
上述这段代码也是非常有意思,我想大部分的跨平台C代码可能都会这些写。首先su模块内申明的通用接口一般与类linux接口相同。所以通用接口在非windows平台下都可以像上述#else部分一样,定义一些宏即可。windows平台下通用API的接口定义成与Linux接口一致,然后再在.c文件内完成具体实现。


scatter/gather I/O

scatter/gather方式是与block dma方式相对应的一种dma方式。   
在dma传输数据的过程中,要求源物理地址和目标物理地址必须是连续的。但在有的计算机体系中,如IA,连续的存储器地址在物理上不一定是连续的,则dma传输要分成多次完成。
如果传输完一块物理连续的数据后发起一次中断,同时主机进行下一块物理连续的传输,则这种方式即为block dma方式。
scatter/gather方式则不同,它是用一个链表描述物理不连续的存储器,然后把链表首地址告诉dma master。dma master传输完一块物理连续的数据后,就不用再发中断了,而是根据链表传输下一块物理连续的数据,最后发起一次中断。  
很显然scatter/gather方式比block dma方式效率高。  
这段摘抄自:http://www.xuebuyuan.com/1599059.html。


0 0