unix network programming(3rd)Vol.1 [第6~12章]《读书笔记系列》

第6章 i/o 复用 select和poll函数(重点)

应用场合:

  • 当客户同时处理多个描述符(通常是交互式输入和网络套接字)时,必须使用I/O复用
  • 一个客户同时处理多个套接字(select,poll,epoll,kqueue ) 详见16.5节
  • 如果一个服务器既要处理 TCP,又要处理UDP, 详见6.8节
  • 即使当服务器 (listen ,accept), 又当客户端(connect) 详见8.15节
  • 如果一个服务器要处理多个服务,或者多个协议, 详见13.5节 inetd守护进程

IO模型

  • 阻赛IO
  • 非阻赛IO
  • IO复用(select poll)
  • 信号驱动式IO (SIGIO)
  • 异步IO (POSIX的aio_ 系列函数)

5种IO模型的比较:
前4种是同步IO,最后一种是异步IO, 因为IO操作(recvfrom)将阻赛进程。只有异步IO模型与POSIX定义的异步IO模型相匹配。

同步IO在数据从内核复制到调用者的缓冲区期间,进程阻塞于recvfrom()调用。

  • 6.3 select函数 (使用最多的函数)
    该函数允许进程指示内核等待多个事件中的任何一个发生, 并只在有一个或者多个事件发生或经历一段 指定的时间后才唤醒它。

比如 共有10个要轮训的描述符
{1,3,9,10}的描述符准备好读
{2,8}的描述符准备好写
{4,5,6,7}的描述符有异常条件处理(TIMEOUT ,select 时间到了。。等)

select最大描述符数的修改

4.4 BSD 在<sys/type.h>
#ifndef FD_SETSIZE
#define FD_SETSIZE  256
#endif

如果想提高轮询 用户数量,修改FD_SETSIZE 这个值后,必须重新编译内核。(否则无法生效!)

  • 6.6 shutdown()函数
    通常 终止网络连接的方式是调用close().不过close()有两个限制,却可以使用shutdown()来避免
    1)close 把描述符的引用计数-1,仅在该计数变为0时才关闭socket(详见4.8节 ),
    使用shutdown可以不管引用计数就激发TCP的正常连接终止序列( 详见 图2-5 由FIN开始的4个分节)
    2)close终止读和写两个方向的数据传送。
    既然TCP连接是双全工,我们就有这种场景(即 发送完毕,我还需要接收一些数据)
    include <sys/socket.h>
    int shutdown(int sockfd, int howto);//return 0:ok,  -1:failed
 
    参数:howto
    SHUT_RD 读关闭
    SHUT_WR 写关闭
    SHUT_RDWR 读关闭,写关闭
  • 6.7 str_cli 函数 修订版
    //code

其中select 之后, 有检查FD_ISSET(读fd),FD_ISSET(写fd) 如果缓冲中有数据就做相应操作

可以在 发送完数据之后(写数据到了 EOF),可以关闭写socket,

    if( FD_ISSET( fileno(fp), &rset)  )// 发送缓冲 是否已经发送完毕
    {
       if(  (n= Read(fileno(fp), buf,MAXLINE ))  == 0 ) //本地的数据是否已经读完了,读到EOF
       stdineof = 1;//falg 当文件读完,遇到EOF 就修改flag
       Shutdown(sockfd, SHUT_WR);  //关闭写socket , TCP发送FIN 
       FD_CLR(fileno(fp), &rset);   //reset
       continue;
    }
    Writen(sockfd,n);  //发送最后一次数据

为什么要尽早关闭socket?
因为FD_SIZE 有限,所以业务做完了,能早点关闭就早点关闭。

当然如果写失败(服务器crash,网络连接失败,超时)、 读失败(网络连接失败,超时) 也会得到相应返回值

  • 6.8 TCP echo服务器程序 (用select函数)

  • 6.9 pselect 函数

pselect函数是由POSIX发明

    include <sys/select.h>
    include <signal.h>
    include <time.h>
    int pselect(int maxfdp1, fd_set *readset, fd_set *writeset, fd_set *exceptset, const struct timespec *timeout, const sigset_t *sigmask);
    //return:返回就绪描述符的数量,若超时则为0,若出错则为-1

把时间精确度从select 的timeval微妙 增加到timespec 纳秒 级,
并采用一个指向信号集的指针作为他的一个新参数。 当有信号需要捕获时,该参数能让我们避免竞争条件,详见20.5

  • 6.10 poll函数
    inclue <poll.h>
    int poll(struct pollfd * fdArray, unsigned long nfds, int timeout);//return:返回就绪描述符的数量,若超时则为0,若出错则为-1

出自SystemV的poll的函数提供类似遇select的功能,不过能够为流设备提供额外的信息。

POSIX对select和poll都有需要,不过select使用更频繁。

  • 6.11 TCP echo服务器程序 (用poll函数)

第7章 套接字选项(重点)

分为两大基本类型:
1.启用或者禁用某个特性的二元选项(称为标志选项)
2.取得并返回 我们可以设置 或 检查的特定值的选项(称为值选项)

    include <sys/socket.h>
    int getsockopt(int sockfd, int level, int optname, void* optval, socklen_t * optlen);
    int setsockopt(int sockfd, int level, int optname, const  void* optval, socklen_t * optlen);
    //return: 成功返回0, 出错返回-1
