Unix网络API函数(3)

来源:互联网 发布:php 换行输出 编辑:程序博客网 时间:2024/06/05 16:34

19.处理套接口的fcntl函数

#include <fcntl.h>
int fcntl(int fd, int cmd, … /* arg */);
返回:依赖于参数cmd—成功,-1—失败。

函数fcntl提供了如下关于网络编程的特性:

1.   非阻塞I/O:通过用F_SETFL命令设置O_NONBLOCK文件状态标志来设置套接口为非阻塞型。

2.   信号驱动I/O:用F_SETFL命令来设置O_ASYNC文件状态标志,这导致在套接口状态发生变化时内核生成信号SIGIO。

3.   F_SETOWN命令设置套接口属主(进程ID或进程组ID),由它来接收信号SIGIO和SIGURG。SIGIO在设置套接口为信号驱动I/O型时生成,SIGURG在新的带外数据到达套接口时生成。

4.   F_GETOWN命令返回套接口的当前属主。

注意事项:

·  设置某个文件状态标志时,先取得当前标志,与新标志路逻辑或后再设置标志。

·  信号SIGIO和SIGURG与其他信号不同之处在于,这两个信号只有在已使用命令F_SETOWN给套接口指派了属主后才会生成。F_SETOWN命令的整参数arg既可以是一个正整数,指明接收信号的进程ID,也可以是一个负整数,它的绝对值是接收信号的进程组ID。

·  当一个新的套接口由函数socket创建时,他没有属主,但是当一个新的套接口从一个监听套接口创建时,套接口属主便由已连接套接口从监听套接口继承而来。


20.gethostbyname函数

#include <netdb.h>
struct hostent *gethostbyname(const char *hostname);
返回:非空指针—成功,空指针—出错,同时设置h_errno。

函数返回的非空指针指向的结构如下:
struct hostent {
    char *h_name; /*规范主机名 */
    char **h_aliases; /* 别名列表 */
    int h_addrtype; /* AF_INET or AF_INET6 */
    int h_length; /* 地址长度 */
    char **h_addr_list; /* IPv4或IPv6地址结构列表 */
};
#define h_addr h_addr_list[0];

按照DNS的说法,gethostbyname执行一个对A记录的查询或对AAAA记录的查询,返回IPv4或IPv6地址。

h_addr的定义是为了兼容,在新代码中不应使用。

返回的h_name称为主机的规范(canonical)名字。当返回IPv6地址时,h_addrtype被设置为AF_INET6,成员h_length被设置为16。

gethostbyname的特殊之处在于:当发生错误时,他不设置errno,而是将全局整数h_errno设置为定义在头文件<netdb.h>中的下列常值中的一个:

·  HOST_NOT_FOUND;

·  TRY_AGAIN;

·  NO_RECOVERY;

·  NO_DATA(等同于NO_ADDRESS)。

有函数hstrerror(),它将h_errno的值作为唯一的参数,返回一个指向相应错误说明的const char *型指针。

 

DNS小常识:

DNS中的条目称为资源记录RR(resource record),仅有少数几类RR会影响我们的名字与地址转换:

·  A:A记录将主机名映射为32位的IPv4地址;

·  AAAA:“四A”记录将主机名映射为128位的IPv6地址;

·  PTR:PTR记录(称为“指针记录”)将IP地址映射为主机名;

·  MX:MX记录指定一主机作为某主机的“邮件交换器”。

·  CNAME:CNAME代表“canonical name(规范名字)”,其常见的用法是为常用服务如ftp和www指派一个CNAME记录。


21.gethostbyname2函数

#include <netdb.h>
struct hostent *gethostbyname2(const char *hostname, int family);
返回:非空指针—成功,空指针—出错,同时设置h_errno。

该函数允许指定地址族,其他与gethostbyname相似。


22.gethostbyaddr函数

#include <netdb.h>
struct hostent *gethostbyaddr(const char *addr, size_t len, int family);
返回:非空指针—成功,空指针—出错,同时设置h_error。

函数根据一个二进制的IP地址并试图找出相应于此地址的主机名,我们关心的是规范主机名h_name。

