基于BSD4.4的UDP通讯初探

       前段时间在移植mmslite到vxWorks上,主要涉及到的修改为网络连接部分、多线程支持部分以及时间模块。这里主要阐述网络部分,其他的有时间会一一推出。由于之前是在windows上实现的,网络连接部分自然就是使用的socket和WSA了。然而和Unix系统一样,vxWorks使用的是BSD,先简单介绍下BSD的概念,BSD ( Berkeley Software Distribution, 伯克利 软件套件)是Unix的 衍生 系统,在1977至1995年间由 加州大学伯克利分校 开发和发布的。历史上,BSD曾经被认为是 UNIX 的一支——"BSD UNIX", 因为它和AT&T UNIX操作系统共享基础代码和设计。在20世纪80年代,衍生出了许多变形的UNIX 授权 软件。比较著名的如 DEC 的Ultrix及Sun公司的 SunOS 。1990年代,BSD很大程度上被System V4.x版以及OSF/1系统所取代,晚期BSD版本为几个开源软件开发提供了平台并且一直沿用至今。今天,“BSD”并不特指任何一个BSD衍生版本,而是类UNIX 操作系统 中的一个分支的总称。

下面的内容摘自百度百科,主要讲述BSD的几个种类:

不同的BSD操作系统针对不同的用途及用户,可应用于多种硬件构架。在政府机构中常能看到BSD的身影。虽然下面的BSD功能可能并非独有,但每种BSD在各自的领域,都逐渐具有了良好声誉,有的专注于性能,有的则以安全见长。

DragonflyBSD是最年轻的BSD,专门提供比FreeBSD更优秀的对称多处理机系统,并使内核直接支持SSI集群,以取得更好的计算效果。这个项目在此方向上,才开始数年,主要关注i386平台;

FreeBSD在BSD家族中以易用性与高性能而著称,由于主要用作微处理器架构,如i386、AMD's 64-bit i386扩展,所以FreeBSD非常关注多处理器。FreeBSD在i386和amd64服务器上,运行得非常好,当然,它也可以在其他硬件构架上运行;

NetBSD拥有特别出色的可移植性,能在多达54种平台上运行,小到嵌入式的掌上设备,大到服务器群,NetBSD甚至还在国际空间站中服务;

OpenBSD在密码学和安全方面特别出众,可移植性也很好,当然略逊于NetBSD。安全功能如OpenSSH,是由OpenBSD率先开创的。OpenBSD作为安全请求机器(security demanding machines)运行,受到好评。

PCBSD是一个基于freebsd的以桌面应用为目的的开源操作系统。pcbsd开发了一种新的软件安装方式--PBI格式,使其便于应用。

至于TCP连接的,网上介绍很多,也有源码,不多做赘述,这里主要介绍一下如何利用BSD创建UDP连接。首先是创建连接,在此阶段,windows下会创建WSA并关联事件内核对象,而BSD下则会简单不少:

BOOL Open(char* IP,int port)
{
	int nTypeLen,sockBindSize,sockSendSize;
	if(isOpen())/*判断是否已创建,即socket是否为-1*/
		Close();/*已创建则关闭socket重新创建*/
	m_sock = socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP);
	if (m_sock == -1)
	{
		printf("创建套接字失败!\n");
		return FALSE;
	}
	sockBindSize = sizeof(struct sockaddr_in);
	bzero((char *)&m_bindAddr,sockBindSize);
	m_bindAddr.sin_family = AF_INET;
	m_bindAddr.sin_addr.s_addr = htonl(INADDR_ANY);
	m_bindAddr.sin_port = htons(port);
	m_bindAddr.sin_len = (u_char)sockBindSize;
	if (bind(m_sock,(SOCKADDR *)&m_bindAddr,sockBindSize)) /*绑定socket*/
	{
		return FALSE;
	}
	sockSendSize = sizeof(struct sockaddr_in);
	bzero((char *)&m_sendAddr,sockSendSize);
	m_sendAddr.sin_family=AF_INET;
	m_sendAddr.sin_addr.s_addr=inet_addr(IP);
	m_sendAddr.sin_port=htons(port);
	m_sendAddr.sin_len = (u_char)sockSendSize;
	nTypeLen = sizeof(m_nMaxFrameSize);
	if(getsockopt(m_sock,SOL_SOCKET, SO_SNDBUF,(char*)&m_nMaxFrameSize,&nTypeLen)<0) /*获取发送帧长度,后面写时用*/
	{
		return FALSE;
	}
	return TRUE;
}

