下面的内容摘自百度百科,主要讲述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不能为内置表分配内存。