参数addr不是char *类型,而是一个真正指向含有IPv4或IPv6地址的结构in_addr或in6_addr的指针;len是该结构的大小,对于IPv4是4,对于IPv6是16;family或为AF_INET或为AF_INET6。

按照DNS的说法,该函数查询PTR记录。


23.uname函数

#include <sys/utsname.h>
int uname(struct utsname *name);
返回:非负值—成功,-1—失败。

返回当前主机的名字,存放在如下的结构里:
#define UTS_NAMESIZE 16
#define UTS_NODESIZE 256
struct utsname {
    char sysname[UTS_NAMESIZE];
    char nodename[UTS_NODESIZE];
    char release[UTS_NAMESIZE];
    char version[UTS_NAMESIZE];
    char machine[UTS_NAMESIZE];
};

该函数经常与gethostbyname一起用来确定本机的IP地址:先调用uname获得主机名字,然后调用gethostbyname得到所有的IP地址。

获得本机IP地址的另一个方法是ioctl的命令SIOCGIFCONF。


24.gethostname函数

#include <unistd.h>
int gethostname(char *name, size_t namelen);
返回:0—成功,-1—失败。

返回当前主机的名字。name是指向主机名存储位置的指针,namelen是此数组的大小,如果有空间,主机名以空字符结束。

主机名的最大大小通常是头文件<sys/param.h>定义的常值MAXHOSTNAMELEN。


25.getservbyname函数

#include <netdb.h>
struct servent *getservbyname(const char *servname, const char *protoname);
返回:非空指针—成功,空指针—失败。

函数返回如下结构的指针:
struct servent {
    char *s_name;
    char **s_aliases;
    int s_port;
    char *s_proto;
};

服务名servname必须指定,如果还指定了协议(protoname为非空指针),则结果表项必须有匹配的记录。如果没有指定协议名而服务支持多个协议,则返回哪个端口是依赖于实现的。

结构中的端口号是以网络字节序返回的,所以在将它存储在套接口地址结构时,绝对不能调用htons。


26.getservbyport函数

#include <netdb.h>
struct servent *getservbyport(int port, const char *protname);
返回:非空指针—成功,空指针—出错。

port必须为网络字节序。例如:
sptr = getservbyport(htons(53), “udp”);


27.recv和send

#include <sys/socket.h>
ssize_t recv(int sockfd, void *buf, size_t nbytes, int flags);
ssize_t send(int sockfd, void *buf, size_t nbytes, int flags);
返回:成功返回读入或写出的字节数,出错返回-1。

前三个参数与read和write相同,参数flags的值或为0,或由以下的一个或多个常值逻辑或构成:

flags

描述

recv

send

 

 

 

 

MSG_DONTROUTE

不查路由表

 

y

MSG_DONTWAIT

本操作不阻塞

y

y

MSG_OOB

发送或接收带外数据

y

y

MSG_PEEK

查看外来的消息

y

 

MSG_WAITALL

等待所有数据

y

 

下面说明每个标志的作用:

·  MSG_DONTROUTE:这个标志告诉内核目的主机在直接连接的本地网络上,不要查路由表。这是对提供这种特性的SO_DONTROUTE套接口选项的补充。该标志可以对单个输出操作提供这种特性,而套接口选项则针对某个套接口上的所有输出操作。

·  MSG_DONTWAIT:这个标志将单个I/O操作设为非阻塞方式,而不需要在套接口上打开非阻塞标志,执行I/O操作,然后关闭阻塞标志。

·  MSG_OOB:用send时,这个标志指明发送的是带外数据,用recv时,该标志指明要读的是带外数据而不是一般数据。

·  MSG_PEEK:这个标志可以让我们查看可读的数据,在recv或recvfrom后系统不会将这些数据丢弃。

·  MSG_WAITALL:由4.3BSD Reno引入,他告诉内核在没有读到请求的字节数之前不使读操作返回。如果系统支持这个标志,则可以去掉readn函数。即使设定了该标志,如果发生如下情况:(1)捕获了一个信号;(2)连接被终止;(3)在套接口上发生错误,这个函数返回的字节数仍会比请求的少。


28.readv和writev

#include <sys/uio.h>
ssize_t readv(int filedes, const struct iovec *iov, int iovcnt);
ssize_t writev(int filedes, const struct iovec *iov, int iovcnt);
返回:读到或写出的字节数,出错返回-1。