图7-1  套接字层和IP层的套接字选项汇总   后续添加!!!
  • 7.11

    POSIX规定fcntl()是操作fd首选的
    //file control 可执行各种描述符控制操作

    include <fcntl.h>
    int fcntl(int fd, int cmd,.../*int arg*/);//return:成功 取决遇cmd, 失败返回-1  

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

  • 非阻塞式I/O。 通过使用F_SETFL 命令设置O_NONBLOCK 文件状态标志, 我们可以把一个socket设置为非阻塞。 (第16章 详细描述 非阻塞IO)
  • 信号驱动式I/O。通过使用F_SETFL 命令设置O_ASYNC 文件状态标志, 我们可以把一个socket设置成 一旦其状态发生变化,内核就产生一个SIGIO信号。(详见 第25章 )
  • F_SETOWN 命令允许我们指定用于接收SIGIO和SIGURG信号的套接字 宿主(进程ID 或者 进程组ID)。

socket进程组ID ,存放在socket 结构的so_pgid成员(TCPv2 page438)

ioctl()//第17章讨论

sysctl()//获取路由列表


第8章 基本UDP套接字编程

UDP 缺乏流量控制

第9章 基本STCP套接字编程


第10章 STCP client/server程序例子


第11章 名字与地址转换

  • 11.2 DNS (Domian Name system)
  • 11.3 gethostbyname()函数 (仅支持IPV4)
  • 11.4 gethostbyaddr()函数 (仅支持IPV4)
#include <netdb.h>
extern int h_errno;

struct hostent *gethostbyname(const char *name);

#include <sys/socket.h>       /* for AF_INET */
struct hostent *gethostbyaddr(const void *addr,
                              socklen_t len, int type);

struct hostent {
    char  *h_name;            /* official name of host */
    char **h_aliases;         /* alias list */
    int    h_addrtype;        /* host address type */
    int    h_length;          /* length of address */
    char **h_addr_list;       /* list of addresses */
}
#define h_addr h_addr_list[0] /* for backward compatibility */

gethostbynamegethostbyaddr 这2个旧的API

  1. 函数不可重入(函数中有static 变量)
  2. 若网络延迟,阻塞,不通,则会阻塞(主进程假死 所以要用新的API)
  3. 11.19中的gethostbyname_r()、gethostbyaddr_r() 可重入
  4. 11.20中的gethostbyname2() 支持IPV6

建议用新接口,参考man page

**The gethostbyname*() and gethostbyaddr*() functions are obsolete(废除了). Applications should use getaddrinfo(3) and getnameinfo(3) instead. **

对于那些事可重入,那些是race的参考
http://man7.org/linux/man-pages/man3/gethostbyname.3.html
http://pubs.opengroup.org/onlinepubs/009695399/functions/gethostbyname.html

  • 11.5 getservbyname(), getservbyport()函数

  • 11.6 getaddrinfo函数(支持IPV6)

#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>

int getaddrinfo(const char *node, const char *service,
                const struct addrinfo *hints,
                struct addrinfo **res);

void freeaddrinfo(struct addrinfo *res);

const char *gai_strerror(int errcode);//return 指向错误描述消息字符串的指针
  • 11.7 gai_strerror()
  • 11.8 freeaddrinfo()

由getaddrinfo返回的所有存储空间都是动态获取的(比如 来自malloc调用),包括addrinfo结构、ai_addr结构和ai_cononname字符串。
这些存储空间都由freeaddrinfo 返还给系统。

要添加图片
图11-8 getaddrinfo 函数及其行为和结果汇总

以下是自己作者实现的一些函数,因为某些特定原因 ,具体看书上介绍

  • 11.11 host_serv()函数

  • 11.12 tcp_connect()函数

  • 11.13 tcp_listen()函数

  • 11.14 udp_client()函数

  • 11.15 udp_connect()函数

  • 11.16 udp_server()函数

  • 11.17 getnameinfo 函数 (重点)
    getnameinfo()是getaddrinfo()的互补函数,以一个套接字地址为参数,返回 描述其中的主机的一个字符串和描述其中服务的另一个字符串。

以协议无关的方式提供这些信息,调用者不必关心存放在套接字地址结构中的协议地址类型。

    include<netdb.h>
    int getnameinfo(const struct sockaddr *sockaddr, socklen_t addrlen, 
                char *host, socklent_t hostlen,
                char *serv, socklen_t servlen, int flags);
  • 11.18 可重入函数(重点)
    函数中有static 变量,导致函数不可重入, 参考reentrant

  • 11.19 gethostbyname_r()、gethostbyaddr_r() 可重入函数版本

  • 11.20.2 gethostbyname2 函数(对比gethostbyname()支持IPV6

    include <sys/socket.h>
    include <netdb.h>
    struct hostent * gethostbyname2(const char* name, int af);

posted @ 2015-08-18 23:30  scott_h  阅读(256)  评论(0编辑  收藏  举报