创建连接成功后,即可发送和接收数据了,如下:

发送数据:

int Write(void * lpBuf,int len)
{
	int nRe;
	if(len >= (int)m_nMaxFrameSize) /*这就是上面获取的长度*/
	{
		return 0;/*数据太长*/
	}
	nRe = sendto(m_sock,(char *)lpBuf,len,0,(SOCKADDR *)&m_sendAddr,sizeof(m_sendAddr)); /*发送数据帧*
	nRe = (nRe < 0 ? 0 : nRe);
	return nRe;
}

接收数据:

int Read(void * lpBuf,int len,int timeout)
{
	int nReLen,sockLen;
	fd_set readfds; /*文件描述符集合*/
	struct timeval tv; /*超时结构体*/
	struct sockaddr_in recvSock;
	sockLen = sizeof(recvSock);
/* Select here to make sure we get either a valid echo response or EWOULDBLOCK.Otherwise we could screw some of our 0-buffer tests. */
	if(m_sock >= 0)
	{
		FD_ZERO(&readfds);/*清空集合*/
		FD_SET(m_sock,&readfds);/*从集合中添加一个文件描述符*/
		tv.tv_sec = 1;
		tv.tv_usec = 0;/*设置超时*/
		int err;
		err = select(m_sock+1,&readfds,NULL,NULL,&tv);
		if(err <= 0)
			return 1;
	}
	if(FD_ISSET(m_sock,&readfds))/*测试文件描述符是否是集合的一部分*/
		nReLen = recvfrom(m_sock,(char *)lpBuf,len,0,(SOCKADDR *)&recvSock,&sockLen);
	if(nReLen < 0)
	{
		/*接收数据失败!*/
		return 0;
	}
	return nReLen;
}

这里主要区别的就是select函数,函数原型为int select(int n, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);它代替了windows下的WSAEventSelect函数。如果用过BSD的话,还会发现有一个pselect函数,二者都是等待一定数量的文件描述符上的状态变化。它们的功能是一样的,只是有三个不同:

(1)、select使用的timeout参数类型是struct timeval_r(以秒和微秒为成员)而 pselect使用的类型则是struct timespect(以秒和纳秒为成员)。

(2)、select函数能刷新timeout的值来知道还有多少剩余时间,而pselect不行。

(3)、select 没有sigmask掩码,并且行为和sigmask为NULL时的pselect一致。

三个相互独立的集合被监听,列在readfds中的项目会被监视,是否有字符变为可读的;列在writefds中的项目被监视,是否写入会是非阻塞的;列在exceptfds中的则是被监视,是否有异常。在退出的时候,这些集合会被适应的修改,以指出哪个集合的状态发生了变化。

另外有四个宏用于操作集合。分别为FD_CLR、FD_ISSET、FD_SET和FD_ZERO。FD_ZERO会清空一个集合;FD_SET和FD_CLR从指定的集合中添加或是删除一个指定的文件描述符;FD_ISSET用于测试一个文件描述符是否是集合的一部分,它在select返回之后是很有用的。

n是三个集合中最大的文件描述符的值加一得到的。

timeout是select返回的上限值。它可以是零,select会立即返回。(这样对poll很有用)如果timeout是NULL的话,select也许会阻塞。(,select can block indefinitely.)

sigmask是一个指向信号掩码的指针(参考sigpromask(2));如果它不为空,那么pselect会先用sigmask指向的掩码替换当前的信号掩码,然后再执行select的功能,然后再恢复原来的信号掩码。

在成功时,select返回包含在描述符集合中的文件描述符的数量,它有可能为零,当timeout在任何导致堵塞的事件发生以前就终止而退出的时候就会这样。发生错误的时候,返回-1,并且会适当的设置errno;集合和timeout变得不明确,所以在错误发生后不要再信任它们的内容。错误代码主要如下:

EBADF:在某个集合当中有不合法的文件描述符;

EINTR:一个非阻塞的信号被捕获;

EINVAL:n是负数或timeout中的某个域的值有误;

ENOMEM:select不能为内置表分配内存。




 

你可能感兴趣的:(windows,struct,socket,unix,FreeBSD,通讯)