readv和writev可以让我们在一个函数调用中读或写多个缓冲区,这些操作被称为分散读和集中写。

iovec结构定义如下:
struct iovec {
    void *iov_base; /* starting address of buffer */
    size_t iov_len; /* size of buffer */
};

在具体的实现中对iovec结构数组的元素个数有限制,4.3BSD最多允许1024个,而Solaris2.5上限是16。Posix.1g要求定义一个常值IOV_MAX,而且它的值不小于16。

readv和writev可用于任何描述字。writev是一个原子操作,可以避免多次写引发的Nagle算法。


29.readmsg和writemsg

#include <sys/socket.h>
ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags);
ssize_t sendmsg(int sockfd, struct msghdr *msg, int flags);
返回:成功时为读入或写出的字节数,出错时为-1。

这两个函数是最通用的套接口I/O函数,可以用recvmsg代替read、readv、recv和recvfrom,同样,各种输出函数都可以用sendmsg代替。

参数msghdr结构的定义如下:
struct msghdr {
    void *msg_name; /* protocol address */
    socklen_t msg_namelen; /* size of protocol address */
    struct iovec *msg_iov; /* scatter/gather array */
    size_t msg_iovlen; /* elements in msg_iov */
    void *msg_control; /* ancillary data; must be aligned for a cmsghdr structure */
    socklen_t msg_controllen; /* length of ancillary data */
    int msg_flags; /* flags returned by recvmsg() */
};

该结构源自4.3BSD Reno,也是Posix.1g中所说明的,有些系统仍使用一种老的msghdr结构,此种结构中没有msg_flags成员,而且 msg_control和msg_controllen成员分别被叫做msg_accrights和msg_accrightslen。老系统中支持的唯一一种辅助数据形式是文件描述字(称为访问权限)的传递。

msg_name和msg_namelen成员用于未经连接的套接口,他们与 recvfrom和sendto的第五和第六个参数类似:msg_name指向一个套接口地址结构,如果不需要指明协议地址,msg_name应被设置为空指针,msg_namelen对sendmsg是一个值,而对recvmsg是一个值-结果参数。

msg_iov和msg_iovlen成员指明输入或输出的缓冲区数组。

msg_control和msg_controllen指明可选的辅助数据的位置和大小,msg_controllen对recvmsg是一个值-结果参数。

msg_flags只用于revmsg,调用recvmsg时,flags参数被拷贝到msg_flags成员,而且内核用这个值进行接收处理,接着它的值会根据recvmsg的结果而更新,sendmsg会忽略msg_flags成员,因为它在进行输出处理时使用flags参数。

内核检查的flags和返回的msg_flags如下表所示:

标志

在send flags、
sendto flags、
sendmsg flags中检查

在recv flags、
recvfrom flags、
recvmsg flags中检查

在recvmsg msg_flags
中返回

 

 

 

 

MSG_DONTROUTE

y

 

 

MSG_DONTWAIT

y

y

 

MSG_PEEK

 

y

 

MSG_WAITALL

 

y

 

MSG_EOR

y

 

y

MSG_OOB

y

y

y

MSG_BCAST

 

 

y

MSG_MCAST

 

 

y

MSG_TRUNC

 

 

y

MSG_CTRUNC

 

 

y

前四个标志只检查不返回,下两个标志既检查又返回,最后四个只返回。返回的六个标志含义如下:

·  MSG_BCAST:当收到的数据报是一个链路层的广播或其目的IP地址为广播地址时,将返回此标志。

·  MSG_MCAST:当收到的数据报是链路层的多播时,将返回该标志。

·  MSG_TRUNC:这个标志在数据报被截断时返回。

·  MSG_CTRUNC:这个标志在辅助数据被截断时返回。

·  MSG_EOR:如果返回的数据不是一个逻辑记录的结尾,该标志被清位,反之则置位。TCP不使用这个标志,因为它是一种字节流协议。

·  MSG_OOB:这个标志不是为TCP的带外数据返回的,它用于其他协议族(譬如OSI协议等)。

具体的实现可能会在msg_flags中返回一些输入的flags的标志,所以我们应该只检查那些感兴趣的标志的值。

原创粉丝点击