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

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


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

/* ---------------------------------------------------------------------- */
/* Constant definitions */

#if SU_HAVE_BSDSOCK || DOCUMENTATION_ONLY
enum {
  /** 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_WINSOCK
enum {
  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_ONLY
typedef int su_socket_t;
#elif SU_HAVE_WINSOCK
typedef 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_MAXSIZE	128
#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_LEN
	unsigned char ss_len;		/* address length */
	unsigned char ss_family;	/* address family */
#else
	unsigned short ss_family;	/* address family */
#endif
	char	__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_WINSOCK
SOFIAPUBFUN 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 __inline
uint16_t su_ntohs(uint16_t s)
{
  return (uint16_t)(((s & 255) << 8) | ((s & 0xff00) >> 8));
}

static __inline
uint32_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。


你可能感兴趣的:(sofia,sip)