第1章 简介

异步回调 TCP/IP四层体系结构(用户进程、内核中的协议栈、以太网) 局域网 广域网
  • 客户端的简单实现

if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0);

bzero (&serveraddr, sizeof(serveraddr);

serveraddr.sin_family = AF_INET;

serveraddr.sin_port = htons(port);

if (inet_pton(AF_INET, argv[1], &serveraddr.sin_addr) <= 0);

converts the character string src into a network address structure in the af address family, then copies the network address structure to dst. dst is written in network byte order.

connect(sockfd, (SA *) &servaddr, sizeof(servaddr) )

  • 包裹函数

pthread_开头的函数并不设置Unix的errno变量,而是把errno的指作为函数返回值。所以必须定义包裹函数,分配一个变量保存值,在调用err_sys()之前把errno设为这个值。

只要一个Unix函数有错误发生,全局变量errno就被置为一个指明该错误类型的值。如-1,ETIMEDOUT。

  • 服务器的简单实现

listenfd = socket(AF_INET, SOCK_STREAM, 0);

bzero(&serveraddr, sizeof(serveraddr));

serveraddr.sin_family = AF_INET;

serveraddr.sin_addr.s_addr = htonl(INADDR_ANY); // any address wildcard

serveraddr.sin_port = htons(port);

bind(listenfd, (SA *) &serveraddr, sizeof(serveraddr));

listen(listenfd, LISTENQ);

while (ture) {

connfd = accept(listenfd, (SA *) NULL, NULL);

close(connfd);

}

这都是协议相关的程序,需要开发协议无关的函数。

三路握手,str = time(NULL), ctime(&str)

迭代式服务器,并发式服务器

ISO (International Organization for Standardization) 的 OSI (open systems interconnection) 模型

  • Unix网络命令

netstat -i 提供接口信息 -n 输出数值地址

netstat -r 展示路由表 -n输出数值地址

ifconfig 接口名为参数,获得接口详细信息,其中有ip地址、掩码、广播地址bcast

ping -b bacst的地址 可以找出本地网络众多主机的ip地址的方法之一

第2章 传输层:TCP、UDP和SCTP

![Machine generated alternative text: AF INET race - ping route AF NETS sockaddr } race ping route tcp- BPF DLPI ‘UDP I 128{üYÅÅl: API ICMP ICMP IGMP ARP RARP IPV4 TCP 326’LÅÅåE &12-1 Iscrpl TCP/IPtåhiN*åbi IPv6

  • TCP与UDP

    • TCP可靠、UDP不可靠
    • TCP对发送的数据进行排序、数据只到达一次。UDP不能保证到达的先后次序。
    • TCP是字节流协议,没有任何记录边界。UDP每个数据报都有一个长度,长度会和数据一起发送
    • TCP提供有链接服务,UDP提供无连接服务。
    • TCP提供流量控制、UDP不同共流量控制。
    • TCP的连接是双工的。
  • TCP的三路握手

socket  connect (  ( æslJåJ-Tf-)  SYN  SYN K, ACKJ41  ACk K+l  socket , bind, listen  accept  read

  • 客户端发送SYN(同步)分节,告诉服务器客户端将在连接中发送的数据的初始序列号。
  • 服务器发送ACK(确认),同时也发送SYN分节,含有服务器将在同一连接中发送的数据初始序列号
  • 客户端发送ACK确认服务器的SYN。

ACK中的确认号是发送这一个ACK的一端所希望的下一个序列号,因为SYN占据一个字节,所以每一个SYN的ACK确认号就是该SYN的初始序列号加1. 同样,FIN(结束)的ACK确认号就是FIN的序列号加1.

  • TCP的四次终止

( 主 动 关 闭 )  图 2 一 3  FINM  ACKM+I  ACK N+l  服 务 器  ( 被 动 关 闭 )  read 返 回 0  C 10 当 已  TCP 连 接 关 闭 时 的 分 组 交 换

  • 主动关闭的一端发送FIN分节,表示数据发送完毕,但仍可接收数据
  • 被动关闭的一端发送ACK进行确认,然后TCP连接就进入半关闭的状态
  • 被动关闭的一端发送FIN分节,表示这一端的数据发送完毕。
  • 主动关闭的一端发送ACK进行确认。

通常需要四个分节的,在两端同时没有数据发送,②和③有可能合并,成为三次。

  • TCP状态转换图

SYN_RCVD  FIN WAIT 1  ACK  FIN WArr 2  CLOSED  LISTEN  SYN  SYN. ACK  ESTABLISHED  CLOSING  ACK  VIVE WAIT  ACK  FIN  ACK  FIN  SENT  CLOSE WAIT  FIN  LAST_ACK  2MS1-ܕj  ACK

  • CLOSED、SYN_SENT、ESTABLISHED、FIN_WAIT_1、FIN_WAIT_2、CLOSING、TIME_WAIT
  • CLOSED、LISTEN、SYN_RCVD、ESTABLISHED、CLOSE_WAIT、LAST_ACK

理解状态转换图是使用netstat命令诊断网络问题的基础,也是理解当某个应用进程调用诸如connect、accept和close等函数时所发生过程的关键。

eke  ( ) SYN_SENT  ESTABLISHED  SYN Mss  SYN K, Mss 1460  ACK  &FACK  FIN M  ACK Mel  FIN N  ACK  42-5  socket, bind,  LISTEN(  SYN_RCVD  ESTABLISHED  accept  CLOSE_WAIT )  LAST_ACK

  • TCP的TIME_WAIT状态

主动关闭的那端经历了这个状态,该端点在这个状态的持续时间是最长分节生命周期的两倍,2MSL

TIME_WAIT状态存在的理由:

  • 可靠地实现TCP全双工连接的终止

  • 允许老的重复字节在网络中消逝

  • TCP、UDP的缓冲机制

    • 数据报的最大大小、MTU、路径MTU、IP分片、最小重组缓冲区、MSS最大分节大小
    • TCP的套接字发送缓冲区,write成功返回仅代表重新使用乐原来的应用进程缓冲区,不代表已接收数据。收到ACK才从缓冲区丢弃确认的数据。

以MSS大小的数据报加上TCP首部传递个IP。IP给TCP分节加上IP首部构成IP数据报,传递给链路层时有可能将其分片。MSS选项的目的之一就是试图避免分片。

![应 用 进 程 IP 输 出 队 列 数 据 路 图 2 . 巧 应 用 进 程 缓 冲 区 ( 任 意 大 小 ) WY e 用 户 进 程 内 核 套 接 字 发 送 缓 冲 区 ( SO—SNDBUF ) MSS* 小 的 TCP 分 带 通 常 MSS<MTU—40 (IPv4) 或 MTU—60 (IPv6) MTU 大 小 的 IPv4 或 伊 、 6 分 组 应 用 进 程 写 TCP 套 接 字 时 涉 及 的 步 骤 和 缓 冲 区

  • UDP没有真正的发送缓冲区,但是发送超过套接字发送缓冲区大小的数据报,内核会返回进程一个EMSGSIZE错误。

给用户数据加8字节的首部构成UDP数据报传给IP。相比TCP而言更有可能被分片。

![应 用 进 程 UDP IP 输 出 队 列 数 据 缸 路 图 2 刁 6 应 用 进 程 缓 冲 区 用 户 进 程 内 核 套 接 字 发 送 缓 冲 区 ( SO—SNDBUF ) UDP 数 据 报 MTUX 小 的 ] Pv4 或 ] Pv6 分 组 应 用 进 程 写 UDP 套 接 字 时 涉 及 的 步 骤 与 缓 冲 区 ](data:image/png;base64,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)

第3章 套接字编程简介

  • 套接字地址

    • IPv4套接字

#include <netinet/in.h>

struct in_addr {

in_addr_t s_addr;

};

struct sockaddr_in {

unit8_t sin_len; /* length of structure (16) */

sa_family_t sin_family; /* AF_INET */

in_port_t sin_port; /* 16-bit TCP or UDP port numbers */

​ /* network byte ordered */

struct in_addr sin_addr; /* 32-bit TPV4 address */

/* network byte ordered */

char sin_zero[8]; /* unused */

};

  • 通用套接字地址

#include <sys/socket.h>

struct sockaddr {

uint8_t sa_len;

sa_family_t sa_family; /* address family: AF_xxx value */

char sa_data; /* protocol-specific address */

};

任何接收套接字地质结构的函数必须处理来自所支持的任何协议族的套接字地址结构。

  • IPv6套接字

#include <netinet/in.h>

struct in6_addr {

unit8_t s6_addr[16]; /* 128-bit IPv6 address, network byte ordered */

};

#define SIN6_LEN /* required for compile-time tests */

struct sockaddr_in6 {

unit8_t sin6_len; /* length of this struct (28) */

sa_family_t sin_family; /* AF_INET6 */

in_port_t sin_port; /* transport layer port */

​ /* network byte ordered */

unit32_t sin6_flowinfo; /* flow information, undefined */

struct in6_addr sin6_addr; /* IPv6 address */

/* network byte ordered */

unit32_t sin6_scope_id; /* set of interfaces for a scope */

};

  • 新的通用套接字

struct sockaddr_storage {

uint8_t ss_len; /* length of this struct (implementation dependent) */

sa_famuly_t ss_family; /* address family: AF_xxx value */

};

  • 值——结果 参数

    • 套接字地址传递从进程到内核:bind、connect、sendto. func (fd, (SA *) &serv, sizeof(serv));
    • 套接字地址传递从内核到进程:accept、recvfrom、getsockname、getpeername.

func (fd, (SA *) &serv, &sizeof(serv));

  • 字节排序函数

    • 小端法、大端法、
    • 主机字节序(byteorder命令)、网络字节序(大端法)

#include <netinet/in.h>

uint16_t htons (uint16_t host16bitvalue);

uint32_t htonl (uint32_t host32bitvalue);

uint16_t ntohs (uint16_t net16bitvalue);

uint32_t ntohs (uint32_t bet32bitvalue);

  • 字节操纵函数

b表示字节,mem表示内存

#include <strings.h>

void bzero (void *dest, size_t nbytes);

void bcopy (const void *src, void *dest, size_t nbytes);

int bcmp (const void *ptr1, const void *ptr2, size_t nbytes); // return 0 if equal

#include <string.h>

void *memset(void *dest, int c, sizt_t len);

void *memcpy(void *dest, const void *src, size_t nbytes); //remember the order of the two args: dest = src

int memcmp (const void *ptr1, const void *ptr2, size_t nbytes); // return 0 if equal

  • 地址转换函数

    • inet_aton、inet_addr、inet_ntoa在点分十进制和32位网络字节序转换IPv4
1
2
3
4
5
#include <arpa/inet.h>
int inet_aton(const char *strptr, struct in_addr *addrptr); 
// return 1 if valid, 0 on error
char *inet_ntoa(struct in_addr inaddr);    
//Returns: pointer to dotted-decimal string
  • inet_pton和 inet_ntop 适用IPv4和IPv6, presentation和numeric
1
2
3
4
5
6
7
8
#include <arpa/inet.h>
int inet_pton (int family, const char *strptr, void *addrptr);
const char *inet_ntop(int family, const void *addrptr, char *strptr, size_t len);
//family可以为AF_INET或AF_INET6. 不是这两个则errno为EAFNOSUPPORT
// len 为目标存储单元大小,可为如下,超出这个大小设errno为ENOSPC
#include <netinet/in.h>
#define INET_ADDRSTRLEN 16  /* for IPv4 dotted-decimal */
#define INET_ADDRSTRLEN 46  /* forIPv6 hex string */
  • 包裹函数,使得点分十进制和二进制网络字节序的转换协议无关
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
char *sock_ntop (const struct sockaddr *sa, socklen_t salen)
{
char portstr[8];
static char str[128];
switch (sa->sa_family) {
case AF_INET: {
struct sockaddr_in*sin = (struct sockaddr_in *) sa;
if (inet_ntop(AF_INET, &sin->sin_addr, str, sizeof(str)) == NULL)
return (NULL);
if (ntohs(sin->sin_port) != 0) {
snprintf(portstr, sizeof(portstr), ":&d",
ntohs(sin->sin_port))
strcat (str, portstr);
}
return (str);
}
}
int sock_bind_wild(int sockfd, int family);
int sock_cmp_addr (const struct sockaddr *sockaddr1, 
const struct sockaddr *sockaddr2, socklen_t addrlen);
int sock_cmp_port (const struct sockaddr *sockaddr1,
const struct sockaddr *sockaddr2, socklen_t addrlen);
int sock_get_port (const struct sockaddr *sockaddr, socklen_t addrlen);
char *sock_ntop_host (const struct sockaddr *sockaddr, socklen_t addrlen);
void sock_set_addr (const struct sockaddr *sockaddr, socklen_t addrlen, void *ptr);
void sock_set_port (const struct sockaddr *sockaddr, socklen_t addrlen, int port);
void sock_set_wild (struct sockaddr *sockaddr, socklen_t addrlen);
  • readn、writen和readline函数
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
ssize_t readn(in fd, void *vptr, size_t n)
{
size_t nleft;
ssize_t nread;
char *ptr;
ptr = vptr;
nleft = n;
while (nleft > 0) {
if ((nread = read(fd, ptr, nleft)) < 0) {
if (errno == EINTR)
nread = 0;
else
return(-1);
} else if (nread == 0)
break;
nleft -= nread;
ptr += nread;
}
return n - nleft;
}
ssize_t writen (int fd, const void *vptr, size_t n)
{
size_t nleft;
ssize_t nwritten;
const char *ptr;
ptr = vptr;
nleft = n;
while (nleft > 0) {
if ((nwritten = write(fd, ptr, nleft)) <= 0) {
nwritten = 0;
else
return -1;
}
nleft -= nwritten;
ptr += nwritten;
}
return n;
}
ssize_t readline (int fd, void *vptr, size_t maxlen)
{
ssize_t n, rc;
char c, *ptr;
ptr = vptr;
for (n = 1; n < maxlen; n++) {
again:
if ((rc = read(fd, &c, 1)) == 1) {
*ptr++ = c;
if (c == '\n')
break;
} else if (rc == 0) {
*ptr = 0;
return n - 1;
} else {
if (errno == EINTR)
goto again;
return -1;
}
}
*ptr = 0;
return n;
}
ssize_t readlinebuf (void **vptrptr)
{
if (read_cnt)
*vptrptr = read_ptr;
return read_cnt;
}

第4章 基本TCP套接字编程

  • socket

#include <sys/socket.h>

int socket (int family, int type, int protocol);

// returns: non-negative descriptor if OK, -1 on error

  • family: AF_INET (IPv4) 、AF_INET6 (IPv6) 、AF_LOCAL、AF_ROUTE、AF_KEY
  • type: SOCK_STREAM(字节流套接字)

SOCK_DGRAM

SOCK_SEQPACKET

SOCK_RAW

  • protocol: IPPROTO_TCP

​ IPPROTO_UDP

​ IPPROTO_SCTP

AF_ROIJTE  TCPISCTP  UDP  SCTP  IPv4  æ4-5 socket  AF_INEVI’E  TCP>CTP  UDP  SCTP  AE _KEY

  • connect

#include <sys/socket.h>

int connect (int sockfd, const struct sockaddr *seraddr, socklen_t addrlen);

​ // returns: 0 if ok, -1 on error

error : 1. the client reveives no response to its SYN segment, ETIMEDOUT is return.

​ \2. If the server’s response to the client’s SYN is a reset (RST), ECONNREFUSED is returned

\3. If the client’s SYN elicits an ICMP “destination unreachable” from some intermediate router, EHOSTUNREACH or ENETUNREACH is return.

  • bind

#include <sys/socket.h>

int bind (int sockfd, const struct sockaddr *myaddr, socklen_t addrlen);

// returns: 0 if OK, -1 on error

远程过程调用(remote procedure call, RPC)服务器

讲 程 指 定  IP 地 址  通 配 地 址  通 配 地 址  本 地 IP 地 址  本 地 伊 地 址  0  0  内 核 选 择 IP 地 址 和 端 凵  内 核 选 择 伊 地 址 · 讲 程 指 定 端 囗  进 程 指 定 叩 地 址 , 内 核 选 择 端 囗  进 程 指 定 IP 地 址 和 端 口  图 4 . 6 给 bind 函 数 指 定 要 捆 绑 的 伊 地 址 和 / 或 端 口 号 产 生 的 结 果

指定端口号为0,则内核生成的是链式端口。指定通配地址则套接字已连接才选择一个本地地址。

IPv4通用地址为 INADDR_ANY

struct sockaddr_in servaddr;

servaddr.sin_addr.s_addr = htonl (INADDR_ANY); /* wildcard */

IPv6的通用地址为 IN6ADDR_ANY_INIT

#include <netinet/in.h>

struct sockaddr_in6 serv;

serv.sin6_addr = htonl (in6addr_any); /* wildcard */

  • listen

#include <sys/socket.h>

int listen (int sockfd, int backlog);

一个监听套接字有两个队列:

  • incomplete connection queue: 到达服务器的每个SYN分节对应其中一项,正在等待完成三路握手,处于SYN_RCVD状态
  • completed connection queue: 已完成TCP三路握手过程的客户端对应其中一项,处于ESTABLISHED其中的一项

backog是相应套接字排队的最大连接个数,即两个队列之和。常设LISTENQ

服 务 器  两 队 列 之 和 不 超 。 k 」 四  已 完 成 连 接 队 列  ( ESTAB 凵 SHED 状 态 )  未 完 成 连 接 队 列  ( SYN_RCVD 状 态 )  到 达 的 SYN 分 节

客 户  conn “ 调 用  connecti@回  图 4 . 8  在 未 完 成 队 列 建 立 条 目  SYNK,ACKJ+I  ACKK+I  TCP  该 条 目 从 未 完 成 队  列 转 移 擎 己 完 成 队  列 , “ ce 戗 能 够 返 回  三 路 握 手 和 监 听 套 接 字 的 两 个 队 列

void Listen (fd, int backlog)

{

char *ptr;

if ((ptr = getenv(“LISTENQ”)) != NULL)

backlog = atoi(ptr);

if (listen (fd, backlog) < 0)

err_sys(“listen error”);

}

  • accept

#include <sys/socket.h>

int accept (int sockfd, struct sockaddr *cliaddr, socklen_t *addrlen);

​ //Returns: non-negative descriptor if OK, -1 on error

  • fork 和 exec 函数

#include <unistd.h>

pid_t fork(void);

// Returns: 0 in child, process ID of child in parent, -1 on error

子进程通过getppid获得父进程的进程ID

父进程调用fork之前的所有描述符在fork之后与子进程共享。父进程accept之后调用fork,子进程关闭监听描述符,接着读写这个已连接套接字;父进程必须关闭这个已连接套接字。

exec将当前进程映像替换成新的程序文件,并没有创建新进程,而且该新程序通常从main函数开始执行,进程ID不变。

#include <unistd.h>

int execl (const char *pathname, const char arg0, … / (char *) 0 */);

int execv (const char *pathname, char *const *argv[]);

int execle (const char *pathname, const char arg0, … / (char *) 0, char *char envp[] */ );

int execve (const char *pathname, char *const argv[], char *const envp[] );

int execlp (const char *filename, const char arg0, … / (char *) 0 */ };

int execvp (const char *filename, char *const argv[] );

// All six return: -1 on error, no return on success

每个文件和套接字都有一个引用计数,引用计数在文件表项中维护。

client  connection  connect ( )  server (palvnt)  listenfd  connfd  f ork  server (child)  list enfd  connfd

fork之后套接字相关联的文件表项各自的引用计数为2,父进程close不会发送FIN终止连接。

  • close

#include <unistd.h>

int close (int sockfd);

// Returns: 0 if OK, -1 on error

  • getsockname 返回与套接字关联的本地协议地址 getpeername 关联的外地协议地址

#include <sys/socket.h>

int getsockname (int sockfd, struct sockaddr *localaddr, socklen_t *addrlen);

int getpeername (int sockfd, struct sockaddr *peeraddr, socklen_t *addrlen);

// Both return: 0 if OK, -1 on error

这两个函数用于通配IP地址或端口设0,getsockname获得内核实际赋予的IP地址信息。

fork之后执行exec,套接字地址结构消失,getpeername获得对端IP地址信息。

Obviously the Telnet server in this final example must know the value of connfd when it starts. There are two common ways to do this. First, the process calling exec can format the descriptor number as a character string and pass it as a command-line argument to the newly execed program. Alternately, a convention can be established that a certain descriptor is always set to the connected socket before calling exec. The latter is what inetd does, always setting descriptors 0, 1, and 2 to be the connected socket.

第5章 TCP客户/服务器程序实例

客户和服务器启动时发生什么?客户正常终止时发生什么?若服务器进程在客户之前终止,则客户会发生什么?若服务器主机奔溃,则客户发生什么? 弄清网络层次发生什么以及它们如何反映到套接字API,理解层次的工作原理,并体会如何编写应用程序代码来处理这些情形。

stdin  stdout  fget s  TCP  client  Eputs  writen  readi ine  read  writen  TCP  server

  • #include <stdio.h>
1
2
3
4
5
6
7
8
char *fgets (char *str, int count, FILE *stream);
char *fgets (char *restrict str, int count, FILE *restrict stream);
// Reads at most count - 1 characters from the given file stream and stores them in the character array pointed to by str. 
The behavior is undefined if count is less than 1.
 
#include <cstdio>
int rc = std::fputs("Hello World", stdout);  
// Writes every character from the null-terminated string str to the output stream stream
  • 运行服务器后,启动客户之前,运行 netstat -a 检查服务器监听套接字的状态。

启动客户端,并指定服务器主机ip地址为环回地址127.0.0.1,三路握手。

客户接收三路握手的第二个分节时connect返回,服务器接收第三个分节才返回。

客户阻塞在fgets,因为并未键入文本行。服务器子进程阻塞在read,服务器父进程阻塞在accept。

1
2
netstat -a
ps -t pts/6 -o pid, ppid, tty, stat, args, wchan

The STAT column for all three of our network processes is “S,” meaning the process

is sleeping (waiting for something). When a process is asleep, the WCHAN column

specifies the condition. Linux prints wait_for_connect when a process is blocked in

either accept or connect, tcp_data_wait when a process is blocked on socket input

or output, or read_chan when a process is blocked on terminal I/O. The WCHAN

values for our three network processes therefore make sense.

  • ①键入EOF,fgets return nullptr, main通过exit终止。内核关闭客户端套接字。

②客户TCP发送FIN给服务器,服务器TCP则以ACK响应,服务器套接字处于CLOSE_WAIT状态,客户端套接字处于TIN_WAIT_2状态。

③服务器TCP接收FIN,服务器子进程阻塞readline,readline返回0,mian终止。服务器子进程exit终止。

④服务器到客户FIN,客户到服务器ACK,连接完全终止,客户端套接字处于TIME_WAIT状态。

  • typedef void Sigfunc(int);

Sigfunc *signal (int signo, Sigfunc *func)

{

struct sigactiona act, oact;

act.sa_handler = func;

sigemptyset(&act.sa_mask);

act.sa_flags = 0;

if (signo == SIGALRM) {

#ifdef SA_INTERRUPT

act.sa_flags |= SA_INTERRUPT; /* SunOs 4.x */

#endif

} else {

#ifdef SA_RESTART

act.sa_flags |= SA_RESTART; /* SVR4, 4.4BSD */

#endif

}

if (sigaction (signo, &act, &oact) < 0)

return SIG_ERR;

return oact.sa_handler;

}

SA_RESTART标志可选,设置乐由相应信号中断的系统调用由内核自动重启。

在listen调用之后增加 Signal (SIGCHILD, sig_chld);

void sig_chld (int signo)

{

pid_t pid;

int stat;

pid = wait(&stat);

printf (" child &d terminated\n", pid);

//Warning: Calling standard I/O functions such as printf in a signal handler is not //recommended,

return;

}

子进程终止后发送SIGCHILD,而父进程阻塞于accept调用,sig_chld函数执行,内核会使accept返回一个EINTR错误,于是终止,SA_RESTART标志能自动重启被中断的系统调用。

在accept调用值小于0的条件下,检查errno值是否等于EINTR,continue for循环,实现重启被中断。

  • #include <sys/wait.h>

pid_t wait (int *statloc);

pid_t waitpid (pid_t pid, int *statloc, int options);

// Both return: process ID if OK, 0 or -1 on error

都是用来处理已经终止的子进程,返回终止子进程的ID,以及statloc指针返回子进程终止状态。终止状态有WIFEXITED和WEXITSTATUS。

若进程仍在执行,则wait将阻塞到现有子进程第一个终止为止。

waitpid的第一个参数设为-1则等待第一个终止的子进程。options常用WNOHANG,则没有已终止子进程时不要阻塞。

在信号处理函数内,采用循环,用waitpid(=(-1, &state, WNOHANG)处理。 state是int,WNOHANG表示只要还有尚未终止的子进程在运行时,不要阻塞。

  • 当fork子进程时,必须捕获SIGCHILD信号(listen之后,整个循环之前)

  • 当捕获信号时,必须处理中断的系统调用(accept之后的判定EINTR)

  • SIGCHILD的信号处理函数必须被正确编写,应该使用waitpid函数以免留下僵死进程。

  • 子进程必须关闭listenfd,父进程必须关闭connfd

  • accept返回之前连接终止

三路握手完成从而连接建立之后,客户TCP却发送了一个RST(复位)。在服务器端,该连接已由TCP排队,等着服务器进程调用accept的时候RST到达。稍后,服务器进程调用accept。

errno: EPROTO、ECONNABORTED

  • 服务进程终止

客户端收到FIN,并响应ACK,却正阻塞在fgets上。运行netstat命令,可发现终止的前半部分完成。

客户端键入文本行,发送指服务器,服务器响应RST,然后客户端收不到RST,因为阻塞在readline。

出错信息 “Server terminated prematurely”。

需要依靠select和poll使得一旦杀死服务器子进程,客户就会立即被告知已收到FIN。

  • SIGPIPE信号

当一个进程向某个已收到RST的套接字执行写操作时,内核想该进程发送一个SIGPIPE信号。信号的默认行为是终止进程,因此进程必须捕获它以免不情愿地被终止。写操作都返回EPIPE错误。

  • 服务器主机崩溃

客户阻塞在readline调用上,ETIMEOUT错误,“destination unreadchable"的IGMP消息,所返回的错误是EHOSTUNREACH或ENETUNREACH。

为了尽快检测错误,对readline调用设置一个超时。

服务器主机崩溃后重启

客户阻塞在readline调用,返回ECONNRESET错误。

不主动发送数据也能检测是否奔溃,采用SO_KEEPALIVE套接字选项。

  • client  socket ( )  eonneet ( )  al  server’s  port num  TCP  datalink  client IP address  on muti  datalink  datalink

客户端来连接建立后getsockname获取内核指定的两个本地址值

img

服务器端bind通过指定为通配IP地址,建立连接后用getsockanme来确定本地IP地址。两个客户端地址值由accept调用返回给服务器。若另外一个程序调用accept服务器调用exec执行,getpeername来确定客户的IP地址和端口号。

  • 数据格式

第6章 I/O复用:select和poll函数

客户端需要同时处理两个输入:标准输入和TCP套接字。阻塞在标准输入时会收不到套接字的消息了。

IO复用的场合:

  • 客户处理多个描述符

  • 客户同时处理多个套接字

  • TCP服务器既要处理监听套接字又要处理已连接套接字

  • 一个服务器纪要处理TCP又要处理UDP

  • 一个服务器要处理多个服务或多个协议

  • I/O模型

    • 阻塞式I/O

![应 用 进 程 系 统 调 用 recvfrom 进 程 阻 塞 于 recvfrom 的 训 用 返 回 成 功 指 示 图 6 刁 阻 塞 式 ] ℃ 模 型 内 核 无 数 据 报 准 备 好 等 待 数 据 数 据 报 准 备 好 复 制 数 据 报 将 数 据 从 内 核 复 制 到 用 户 空 间 复 制 完 成 ](data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAooAAAFoCAIAAABfck3PAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAE3vSURBVHhe7d0hnBs3u7bxwtLAlxaWFpYWlhaGvjA0MDSwNLA0MLTw0MLSwtIDQ8/3X98TfcrYu9nE6x3v7H2B+UmaR49kW9Ll2WST7/6vlFJKKVdG9VxKKaVcHdVzKaWUcnVUz6WUUsrVUT2XUkopV0f1XEoppVwd1XMppZRydVTPpZRSytVRPZdSSilXR/VcSimlXB3VcymllHJ1VM+llFLK1VE9l1JKKVdH9VxKKaVcHdVzKaWUcnVUz6WUUsrVUT2XUkopV0f1XEopl+L9+/f/+7//u1QO/PPPP2/fvl0qpdxO9bxnnAuvX792HCz1IwTcwRJ04Lg6jhiF1d3BTZbDrRRmElDK0+Xdu3fffXfXEWrrvXjxgqGX+gFVvboFyhepnvcMNzsI/vvf/y71z6FVd+/gzz//TKSjRHXOk8wOGjEKqsuNAzILhluOJzGuyjO//PLLEl3K0yQ7aKmcIrvjr7/+UqbqfFeO1DUqo54ut1E97wp7/reJH374wUFAjUv9wJBuDheKRXRLmanmluBEwi15nCbKDpSTeh4HjY4//fST9rg5MSM53K2ey1PE6j1soxvypXOpHFh9TxWcABvTmrcf08VVNS3uLtGlfE71vCtsdZvf1/NI8SRDohzsmEhZu47j+dhpkmMlVSQg+nemQNXVEaMgD90qL9EHNOa0WiWHyOq5PEXsGsve6oVVjZQR9WZ/uY6WkAdljcp2xOHJefmye0hcyprqeVdEzyS61D//myk5DgZ5RHZeEGdODacJcarGvgKW0ANJK6H2/OBaIT+pE5/GRDp9huC1YyRHkhsxwaU8IbKeU7aS5y+d+ZHS0LPFn22lMPaOXZAuGpW13PQs5RTV865Y6dkZMYvQacKOKSPWjJ7T0V3lUXVNJAc7elRHZlWHi2qejOdTJmJ21zUFLQJ0yRMDtGDpUMrTYaVnyztlWOFa5qfhsa2s9uwUCLPFFPoNtdxN9bwrchw4CxwTTgEnxc3j6kHJecwdxoVTZj5c5kcBGZInVakkSUtUnapsGVFV4zi2oJwAc1CWWcCK+SAr5UlgMUPBArbCkS+gWujWgj9E3eBrqD2VGLdG/MB31rHjSjmmet4VkWX0rDC3OFMUyDWRyANu/hjMSaHMsoeH23/yTX8O1qhFmJh0hELKKbi1RB9knxgTMLS7uY7g6rk8RW7k/PatDWUNk24KUFC1vMeqTjUL3nZIY+I1ZkMp22u5VcqK6nlXcKFDQSE/PbPz412HggPCWZCwkGMCuZXyKECvJfSTnkeLcmQcka+OmAw60Et3V7PKHCrm8kThZrvMMoZVrcXCVraJso+ytvPDqhg6YcgDtJh8zVVWyK1Sjqmed4XjIHqOTZ0jDgvHB1SdDgkLjgmNzComQhWvo5YcLnotoZ/rOcF5to6eZ92KkTnJJaTktKgGBxNWkynlSUDPtpgFPH8ltcJtCu0WfFoSlp0SPasqu+qou7JNkeBSTlI974cYdPxplpNinCDK45gIhOp0GMGx7Kg6blQj4zD0rDA/JaTjfFSlJUp2SGkRKbM55FSKsOfJlPJUsKQxfx8dzHqGmOyFLHW3rHwt2QU2kWs2SCknqZ73Q350NpsyxLXzwYE0jtMh58gdes53f0PokkLa03H1KJzzaOh5YA59YihPGkt6taoHKz1j1vO7d++yiYhZmbzj6duylVI97wdbnfzsfNcZ7SHVODg/vh4PAbOe8xM5DD3PSUI8HRw6ozo/E+uVlsOwN6TvUpme3Ut5Ktgpr169WiqfGK51nR+sLX7CzpdXq10AN9tfuQvbUMDcpZRB9bwfHByOACdCTHkbMajI+ZE3vWRQdnzEnePUGL3gQFGNX8PBuTc4ehITVEUqSKhwTE+l8hT58OHDUpoYi3y1qucfQZXyVVTP5VwcTJztGJofC8KQeimllK+iei6llFKujuq5lFJKuTqq51JKKeXqqJ5LKaWUq6N6LuUs/vfAX3/99fr16/zV9xm35kZhWpbKGRzGvJXjaZRSnhzVcyn3hfn+OfD+8H8N4YfDP6DmGrQsoZ/46fD/Wy+VT79TflKfGnXP7+G8efPm7du3fK8lv64jz9xLznlcOX85/EuTo1pDl/LUqZ5LuS80+dtvv1Es8u+6cCRnL7eP4Egx+e0yV+qFLroruDv/6jkkZ1kFA7kK49rk1yt5gvbRV4CqhKkKq55L2QHVcylfDfkxIptGiq6jPONxNqL1EJx/W00Ld+ZRWBd3Z+kqR7quAnwDEJkwek5MmPUMyavnUnZG9VzKV+BRlQuj2MNT9M2/FaWqcRatMPqMs13JNfrUyKzcqWMiT/LmzRvxgnWUX7ZjPRtxnoDrXK2eS3nqVM+lfAWsSX6My515FJ6fXAfs6BZo1VWVYj1D66IABVWFxEfhUFB99eoVy8blefJ2K5FBuwwC8sCdjgoQnxGX0FLK06R6LuWrodUI9d2n//R6dm2IksVE3nnmFsa7iNoj4zD/UDp6VkgS12M9M/FSOfwUfXxFkEfw/ChfSnmKVM+lfAVkSZz8x4jv379nQVWm1EKuWpa4g1k1uorBeK6VQV9edwtL9Od6llMweUfk0XzCQvSsXdmg6ZiqPLocokopT5jquZSvgBTJL+QvbzPowbM3f9VrSJQjI2yNIhmUO+NRBdc8ds8c6zn6lyfXhAV6FiOJW8o6uhrLM7ReRlziSilPluq5lK+GO6kxoiVUpqRJRK6BKecq7qlnHYk2elbV6NaxnvOsjNydf7hdPZcHx3oba6w8DtXzFeE4XkrlWnFI/fLLL+zIghGtTw1aCFXjEvcJLe7G0yzOuwpDzzLgJu6TngVI9erVK9mgy7Ge4+zR0aFZPZdLY11ZmZYWXh/+TuJyo1yM6nl7nLZWvKXvzM05Xq6TfFLjD31dhxejzFF19YGCyx1kNxI+/OMhsbLIFHJXAQraxdA5K7smVQJmPZtANB+E6ZhgiDfJlEvw5oz3Z8Y76a1eKocPUctS+Zx8gin/+++/KdzGPFY+/ZQVfKwp43hKmYD2scZuYzXz25AkP+ZZ6uchj5fjJeS8ysosl6N63pgs9Gyhh9pF5RI4lXxSOVJdnaRpyV3E0PGuk2s+YRVEIi3C8qitJfGJsRicuW59+PAhjYnhZl1GC6nPS8VkomepREri+tyOzrx2r9r7kB855L31Kfgq49bqC433UMFbLcAHJ8Bbmp+LJGaFW8kg+ffffy8eI7mECQta8rFmSUSlUBhrANaP7vmkEj9GUfA5zp/yTJaKVPoaApKv5hA0Zm2kKlLZuHmjjHKy130w4UxgqZcLUD1vRvZYjumlqVwxq4NMNQf0Uj9w8rAT41N2Ji716becMbfDAerQ/Pjx41I/oHtObVg2SDmoOp3TmDN3NavnwPzOeys4afXGrvB+etO8sa7eMW/73b3E5COIC9MIZR/iUvmEPLOexwbPp5NycAJAQOYgOB+laSjf9mmatruZPMSonpy5nPMXgsRDu2mvJvO1yHNmhnI31fNm5Kv0UilPkHt+r+rXr0fmbtG6S3t2nwDGFRkFsh1juYXcygeXFhK1WzUqvHr1Ko3Qceg5mSGndvZyVU6eSHflM5qU09C5lWnDWAbSneZVl+gDWoTpBVVpRaY8k8nI6a5Chljuffphg/al/k0YwjzPTFLuoHrehnyrfVoHdzZ5zp3uyXK10EY8t9QP2HHaFVyVIQYsNZ4mSU5BVTuBZXseYm/+zgEUrH+7QMH+VcYwrswyxLJjpygnj+7Kxx51VxezzWTSqCWRqyPCuMKkzVgQefIkMX9zEGxcM1HQkluqBpIq1XMwE6MslfLQVM/bYKvYJEvlieAgGDu8lKuCrhgu+O7IRvbXUn//Pn5SWKI//ewK7OVqYbvGNLprTNggvmRBHTlJi5jbzDRcGJsqGzqqliffEgbuSgX5M0lkYplkhgvKo6qj+HRJywqzddccMo0xrjkgZbdS+DYMXT1fjup5G7L3lsqDYr/Zt1jqh5MrLfMj72i0h+/zKCzePhS8Olxmjr/CyzzyK2RE2NWZ532GLtdPltMda+OiZC1ZYMHO8nQY6S5NB8b0BNiAWdKWIuEJVsjDq17DXgMt7rreiPTwL9LEoCms9jIXcq3kuPHn4WEaIpV1WeIO75skGs1B2RCZqkZjpYwlekK8XkYRqdecc6BjEnpphk6jcl5FXr4kf//9d259A7JJslTKQ1M9b4PdcnLXfRs2p32YYyJXOzAbfrSk0X5Ol19//dW+SnuODNUcQFGm7qruOvuQMLtaBuUksTm16KhLgl0Fu5VsWgQkvxgF+TWaEnJS1NA7wOfrw836sXJO2uLRyAKzULMUV0RXCnaHOaecSDN31dGrUAjDoNk7Yl6+fJkF72W6G9yS2V1hWedipFW2F5LQLalukh4Y28pVNTlzS3yGOyYbc8wcglU1GsjdTAbZfSm4JaG7eXOgLMMI/jZkkHmplIemet4G2+PMjbFCtrHPBzbPfBxATExpcy5NB5XGkW6NDM4OfedJzl0GOYDm4wzjPAqOLa83h1danBQZUcvJtOVp4UO0DGbYwlp6wO+g98fosVGkuyLr0Pq0vKM016ExWKtzR9lubHbwvaqA3NUla3jsEWnzqnXJF5TsDiNq16I8Z07k2Cl5D+1ZZMQEjA0ig0YtUNDuahp5ORqNkoES76VJpSBGqjRC8G3u/1rkN8pSKQ9N9bwN9tWD61nOpfIJe9I+dIU9bCPZw3aUY+WPP/5YgiZyjmRitvTY52EcEzOOm+Otru/qXE7LyKBL9fyk8WmOj9iCiR5O4rOenXRp4jaF43HnHWfVWepewjBoGpVXezOG8wKzUPWK7YRphJZD4P9HkpWeR+YEDEwy+ZEYHSEyGxAJMKW8HC3zJrWple2mk8kzw8xhaa2enw7V8zasjoDzke14f8bKdqa7sL2zw2/TM+xb+1+Ana/L0nogx9MKCY+3emS8VD7NLYdUWnR5WD1LLuHX4mQxk29Ghrz8FUl+Dr///vuSayPyuSyViTT6fJ34yOnvw031DnRcPqpLYqVRlwWsbJ5Z7WF4LuSrqhjxXkIarSKNXl2qM2KS0IudX4vyF/XsAzWcMMxzMFt3Zz2PVBku5RXi9ZJQvMnE0Doeb3+IEQxHwdiSdyT/Wqrni1I9b4O9tJLfmUSBS+UTzgWNrkv9cF7YpXj16tXSdGgck3HLhrd7c74MBNjhS2VC3+OtrmXetMqpDm0LyKwcH7gJOo83b944dL4NL8Fkgle9tB7+mvrSevg/KpbWR8HLWQa+BXMb0/a5eHtXM9eyVL4JOU++5Hxwx7gVDRxjBf76668Pu9pPYhoW2FilpuotShnaTXKpHIi6dOE2ZSvTVMWo6ruasHbvqmDXecUqD6cOpBWmYALKcaf8PhRlSTIxQ7g761nVLYgxooK7JpOAMI8oLDvLaz/e/tA3c3Z3vDMyr3J+M9XzRamet8FuecADyyaxA+1taed9CJs5jcFesue103Na7N45Hrau9nl6ylrkl01wjh6NqoIdFloQ9SIHpbE0CsgGNkmN8rgribIMJqMlvcrTxQdqeaygASvkAdf5HcRPhlvqn5ZxFqGrVYos0ZjS9KLGLMKxsDWavJbsFEilRXcFVZHuhqRNmFd6sw2mP2N2VdZrvAlmmCkpa1Q283FLsJZBshkuAUGYEV0zjdjXEBrHBoS0GpFGM/G60ksX7Qk7EznNc6mUh6Z63gb76gGPLXvPrnOVMyw3DqTFRprbP378mHYsTZ+4o3FwR+PAiJnVUv8883GhPF0YglrCWIrLvctjRKxGVI3bBlmKdKU8TCYsXyXn7vkGOVpYzYaVLS2qCkFOmk8YVL0Do0X1/iKUdk51G3Iy8TL8J7LXhukhTMKl8gmRGlciP4fq+aJUz9vwsHouZVty7mOTVT1rqTwm1fNFqZ63oXoupTx1queLUj1vQ/VcSnnqVM8XpXrehuq5lPLUqZ4vSvW8DdVzKeWpUz1flOp5G6rnUspTp3q+KNXzNlTPpeybP6dfKdwr1fNFqZ63oXouZd/89rj/2PgmVM8XpXrehuq5lH1TPZczqZ63oXouZd9Uz+VMqudtqJ5L2TfVczmT6nkbqudS9k31XM6ket6G6rmUfVM9lzOpnrehei5l3/xy+A+bl8pOqZ4vSvW8DdVzKTvG7n7x4kX1XM6het6G6rmUffPTTz9Vz+UcqudtqJ5L2TF2tz1ePZdzqJ63oXouZd/0r4aVM6meN8Cm/e6776rnUnbMc9CzF+hlLpXy0FTPG1A9l7J73r179/r166WyX/qDwMtRPW/AX3/9Rc+uS72UsjtscIZeKvuler4c1fM2vH379jl8sy7l2ULPu//hNqrny1E9bwM3V8+l7Jj3799Xz+UcqudtsKb/+eefpVJK2R3P5K+G9a/RXI7qeRv6lbOUffNMfrGqer4c1fM2VM+l7Js+PZczqZ63oXouZd88Bz07xHqUXY7qeRu6pkvZN/3hdjmT6nkbqudS9k31XM6ket6G6rmUfVM9lzOpnrehei5l3zwTPf/0009LpTw01fM2VM+l7Jtnouf+j1WXo3rehuq5lH1TPZczqZ63oXouZd9Uz+VMqudtqJ5L2TfVczmT6nkbqudS9k31XM6ket6G6rmUfVM9lzOpnrehei5l31TP5Uyq522onkvZN9VzOZPqeRuq51L2TfVczqR63obquZR9Uz2XM6met6F6LmXfVM/lTKrnbaieS9k31XM5k+p5G6rnUvZN9VzOpHrehuq5lH1TPZczqZ63oXouZd9Uz+VMqudtqJ5L2TfVczmT6nkbqudS9k31XM6ket6G6rmUfVM9lzOpnrehei5l31TP5Uyq522onkvZN9VzOZPqeRuq51L2TfVczqR63obquZR9Uz2XM6met6F6LmXfVM/fjLNx9dZ98Z00k6W0I6rnbaieS9k31fPA++DE++uvv5b6p5bb3h9pv/vuuxH/9u1b1ZMCliEH6YsXL8SrCvvjjz90ScAKgybPP//8I3ieUlqWynVQPW9D9VzKvqmeBz/99BO/vn792qGnzJ3KWnIGSvLu3btEBjFzWsHsq8CgK0lL5ZZ2V9kyysuXL1dhg6F5V6P4gGQIRtSSsCuhet6G6rmUfVM9h5j4l19+cR0ijEc5VbuW2abJ6erRNvHeSfEaU12dnJwqUioBIt2l59xSXn0E8vgqoN11NXPzWbVsTvW8DdVzKfvmmejZy1wqpyDO8fNkBXLNk24el1Xp+RC4IFKY941HR2aNuqQ84wjlVHrWRbwYKP/nP/+R2S0ZtJiDQW/EfvghuRETn2flRLqqVs/lBuujei5lxzwHPTMfz9HbUj/CLXLNWZfH36g3elaOPg+xN3BnVKpdR7egY8KgY2ISr6BFQvCrLojXDa3X6pgVJkBhyFiA01hmBddD1LVQPW9D9VzKvnkOekbseIehQbfwhghzFZ+faROk6uqZ1cFIq+lyMPKCgRyb3lLloWdlqaJ58TKLGXo+nlWmKkC8jlpkGM6WZ2S+BqrnbaieS9k3HPAc9Axu+6KhcePYwx8nC87Tc9Bd41I5vG/DlzPxqFHoPIench58OdUtLd5wd5ONgOdRkAzaheVH4iLFazRoysfjbkj1vA3Vcyn75vnoGcRGe8ePnlFvYNMZ70+eoVPNeaiROKNnjdImBmmHQt5YXRQMGqNrEawjK7ulhY9vJnH4Gbgj99B7+WYgSQqJvCorD6rnbaieS9k3z0rPbEelS+VzHHQnYVxeVIhQRSrwq5bhS1flvI159h3GhbvGjXe5XN8E//7779p9XVjiDnr2cWREvVTdNWG9lNMocwa9HqrnbaieS9k3z0TP9MaLt7kZ3gT2FcCCBJkyLxKkFh7V6L1aoj/92bO0qQoWo3Cs5+Cu7uIVEmnEk5GIiSHSuGZuPhDsTL7jVWxC9bwN1XMp++Y56JnV8hi61O+EkklxnHukSJYpzwhwPC6VT/pXOKlnwXGtWzCElmRm3+MzVru74vVSEL/cuPMHAFtRPW9D9VzKvnkOeia5uPOesKMuKbPjrOHBSs9g6DyC6378TJw8EONqPnnnBdPtiJfE0BrFCIjFExm0V8/lBkuhei5lxzwTPX+V0jjSuUeZDkCP3fPD6+BYz0bxZjKrB9yl6YDI/GB8PCibjLR528coBlUVIwO0xNnxuu7h/j8GeDSq5w2wXCyF6rmUHePEr55vwzvDzbe9P1S6lCbY9PjReT5FZVv9QFt5rpptfvqdqmzzBOSPyK+H6nkDrAlf4uZ1U0rZGdVzOZPqeQOq51J2T/VczqR63oDquZTd8xz07AV6mUulPDTV8wZUz6XsnmfyZNm/5Xo5qucN+Ovwy4K7/2ZdynPGNv+qXzp6olTPl6N63gZr+uQvFZRS9kH1XM6ket6G6rmUfVM9lzOpnrfBmu4Pt0vZMdVzOZPqeRu6pkvZN9VzOZPqeRu6pkvZN9VzOZPqeRu6pkvZN9VzOZPqeRu6pkvZN9VzOZPqeRu6pkvZN9VzOZPqeRu6pkvZN9VzOZPqeRu6pkvZN9VzOZPqeRu6pkvZN9VzOZPqeRu6pkvZN9VzOZPqeRu6pkvZN9VzOZPqeRu6pkvZN89Bz+/fv+//jXs5qudtqJ5L2TfVczmT6nkbqudS9k31XM6ket6G6rmUfVM9lzOpnrehei5l31TP5Uyq522onkvZN9VzOZPqeRuq51L2TfVczqR63obquZyPJdRVdLU8Ez3/97//XSrloamet6F6Lufz22+/OR+XSrkyqudyJtXzNlTPpeyb6rmcSfW8DdVzKfumei5nUj1vQ/Vcyr55Jnp+8eJFj7ILUT1vQ/Vcyr55Jnru39y+HNXzNlTPpeyb6rmcSfW8DdVzKfvmt99+q57LOVTP21A9l7JvqKt6LudQPW9D9VzKvnn9+nX1XM6het6G6rmUffPnn39Wz+UcqudtqJ5L2Tfv3r2rnss5VM/bUD2Xsm+eyV8N6z9Lcjmq522onkvZJf98gpt/+uknhdevX3uSXm7vi+r5olTPG/Dnn3/2J0Kl7JIXL17Y3ccst/dF9XxRnpGe85V2qWxK9VzKXqmey0NxkUXz119/DREq5Cc8+OWXX046yS0fszBlBTFpweqvV7x+/Trt1oQwnksVKd/hPKnsHGFL/WuQ1otaKmdTPZeyV5xX8fHMXh3myK2eL8dF9Ew/499JJ0WrU5lxFWY7anz79i0rC3alXi1ZyhEtlH/44Yelw+GbqUamVJDQrXSESH3nP+MR+dtvvxlikOQKpiFSYSVdCUeXTCzlh/2PdavnUvZKdveKvW726vmiXOpHLj6zmDJ6VhhOnQ0diFC7gkUseA7g3WHckUpy7QpsOhaH/O7OulUWYAHllm+1UbJyCuYzkh+jo8hL7KvquZQd42CxwQfOt+XG7qieL8ql9MyIJESiBGmBsqCygoWrhRqXuAORKENDTPSsTGBD81BwV2M0r+VuPctJyYes/+hiAgqZhiHcWk1jhe4X2ldG3/GOLeWZk0NmoLrc2B3V80W5oJ4/fvyoEKcqUCObzo+MLOWjzR/V0JVryq7Qou/8gDv0LE9aFISxLKRyd9YzDGEBwa38gFrm5FQ2pSXuFOk1Juzl3B1/f8zKZJZKKWVf5FFh8FDnxhXikKyeL8fD65n5YsoPHz6kqqxgjR4/MlrHCdCFtFTjzmFZH//47pnI6Fm7lrufniFYWkibIYg8T9I8nSRBx0QOBEgoJlXdtSzR5yFb9VzKjnHIOD3wUIfGdVI9X5SLPD0zsXVJz/nwlBV4MVqlRuUl9P/+T1lA9MmR7iY4f0VLI/L1c5h+mPg+etaCYX1X3xLSKC1TJlI5jYNMeKl8IsHnYEp2bPVcyo5xLjmOMJ91+6N6vigX0TOsyz/++MMnR7fKCoQUB/tEscR9+j2ESFoMTYJEjz/1oWcBCsxKn+PLKXdqHAbNQ/bArRTyrVavUR6GXnGJlWdWvhzkZS5NpZTdkeMID/Kd/mq5xCFZBhfU899//63gw8tPpwnp5A+3s4jdpcmxlH3qgvOj6dEYPRN5lB89C9MCA2mcN4PuQRgfG4KPZVYlyOXe7X99+sFXnrHM1uh1cym7xyEzHh72SvV8US6rZ7JkxCjwWM/auUqMdh9zWuJaK1sGV+XcwtCznK5adB+LQ56VniGhAPmNDgGSJE8aM7eTPPjK863CzA261Esp+8Xzw+43e/V8UR5ez4Q3POrL4/jRsZW60rOwPFjHlGkMJz/1pF0qB+7Wcx6vOV5+BQhIIahi6H+F4R525ZmbQccbUkopT5rq+aI8sJ65OS4kIQUM7ypoF4A0uiorzGHBpx61a6f5SHfWs1uQkH1Tzl2p8gMlLW7NtjbWLGOZdRGWOcxoFOZJ98FXnsw1dCllH1TPF+Xh9cx5ER4JMRwOmr4hZX5yTXxQJUvxN0EHKJZc4bN3zUO2zPoqyK+deuNXVwpPWOIPWW/QAsktI2H0fJP08AvQUt32J0O6mIPg/Aj9YamhSyn7wCE8n7flYXn4H27P8Cg1xo4+SO7kSKweWFkwLQnGKiBoPNl+B+mCJe/naF/ijnD3Em4O3o2u6VLK/OO9J4qHjTsO0nIOl9VzOQn33/bgXkq5BC8Ov7Vh67mqkornhNyaYRrfy4Upc+dKn6rzz71UBScmzwBpP+b4u74J+I7uiQV3dERmvlRux8TuE/bgVM+Xo3rehq7pUh4Bxvrp8Cua+bMq+44OCVX1pJ6Z0q04WICv0XoNmPL19A9oCxA8l90VxrvKxs0t6Jhv5IY2JTEClDOcqlsnDwQJBWTcoHryj8a8rjFivjcEwYaeJ/Ow9Ci7HNXzNnRNl/II8B+3Rc8UFUuR1vzjq3knDj0rCKPD5cYBDp5bZj1rn3OS5VwdXwg0Ktv+qUbkgk96V1VjAgTDa4G5LRETUs0jDnwbkOFyf5rWo+xyVM/b0DVdyqWxxSiNnHjLldjsO4ZWViA5pmS+Ibxo210xCUYkTW9SaY+e0zdOFeCu+NmOWlY/ajaQSAkzpQSkrF0Zs3dN3nDCIm9VAbobd4n4hCRj2pnMOFskTGOql8CUepRdiOp5G7qmS7k0thiZsRp1xV5xpHKej1ltFt4IJrzYV3za00W7AC2znhUQR7olwEDaEzkjPoZ2V1mLPMmggITBzCURk8bME0ZJwIzu5ub7gYL8CuNsoWp5LnrUmFWPsgvxXPRsWVumS+WAqtW8VD7neF/NzL1sBplv47bthK7pUh4BerPXWIoFXZVtfNc8ULLvaoeKj1mzfyNRWz4STfkQeIMY7SlLFT3b19o5cnWMaNcIYVCQHzK45tZIrpAZBnMWdodo6VkSBa8RaYTy3afZ+Ri3R9mFeC56ttazeYJNaDPMG2BgqdkJuWVlJyzYgap2QiJhaWbPiLQT0hF2tWDVbO9juqZLuTR2pV0fP0V+9qOtlx2q0eZd2St6hu2si4JrCvG6wpBo9Oyu9txSlln+CHU+K7QnwBDuOh+cAFIlTPnkgZDHYq9CdwVIvtybcDdWFin5zRn06f8mMJAkrrO2HxAv/OTMy/k8Cz1bqZapq42RL8vZV2NVrZaXu3G5giWuoOObN28UbI+biE9YmtZ9ypKojq2rmo2X6oqu6VIujf1r59pocZuC/Zgdmupqh8Zno1FMtn8kOrY2SD0+hoNFi6tDw2kQ33Nk+oZkJkjzUdU3d1W1J/N8IChrlNDdTCMo3wz5qTHZYDgvNvM0AZPxEhRUc5S5ddtZdA4ZYp55eUCuQs8+XevMJ50vekvrAyGtpWy95uuwjWEs16DdylY4ucKseFOCvj///LOqDMu9Azq6KyHM3Eq1DaTKK3J3iTvithFLKQ+FDWujRWmjEBcq29TzDk2wRjH2su0pxuEwtraTBNm2dj3tJTjdlbUI0MUQ+uqYW0iku6kaSLDDh2K1u5uAzC0zEZA5q6ZFsILITF41k8lUXTPPMa4W1Qc/UWe8ogy91MuDchV6zhfPm7X//v2DLyY57aWUFWye6DYtUDZ6yrYHrHuoZjPI4KqXgrWYyJD9IycUbAZhyY879oaOXdOlPAJ2t+2Zwth3NqntmW0+w23Ztja7eLtYFweUKuSZuzgfBKcsMseImJwDaQ8yOB+GOONahQwnj4L8uTvIzJ1I5qOLMGWZV6eQvsKgPXMYqF5az6tXWh6Qq9BzluBSOSLfCjF8ZjmuWoJ2pOxuCvMCyspeLdmxr1K2bcbuip4Fq758+VLY2I3BnhnJDeTueCHmNs6CY+64VUp5QKJYh4ztOeRq287VgQNEuy1vs+fp1iGw3DvYbu5yh55XmaPnpXKk5xwa4+waZObSQneZFfJalogJt6QSNk6k8VpSvQQ5HpdKeWi+oGdvvaXg8+YSH78PQzXf8tJikaVxfEjWhHgtoz0ZRkx8qSoySawha06jW1mjIrVrsQGSCvGZRsGqAhREWtyqCcvahWWaVHKKVEjftAsYuy6pUoZRxpoWI6H8g3mPIdNO2d2x0yCPuykf41ZeTinlotjv9ri9maPGgUCWTgBXVSfYvBMFaxQPVXfHBofG2bsrPcuZQylHgXKOERzr2V0z0StzUNY4J4dqpgHxOVukmqcU3NJdEhgo4+YIGnN4cIxlVkulXIAvPz37jH0G8Zky0pj1nXKWY9aBpQON//77r4Xi1hwctFt5I1UWtEJa0ghhOs7LyxxMZoS5lSGMGN26WtACtFg9WlyzAUxANiRMo47K4udFpu94LcLSMTOxdRUSFrLNhCVSR8Olqpe7S9wRbo1XUUq5BDasc8CezYmkJafBqNqkqgJEpqqsBTkcbGot2oOyFu3uKkOkKiJaMcnsBLDHxzZ3nc8x7ZnDMUvEAWHJ6ZBRcARpybhLxAFjaRn5kyeRes3jPiB5Jy+UvIQv6xlDdQMrxsefNQ2rIZLLZ6aa9khXQbv40TjrEDHfUplYrUIYJdtmoMVkxgxdlVNYtUAhesZY0F/UM3R8+fLlST27Kzhz0NFwylq0j5d8jFvjXSqlXIjZH3acvZljYWBTj8MnARgtyg6BlKE6EsbQI1sOopS/iJz32f7C5vkHLasz8OS48h+Ors8exx8Kc3CQXih5GdxLz8cfv3VDRT4eBQiI7WYXrvBxZikz02rNndSzlpXFcWw1MUY3aJKPCQhbtUBhlE07BftqXmc6emnJ6UXdLPBP/8y9Kc16TnXMx57Rcewc7dVzKWUfOO5yfjrlHF/HJ3Z5cL5Rz/nmuPoSB9697ZOjyRh6PL8OTuoZx3o2k5VKMw2LZiVj9l21QOF4dKMMVSN6hvYkN6IXpRwfL3GHIeZsIvUa70n1XErZDc6rHJU5V8sj8AU9E5IPg0tcqXdINB8VV6XdVTVmiszS6IlzflAmM7dmv0KvkX9pOuRnYrbTiGEyEzBQ0looyrJplEFZWGYiUvfRYp7CdElhpILGjJJG6BI3azSQFhk0/v3338pDz1q8HLdSxdCzW/q6ZVbLvSPcErZUSikbMb5P3xOHxlL6SpwJt235r835zXMoT4sv65miBrONLDWCXG4cbmXxjXaPqqtlFOHNjYJJ7pDgBk7NXQXBdk5YLevkgVuqhtZiOGHIs7JCAly58JDmpmxuhxw3Sbg2uJXGIMY0TGxuf/PmjRYCTtUoGWggWCodMz2R1XMp14yjI1+pl/rE6uwKGu3cfFMf2MhOBrs+1RQ0+oqf00DVkWKg+fwcjHND2ZHi+UHYQGaHYSKDVOYw4lWX0MPjkGvCyg641w+3L8qlLWV7jJ22Gku79T321Yxbq12hr9U/lHxy2nPjbZlD9VzKttiAvMiarvZjoEOCTGHeodn76fLx48el9UAah+NZWSRNyqAxVnaYiDm55ROTM0ohjyiDtCQyzKkMYWJL6OEni+Ong2UHbK/n54n9f3KvllIeBzKLO+3EoAyGWz1Px6AxtMKbN2+yf+NyBdUldHoadtcQkauWYVnPu/MXd2XdXQVI7u5y48Bxy9CzdkOMBwaY4R2PBGcis3Fx/P6UC1E9b0P1XMqGECeZ0ZudSHgD7mHEleS0i4wR3U2kKjWu9MxbWjgsIo/PFNLLiGk0etKmrNGtRCpoT/c0Dj2LRMJcU4iezcEXi2g+wRfCZDIr16WpXIzqeRuq51K2gsniPG7jXVVWy8+HFThvJTlh2a25qyDSlaJWetbO0JErjKJLqgng19WjZ562JdE3XTTqlS8EGWgmUk9Ztug5SR7zVPHaa+hLUz1vQ/VcyuZwW544B+zIkQpLxMGULEiKICR387dEReq70jPSLtJVmL4HkS3/JrGWYz3LKUy8goRGjINvJnQY3XWJ/qRnXym0y6bXYWo3/06U9tj6EfByjG62S71cgOp5G+yl6rmUbSEzSotmXDkvT7qzDme0u7tUDrt4pWduZk2PvPJIqKAafSImnvWszNyxcp6edcxkTCxeR84KVzNUFeaqbFwkJ4ylPZkfAe+G0Y+f78tDUT1vg2VdPZeyLZExaPXDgZN6/vvvv3/88cc//viDYt1VsH/p0HWl599//z1hWv7zn/8oSChABsa9Me2kT5G//vrry5cvmfjff/+l2++//971f/7nf+ThWgUZ3BL88eNHka9evUp8Msgpv8lLq4tJPvKpYj5YKuWhqZ63oXouZVvy9JlHQBbUkmfW48fBPJgqECE927muGsWv9Azt7kqYdvpMu8JtyXMa5Dk7OZd7Duhb/uZ2ytFzCiK9orQ/GkYcL7A8ONXzNmRDLpVSyuPCK5QWWboSnupJfWLo2Z5NTPScuys9u6sqzBOtsDyIp4vGY5lF5xzsrqpes2Vza6kcONazeC0GNcnVc/+lqZ4vSvW8DdVzKRsyNEacBGM/Up0rF/Llam8OPefpmUHpcOg5z98pB7eSUC+RhKrqKlJV4+irYHQ5E2Dc5NeODDf0nEbxs551BE3ma4H4x/Rl9XxRqudtsJGq51I2gcmYklqYMk+rWrRr1MJwMStfJt5W5UWF+PLvv//WRVWjGC228yFwyZC+ekmrbIjkHwHDuHEz3SZh9ByRB1XXBBtOKi3jp9+Z50iuu8xJ9TgYq3q+HNXzNlTPpWwF4VEdqw2xzaSdAo/vxn9L5RMRfMrpmzKUZzef5NDjsy7zyTDfCnPAKvjxqZ4vSvW8DdVzKeWpUz1flOp5G6rnUspTp3q+KNXzNlTPpZSnTvV8UarnbaieSylPner5olTP21A9l1KeOtXzRamet6F6LqU8darni1I9b0P1XEp56lTPF6V63obquZTy1KmeL0r1vA3VcynlqVM9X5TqeQP+PPzTu9VzKTvm7du38/89tUuq54tSPW8APf/2iP9reinl8bHHfzn8Rxo7pnq+KNXzBtDz7vdtKc+c56Bnr7F6vhzV8wbQ83eH/9R9qZdSdgd1ebhcKjvlp8N/OL1UykNTPW9A9VzK7qHn8V9S7pXq+aJUzxvQP3suZfc8hx9uV88XpXregOq5lN3Tp+dyJtXzBtCzZb1USil7hJ7t9KWyU6rni1I9b0CfnkvZPdVzOZPqeQOq51J2T/V8Pn/99ddSOrCq7p7qeQOq51J2z3PQMzff88/pmHWW6/v377/4qyv//PPPDz/8kF9Oe/funXjV169f39bRu/327Vu9DLSKuW04kTIvlcOI9//rAmZy6T+jrJ43oHouZfc49/uPeg68G999t+jGAag83pxjcdIk8wnL07nIjPLLL7/MNl0hjL8Vfv/9d5FEO3jx4oWcCZvJraXy+SS/yBjuclTPG2ChWD23fQcspewAT2a7/8Wq++g5B90wnyqrkSK9saPu2ldJxAw9C/Ywk2di5XFsalkdofGlxl9//XUlY3keUM9SwZzvns/5VM/bMH+upZT9UT2DtJx1+RlyzEeiCvmRNdxVXT0T60XJ3j3udFdBWKq6uwXt/J143bWIESDs+++/52Pq1Sge2uVM8MxX6dmrkFCMIeTP3DKEPMquS+gDUT1vQ/Vcyr6pnoOAaJXAxHtPIkUHYKy2cjO0iIy5QYHeTAWp9EoqLUv0gbgzaQXkrhZoGSJfYQLz/O/Wsym5O75YGGX+fA1dPe+E6rmUfVM9B+9DnmW5jf+cex5kySz29RadfK6V1q0R5qrMtW7dJlG+jCP/85//5HSVxNAKhljpPEg7O9WIMp+MhPb5rrGMmDIy9FJ5IKrnbaieS9k3zvHqOTjrhlNTdgByLWtqVE3YgPbykJo/M1YmP2F5PxVODqqXbGIgHqpQ0KKwxE0kbHw/yFeB2yzrA43skW8bqmaSk1ye6nknVM+l7JvqeeCtYC8+Y1BKi5gdgNBIt/IMR5JccuYveLulEPN5P6VyeM6PrZAh+d1SffnyZbLJM4R6Er3icmkznMyqy+3PGR9oCqZk/kZEvkBUzzvBJ+qjXSqllN0xTvMdcx89x2HeCvK7+aPgw9/qIjPM5fEMTZPR6rilkFHSXcvqz5Il1+5qINU3b96YGDRqie+Pz9ukUjAWTMBV1XA+u0PIZ2gcJtZ3NGZ6GqvnnVA9l7Jvqmd4E+It0NhocU2AxiHmQdJGk8oKeRrOA+5t76oH3+g53pVBX+SH3uPpPDh+h1ATgDyUx/SHqM8wGTGGWE1YvDzV836onkvZN07z6lkAeznrKI3btChHnzkANR7rGXn3PM7eaPOTXJNE35VrQ/Sso4KYpfWT1FddItRxCBtopI2GzTy3Bml3Xeqfo3v1vBOq51L2TQSzVHbKF/WMHHRDz2DBPKdC4x16dk5yHncaRZgqtc8qnYn19eJjBXMLeTie401J2BCt+PRKFTH93ALx+en3jFQZpXreD9VzKfumep6Z9Qzmi2tP6lkLEXr34lTBwoZQnZwxtNEP4TdoEYPMx10tytBR+9Cz7sYdbubUkXkgxvTmXjj5gQpIfjz4kV49b0P1XMq+qZ5nGHc8MQfvD4liqX8iz7KzLxn0+LRctegl/3h+nR9kGdStk+etUah6qXyOXvMccMcH6tb8XeGhqJ63oXouZd/ccZrvhvvruXwD1fM2VM+l7JvquZxJ9bwN1XMp+6Z6LmdSPW9D9VzKvnkOen77+X8pUR6W6nkbqudS9s1z0HP+dvRSKQ9N9bwN1XMp++Y56Pn169f31PPqt6f+Ofyb20vldvRa/fLxF5H2uIvhvjaPj28p3Q/xx78hdibV8zZUz6Xsm+eg53v+2bOw7777bhYkX774/BeXj0kv+R2VM/kdqmN9itfo7vxbVSGp9E01v2o1c/wb2FpWH5/kmY/8yO9ep4zD74id/r80vpnqeRuq51L2zXPQMxc6ythuqd9CZJwTTznoSHUK3iiCTORAoy4USJkzIrUrCFhCD5hJ/MqUf/zxBx/LrD3fAFznz8JMDslu/ofKGDdV7aMjPUuV+GDE+dw2E9WUIdUq/nyq522onkvZN89Bz+CkO04z7d4H5mNBkd4QwaTrqkUhcOEqg5abp9HD4ykRugbtsxQHkSXRuv78889yGotojcLcpOvWEvoJI8qf5FEytCS/gXRMY1h9oCs956l6qTwQ1fM2+Fyr51J2zDPRM2jJgTYMN6PRm0CWNKkQDUOZ3gRQ4HHHPM5yql6C54C4fKl8QoD2hEW3o/02PUury5gDK4tRzhcChZN6dlcek4ey5Alz9Q5Uzzuhei5l3zwfPYO02G6pHMGCTOYNSTUycwBq0b4SJ7S4S3vuRnsheY7jtXur+RUKr169ynySwVX73CtuTvKgTLfy3K1nYVKxPvIqUoaE6fiAVM/b4HOtnkvZMc9Hz/EWXS31I+LCPHpC2ZXMtNzxFkWuQ+pBKn2XyidEOk7hmds0BPAl17rKcKxnRPYQoKyvzLrfrWcxS+Xoh9t6peMDUj1vQ/Vcyr55Jnr2MintDjdzpAAW5EiFWM01bqZA5ZWDQ/QMYZEf9DrWM2SIpM3kzZs3yoLv0HNwS8KUcyCr6qhwUs/5QEVCwnGMu2aSh8AHo3rehuq5lH3jcN+9nuPmu7UUE9PkfOJxm8ZIzq3bMjgnZz2nl8bl9oS7bnn89YD7448/KuM2PWtXRR7rFcg4Z7JeUom5Tc9SGQXKuihol2d0fECq522onkvZN853LJWdQmzsu1ROQYTOOjGznvXiNlbjtjvc7O5BssvjsnjBqtoTMCNJ8njP//jjD2WRt+nZTLRAsLCUcbeetc/ft7yo+fO9Gb563gfVcyn75vh83x+eGilwqZzC3Zhv6DmPqspx82zNGV2Y0hsYwfNinlBve0uHHSUfejZKxpLt5ECSC1sqB4wy8qzG0jJ/M6BnE1sqh9eVjg9I9bwN1XMp+8bhfptLdsMX9RyGnr0hynGzM5DwePf4XfKgTHXJ7CpMd9zxpC4+dhSczLN3b9Pz+IrghZiJiQW3ZJgnplFaE1vqBz1nxGC4ufogVM/bUD2Xsm8c/dVzGHpW9p4QIfkN+7q1EptGIoxTac8oyjE69FXGPLS0ebfdffPmTbqkPR1nPYtJKsm1Q1imlx+5m49ZJWEg4zkD0mIOxpLq+FWcT/W8DT7O6rmUHbN6/NolX6vniJn/lhsHVLUvlQM8JzP5UeDqnEywbK7zs+xA34g5mF7cOTcqJ/MqeXBLPHPPL02SpfQJH+6YQKZ6cj7nUD1vQ/Vcyr6pnme+Sl0Ue8+0F8LhPOt8K6rnDbBSfTWrnkvZMdVzOZPqeQNs2vyoZ6mXUnZH9VzOpHregOq5lN1TPZczqZ43oHouZfdUz+VMqucNqJ5L2T3V8/lI/sXfVhKz17O0et6A6rmU3VM9n0S80++e74zgF4d/r3upnyIx+YvWDtUZjQ/+y06PSfW8AdVzKbunep6JL1PObz+njNVJqCrnu3fv/nv4ZzLzW85Eq+z9VNA+fnM6jVpyopqPW+nrqlFLIp8i1fMGVM+l7J7qeYZlwabwziBlHtW+BB1wS1oa5lfXFfk3Q1wTrG/m4FZaZjLiUnmCVM8bUD2Xsntopnoe/HD4f6uWyoTG+R/kYllno/fN8Rj15pw8CP03d13nPGK0KJiG8o3A379/+fJlfqbN/aqHwCdJ9bwB1XMpu4c2qufBPfXsVIxQmfXF9J9MaBfp2NQ4v6tDzyLdMhn8+uuvmVX1XL6a6rmU3VM9z9xTz4FTvXvHf6vr3eF/gJ4Pz5We0+jpOQHVc/lqbNrxQ5tSyi7ZvZ7pkynvr2cqJdEVGh2GS9ABLYKX22/f8jcUhBEw4yovoZOeYTJuqepePZdvxKYdS6pcMx8/fjz5LUr7v//+a+crLE2fGu/g77//vvvXPMQYTp457R18+PDhttNHqjE9Ob/ZE/piqZQvwVWvDuRnszyhYLMTzBKxIyyq77///j56thq9GycXYdb8UjlUYd2Oxp9//tn7mfIxQ8+6GOJm53z8qCXdq+fy1VTPTwU2dcLGqe/evXPIOg4O3+ZvCphPHN/cBWu5DfHOiyX6CMecr/8CnCzz88EKd6VK2TTMKuUVYpxWOaSEmdhoN43V8vMCjXgSkeNZpHwRb5S33ee44raP6UljLd3zHOPIbz7xvHXW4VI5YixmMd5n2wdjxSpXz+XruP+yLpvjk7LJFUjOEXDH4++Nze78LQ4nRVKdxIHiWMk5nmMlzyWu0lozPx1wy5SGd+/Qs2CzlUcYZyg4ywyh+1fZ4j6PR2XgTY6SB3vd7Pc8x6xVS/Gbv6BY8Hdsq6Fno4wh/ud//ieF7KOUnyLV8wZUz0+L4cI75IqDnW/OEfHzoeDjjuE0zhmUHS7OlKCvozyG1u7u6quAluQfpLuC/ClAjNXlQJTtxucH6DkJ81pcvZzEl4cln+PM/KHviXueY2LuE3YSK1bfeZ1rsUcMnYCh5xXZLN786rl8HdXzE2IozVZ3LlBddr5zgfNmg2qEgg9XZKoYB3T6HmJvcEuSpXJQpl5p+Xjqz57Hs3Kep5PZWArSSp55hnmN6ShzysGZZfL3fCwWJsOcvNyNz2XmSRviDu5zjlmx1t49VxpsKCszy/skWfxDzyZwcg7yxNz3H/oKqZ43oHq+fiJLOF7jVAXnQu7CIeLuUjlwOD0WPc/eHQf0Ss+WwUrPo6ND7XiFDD0PZM7pc7yikty4CjBbwSaskLLJ6AsF7bpLHpG7K0YBEb9U82svd+Pt8h4O8hntDyvWK737e9v8/bV8LdXzBjgujw/fcp04gPJhxWoElhNHdWUsLox9o7Q0YkSOVEGYW1qCuzK4uiX++Fw7XjaCNSo4Io04H5SUIDmSR1i+IhyMfPp/AaJnMeKlykB3n7zlNryH3vm4Gd7w5cbusFR2/Oo2p3regOPDtFwtjBXXEjMd+tSiUsfuseQcyq5OZ3fZjhQh3mOodqm0j899Dstjq0aZlV+9ehWbzuiYmIHu+qbsVjI7LvP9YNzVrqzRhLW4zh0Hho7Ox0vWZfW8Xu6Jt86bDO/q0rRHqueLUj1vwzhMy5XDVfmwXIft8mykZT6bRM56di4Tc9yMBGgfn3vCkoEFE5NH6pNneixLrshDfIZDUiWzskEz7SRPx9nHOo5pBBMQo5eyqwwKYiRRXgWXL5L3c/W274/q+aJUz9vg1OuR9yTgKmcQ50WZTltVgnQqKQwFQmN8qUU5jfBB57M+1vPQMN3GiPpKMuyY5K7xt+66JLkYvRRgVnPmMI5O7e7mUTgMr4eYfojEPAWnLINxDbpKXu7G2+V9867mI9gr1fNFqZ63oXp+KlCgMyj2UvbB0VtuRXt54sTQ822s9CyeYmWTmf+S1mEnycuXL5U5Mso0aL4TjL6CpUoZ99Gz0RVwPM+8uqXyuZ4hfn6Z5Z74cMfXr71SPV+U6nkbqufrx7njhHUA5ZxVyI+UxweXwqhGY6q3EacqJF5O2YxiMcR/KRvlxx9/zNeCk2efXqZEqEkLNtWy3D5MCVKlu/KsZ8x6NpCZp5y75mnotARTrZ7LMdXzRamet+F56vlpvWTeoqXMmREpkNV4zmfHhVDO1V0x0TO33UYyJKFgR9t4Q8gvLcJUP3z4YBTByTxjVgYds1IWhlmfBjITjQlzVdVxnkYiV4gRCTFLUym3Uz1flOp5Gx5Zz87x1S6af5j5zeQlOMpjkVTnp7EVNnMsYnQF10GqCQsmzI6ZtmvCBrcNcVG8zGX4iWFQk7x7Vt6fESxylCHP+CowWH1kmDMMtKwahc0TUx1GD+4upSN8ED6mVcJSTlI9X5TqeRseWc920ep5KH+6ucKpbbM5oJGHOce668zYjWJiFK/Fac7K7qq6KidmhYe2CCzPfFFIkAoJCxo9xrkq66XsyU/mTEk1YaWUrZgPhPLg9IzbhsfUcwy60vPxc974sactp0CWsaPtpzHang2qRQDFSu6ajSo+wUvQ50TPYih2JePjlpWe9U07TNVwS6WUshHV80WpnrfhMfXMnTzHuCRHgRHbsZ6DAPJTiHczyagxWr0JOiBDrO+uXuO5VtmO1TEB2b0Z3TRm3DKWXsSvOvRsIMESplF+hVnPst32gF5KeTSq54tSPW/DY+o5msxwPBc9uzLc8RxmPQuOdCNg15WeaVJMEAmFmFikclINZHBLwd0hY2F8bCarfX7y6ZnIvZYMd4gq5cHId8qlcg/skaX0OXcsTrvDGl4q92BMSQFpvB6q54tSPW/DY+qZCIdWiS2nw6tXryLXcViYj50W80Wx9l4mGTWunp4h0i1dvBzBuijkzCLm40NKsEa3hEXPGdEcMnrCQvQsm3Zd9FVAVK3lPm9g0uoSzZdyB9kRllZW2sDicUshYR8/fkx5rPYVWdJL5XOy+O+zdIOlm11g631Vx8eher4o1fM2PNpOs7HJLFp1ZCin3bZ3deiYSVpsMwHOAo0KAhjUwQS9XJ0ss56JVtWryGuxURWE6e6uvis9J48YmRMsQJgRVaVynQ+1TEajhIZW1l287oJV5+AV5mM4YUYRiXnmpRyTNWZNWlfBYrP8BsNDbqmOla/FYnNVTow8Q9tZitBiiIwig2WcRitz3ilJOGPZJ4mO8h8HbMj8tpQHp3rehrGxL40jxongajieizsRPcO2TyE4AsQrODj0ctUljQqjV84aBQeNuzk+nDL2akToBSYgJAmSQcwYd3xjGOho2ydefi16CXNNdxO7Tbc574aVB7fFlwJLLuskax5WmnKYVzLevHmTL5rib7z96X8fscayzFRHFy0ik/MYo+RL55Cc+IyYAHtKNldleYzlej1GNJ/q+XJUz9tgjz2Cng1ht7va7Qo5RHIrnjtmHEZ6ZePlBDnc/AztQ7pejph0NFC6jBc4zK3RLe12tRbtsXseJsaJpipzbsXiI6HppX11KPz99985uW5mcwqzWkI/R7bHxMsx85TvPtcSc514M73VJ8kHd5LlhV0fPgiLx/Iz+SzUgc/rePG8evXKLVvJmswHiizORI7Fn+6uy1swkbEMOrZkyPZJTiTD3IKxszaner4o1fM2OMgeeY8Z0T5fKrfrOYeRg8PGEwOHTrSqxZmyxB387Za0ThB3R3J9lQWnOpM8ziZ3k01ZfFJhiTswjjZlvfRVEJMZHkL+P1q0340Mx+SUfHC8LXmNyq5GN8NM3qCH6ZyezyB5NiSTD+Zjwt4reBVL69fgE1w+qqskm9E88+bPeOFWacJgQdKzgmC3IlfviRfompWp103oAZl1ObwHJ1iCJrzD83DwQaSFCK2rlc63xdyq58tRPW/DI+vZAZGDZgz6+++/pzDwRd55IcwRI96uc+Lk0NEx5fngECDYC0mvcdY4PnKUpzojTJIcQOLnmNhrqRwwB8mz+fXKXb0kdygcv3uJcfckj/yGl6eI1WUVWSczFs9Kz2Jg9bqliwWvavuc1PPdyDx+aBSklURayCO5FuSWEU0pkddA9XxRqudteExbODvyBdxBYMM7ROzzDx8+2FfINBTE5HBZofs4bo7nnCPDy8kpFqemujp3DCGPUaDq9BF2mMINZoVEwkBSSeKqag4ZRV+3kkSG4/loMa67xtJlcPKllTJjRWW1WDyHVXyD6qxn/Pvvv1YscQqzPi05Ve0W8LGeD6v75puu5DbLXECGyCIPMrhrDWdnuaVgPbuVNZ+wK8GU8trLJaiet8GePFbLg2Pn2O0YY9ntsdePP/54OIhO/BRasFMgxwdyOihodADlqBKT7/WS53BRkM3ripXFqwq4yfjpkToB2c/CnGKuEsIQMiQYyTzeJdkES5K7MpjM3UeDW0kriXEzq1LuwErOKrX8sgKzjC2kJeLAy5cvx7rKuk3MsZ6t3pvFfeguYb6AqoofW3JG4xguYQr5HqCv68leG3L3HixnUj1vwxDPRbFz5i/mMx8/frTtcXIaJ7fcHMl2Tq7R4ihx+swtkFzLUjmcfVpGZl1Wd1czEZxzCqvgr0La215mKTO+89mY+dZoQbpGwKvFk6UYl7uFOPXk0/NA5qxn13RM+4zG4eCEjUbl8eh8JUb0DphY9Xw5qudteBw9H2NHLaUHZZPXUsoDQn7kaiXnBzPsaJMeuxnRs+98Kz1rzw9pjvUcl2f3rfSsMe0p52QQ4/uuskbV6FljYr75q+rD4h3IzwPKhaiet8HGG9+FSynbEh2m7HHQ9qTDWaIzsaMw1qQo1eHOcKxn4h8mO9bz+JMXZ4JsMgsWk3Zl5Efo7ipfiZ5NCUulXIDqeRtsPF/Ml0op5QrgWoIkV6pWIEJlLsQwKGLiYVnXPObSaoJXetYi1VL51DEmhnMgT8w8reN4kh4tQ4HmoBpJp2VDTMa0+5Pti1I9b4ZdN2/a8lCcfOIp5TaiVbKxJRVm5eQnyaSI8XgdhEWrS/1A7D6EqruY423ObdJCvLTxvWAkILi1aoG+x42PiZdsYnXzI1A9b4bFbZXbul3lD0v/Ilj5KqyWB1ww83be01L0ncDXiDxUOLh6aj0C1fPGWOi+blvxlr7NvLSWUsrV4EtGniUcUxXzo1E9b4+vpa8Pf2t0/EyslFLKM6d6LqWUUq6O6rmUUkq5OqrnUkop5eqonksppZSro3oupZRSro7quZRSSrk6qudSSinl6qieSymllKujei6llFKujuq5lFJKuTqq51JKKeXqqJ5LKaWUq6N6LqWUUq6O6rmUUkq5OqrnUkop5eqonksppZSro3oupZRSro7quZRSSrk6qudSSinl6qieSymllKujei6llFKujuq5lCfP/x5YKl/JN3e8P//8889qlFG9mfflJ1DKU6R6Ljvk9evXf/31FyvcQWKWDptiMktpKh/muPDu3bs03sZvv/32ww8/iFzq9+bPP//UMY7E0vrQGOXFixfz9L777ru8qJ9++um///1vGu/A3N6/fz9n8Nl51ZebcymbUz2XHeLQ/+WXXwgYxEAGWpzvGhWC9vuI4REwk7cH+CYae/nypZmnhT7Nf+UhVc4LKesoOAKjrjQm+A7SUTBZGuie31eMkoEGGW7VOHBLcgX5lb0ur8gLVPZB+FC0uKomXp60eEV5E/KRxejCDOfTPH5bStkT1XPZIY7yoV6nPDc40x36rki7mFGe4YN0v002wV1hgpf6GchDNgrxkII5H+7cQGk8tJoqUWkUD8EYZTNXcE3A0uEWJB9W5kvdh6GNqMXEQhqDql5DjVG7mevuqtfqrfv3339NRmaRmWriFVyV4+DVm2kCeeHIt5a0J4PR0zGNpeyP6rnsEBpwoCvkEI+oaIMMlBXc4pgUBoK1CND9i49lSeu61M+AYzITk4wIf/3118OdBaOs5uOFjO8fs9jyDK0Q4Q2l6e5FnUQXwks5L2q8LZKozi0hb2bKusjAoMpGybcWjcPoWvKu6iVSmaddDeeujl7I+E6wQq/kkXO8FvGqCkZZTayUPVE9lx3iTHeys4UC9zjHCQBRUVQXMSQ+uKXXbapYIYlgmZf6GbAgIra0zHrWbtqZ80A1vdwyDR1TVY47FVavTpdj8p6IXOoH4loYWh6s3hNvVFqM5W5EO5BBo7RDqPk4XJM/c9bXhA2dqnKCg5wjTIyCQbV4w1Wr5/IcqJ7LDhk+iCQc/Y5yZUf8EnGk57htbvki4h9KzyRktrOeY69g5qyWWyu0m8MQqilFlrfFH3OH5CQxJTlnPSvnbVRwy9zSPhOhjrQfPnxQ9QJT1cWLSjkct4T08uoUhuyr5/JMqJ7LDomenfg501PFrJlZz+MxMc9qGC65A/EPpWdw4Xgq/eLTM0SSk47mMFxuStRl/rqsHmohlTyuQbwkd0tOZjnn903yfBuQ3635G88gb/j4tvHmzRtVU5LNBBT0yoRlyxDKCR4kScYySRNOu9nqksbqueyY6rnskJzsUZrzPWLIgyANkI1b7MINiXf0xx8Rs3Nf9YuGFiNyqZzBwVM3cjJibDfr2WzHa1lh2tq9rmFikV6CRrg7a1XZm6DRnL1SY0Ejw932KiQRKefIo0XwF/Wc93N0fPXqlbLXmHFd83oHWo6/THghUDCc7nKmPWQmqzyl7InquewQDnCgu5KHI97Rz0zxsTPdLdc8ZSbeKa9xVNN9PPzdRvIslTMw+tCMCfMlPbMRLcG02YuNEgC30gVel7tmHswHMkBVNhmWbp/QS5el8smyS+VzVpaFgvyZjPxuzakG6ehWnKqLqpl4Y6N8EzPzvNjkSeTAJMdAOqZ7bmnUKznnt6WUnVE9lx0Sv+ZhMS0O9FjBge7oFwBiyN0YYjaZ0z96lkQ56J67QReNS+UMzMf0TNXcJDQiPUdgULjDQwlTkEGeeFH5Dm+t9CxS9fjhFcmGoeeMknK8PqcaCHMrb5ck+eH2eHu92waVM++wclrGhyXSixovQbuqd2YMDS1GWSql7JHqueyQ6Dnnu6uqgkN/PtBVh55jmnguuBV5xJ1h1gN0eUA98xwkZKP5h9vza1kRaaWskAnnq0aEl1srDLdy6nD8imM9z69X4+ruIHPwopQNZyaq3nCvRTXkxS6VT9Vh6GPcTUJIqMvwfSm7pHouO4QGxo+mo2GnP0OMRsx6dtw7/fO0F0SOu7ch4ayrbyZ+MofxCPtFPasSqhcVq5mtGKQFqXoJxw4zlgAizBcO14h2jK4xhZWepVq9XnNwd6X2THh+qz9+/Khlnkky62iGq5cWjOhupje/IoX0Ujj5xF/Kbqieyw45Vhof5Ewf7lFFysgDNBmIpBYeUljunSKCEXbSLl+FKY1ZhWM905VCWgQbd9VFy3i4RF4vk41HWy26aIzkvEYveUxepEYGhXIa8xqRoeU/NmI6+majo3gx4ueZ4FjPppF3WMcvvtVh9QJL2T3Vc9khURpzrOAkcAPNpLB0OMAfbJH2LwojkVC4j13ugHWGa9kUP//8s5egMULNQPwU10aEzJrg4K4XuFQOfx/bNTmh7A0RI5vMJyese0SroKrLeMcgoe6JXCHSPJe4T/9A6cysZ9POQAlT1SWTn78uHCNmpWfBx18XStkN1XPZIY7+8Sx4Gzy00vNWDKXxTfRpescGnVsEiPQS7oAyl+hDZl3ukF+Q0/uGk5Hf/C2EnuWkUg42MZadUxnLVDPhjCtSeYW+prdUDuQdmL+FlLInqueyQxzZX3yuYoJv9s3D8kVrPibeE98PHnxK0sLn4ro0lVLupHoupZRSro7quZRSSrk6qudSSinl6qieSymllKujei6llFKujuq5lFJKuTqq51JKKeXqqJ5LKaWUK+P//u//ATWRHw6iy9EyAAAAAElFTkSuQmCC)

  • 非阻塞式I/O

进 程 反 复 调 用  r “ v 缸 “ 等 待  返 回 成 劝 指 示  ( 轮 询 )  应 用 遭 程  recvErcn  z-ecvfrm  处 理 数 撫 报  系 统 调 用  返 回 成 功 指 小  无 数 据 报 准 备 好  无 数 据 报 备 好  无 数 据 报 准 备 好  数 据 报 推 备 好  复 制 数 据 报  复 制 完 成  等 待 数 据  将 数 据 从 内  核 复 制 到 用  户 窄 间  6 . 2 非 限 式 耵 O 模 型

when an I/O operation that I request cannot be completed without putting the process to sleep, do not put the process to sleep, but return an error instead.

  • I/O复用

应 用 进 程  select  进 程 受 阻 于 select  调 用 等 待 可 能 多  个 套 接 字 中 的 任 一  个 变 为 可 读  数 据 复 制 到 应 用  冲 区 期 间 进 程 阻 塞  处 理 数 据 报  系 统 调 用  返 回 可 读 条 件  系 统 调 用  返 回 成 功 指 示  图 6 . 3 1/O 复 用 模 型  内 核  无 数 据 报 准 备 好  等 待 数 据  数 据 报 准 各 好  复 制 数 据 报  将 数 据 从 内  核 复 制 到 用  户 空 间  复 制 完 成

调用select或poll,阻塞在这两个系统调用中的某一个上,而不是阻塞在真正的I/O系统调用之上。等数据报套接字变为可读,select返回套接字之后recvfrom调用并复制。

  • 信号驱动式I/O

应 用 进 程  建 立 SIGTO 的  信 号 处 理 程 序  进 程 继 续 执 行  数 据 复 制 到 应 用 缓  冲 区 期 间 进 程 阻 塞  处 理 数 据 报  s 地 茈 “ on 系 统 训 用  返 回  递 交 s 还 10  系 统 调 用  返 回 成 功 指 示  图 6 . 4 信 号 驱 动 式 I ℃ 模 型  内 柩  等 待 数 据  数 据 报 准 各 好  复 制 数 据 报  将 数 据 从 内  核 复 制 到 用  户 空 间  复 制 完 成

让内核在描述符就绪时发送SIGIO信号通知,之后进程捕获信号进行IO处理

  • 异步I/O

Machine generated alternative text: i ead  a io_readrtl

异步I/O函数告知内核处理操作,在完成之后通知进程。

  • 各种I/O模型的比较

    • 同步I/O操作导致进程阻塞,知道I/O操作完成。
    • 异步I/O操作不导致进程阻塞

Machine generated alternative text: I/OZHI  46-6

  • select

允许进程指示内核等待多个事件中的任何一个发生,并只在有一个或多个时间发生或经历一段指定的时间后才唤醒它。

1
2
3
4
5
#include <sys/select.h>
#include <sys/time.h>
int select (int maxfdp1, fd_set *readset, fd_set *writeset, fd_set *exceptset,
const struct timeval *timeout);
//Returns: positive count of ready descriptors, 0 on timeout, –1 on error

maxfdp1指定待测试的描述符个数,值是待测试的最大描述符加1,因为描述符从0开始的。

1
2
3
4
void FD_ZERO(fd_set *fdset); /* clear all bits in fdset */
void FD_SET(int fd, fd_set *fdset); /* turn on the bit for fd in fdset */
void FD_CLR(int fd, fd_set *fdset); /* turn off the bit for fd in fdset */
int FD_ISSET(int fd, fd_set *fdset); /* is the bit for fd on in fdset ? */

readset, writeset, exceptset对某个条件不感兴趣,就可以设空指针。

1
2
3
4
struct timeval {
long tv_sec;        /* seconds */
long tv_usec;       /* microseconds */
};

①永远等待 timeout设为空指针 ② 一段规定时间 ③ 不等待,两个值设为0

每次调用select之前都得对timeval结构进行初始化

readset, writeset, exceptset都是值-结果参数。函数返回后未就绪的描述符被置为0,用FD_ISET测试。

重新调用select之前都要再次把所有描述符置为1.

Figmæ 6.7. Summary of conditions that cause a socket to be ready for select.  Condi tion  Data to read  Read half of the connection closed  New connection ready for listening socket  Space available for writing  Write half of the connection clcxsc•d  Pending error  TCP out-of-band data  Readable ?  Wri table?  Excep tion ?

FD_SETSIZE定义了最大描述符数。poll可以避免描述符有限的问题。

  • shutdown

close函数终止网络链接的限制:

  • close把描述符的引用减1,仅在计数为0时才关闭套接字。shutdown不管引用计数,直接终止连接
  • close终止读和写两个方向的数据传送。shutdown可以半关闭。

#include <sys/socket.h>

int shutdown (int sockfd, int howto);

// return: 0 if OK, -1 on error

howto:

  • SHUT_RD 关闭连接的读这一半,而且套接字接收缓冲区的现有数据都被丢弃。

  • SHUT_WR 关闭连接的写这一半,当前留在套接字发送缓冲区的数据将被发送掉,后跟TCP终止序列

  • SHUT_RDWR 连接的读和写都关闭。

  • fileno(fp)将标准I/O文件指针fp转换为对应的描述符。计算出两个描述符的较大值加1就是maxfdp1.

使用半关闭,并针对缓冲区操作,从而消除还有数据正在发送途中的复杂问题。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
void str_cli(FILE *fp, int sockfd)
{
int maxfdp1, stdineof;
fd_set rset;
char buf[MAXLINE];
int n;
stdineof = 0;
FD_ZERO(&rset);
for ( ; ; ) {
if (stdineof == 0)
FD_SET(fileno(fp), &rset);
FD_SET(sockfd, &rset);
maxfdp1 = max(fileno(fp), sockfd) + 1;
Select(maxfdp1, &rset, NULL, NULL, NULL);
if (FD_ISSET(sockfd, &rset)) { /* socket is readable */
if ( (n = Read(sockfd, buf, MAXLINE)) == 0) {
if (stdineof == 1)
return; /* normal termination */
else
err_quit("str_cli: server terminated prematurely");
}
Write(fileno(stdout), buf, n);
}
if (FD_ISSET(fileno(fp), &rset)) { /* input is readable */
if ( (n = Read(fileno(fp), buf, MAXLINE)) == 0) {
stdineof = 1;
Shutdown(sockfd, SHUT_WR); /* send FIN */
FD_CLR(fileno(fp), &rset);
continue;
}
Writen(sockfd, buf, n);
}
}
}
  • 把服务器重写成使用select来处理人一个客户的单进程程序,而不是每个客户派生一个子进程。
1
2
服务器有个描述符集,在前台启动则描述符0、1、2分别被置为标准输入、标准输出、标准错误。
服务器端还有个已连接描述符的整形数组,都被初始化为-1.

Figmæ 6.17. Data structures after first client connection is established.  fd0 fd2 id3 fd4  roet  (E_SETSZZE-II

Figmæ 6.19. Data structures after second client connection is established.  fdO fdl fd2 fd3 fd4 fd5  (FD_SETSIZE-I)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
当第一个连接断开后,maxfd的值并没有改变。maxfd + 1是select第一个参数的值
int mian(int argc, char **argv)
{
int i, maxi, maxfd, listenfd, connfd, sockfd;
int nready, client[FD_SETSIZE];
ssize_t n;
fd_set rset, allset;
char buf[MAXLINE];
socklen_t client;
struct sockaddr_in cliaddr, servaddr;
 
listenfd = Socket(AF_INET, SOCK_STREAM, 0);
 
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(SERV_PORT);
 
Bind(listenfd, (SA *) &servaddr, sizeof(servaddr));
 
Listen (listenfd, LISTENQ);
 
maxfd = listenfd;
maxi = -1;
for (i = 0; i < FD_SETSIZE; i++)
client[i] = -1;
FD_ZERO(&allset);
FD_SET(listenfd, &allset);
 
for ( ; ; ) {
rset = allset;
nready = Select (maxfd+1, &rset, NULL, NULL, NULL);
 
if (FD_ISSET(listenfd, &rset)) {  /*new client connection*/
clilen = sizeof(cliaddr);
confd = Accept(listenfd, (SA *) &cliaddr, &clilen);
 
for (i = 0; i < FD_SETSIZE; i++)
if (client[i] < 0) {
client[i] = connfd;  /* save descriptor */
break;
}
if (i == FD_SETSIZE)
err_quit ("too many clients");
 
FD_SET(connfd, &allset); /*add new descriptor to set*/
if (connfd > maxfd)
maxfd = connfd;      /* for select */
if (i > maxi)
maxi = i;     /* max index in client[] array */
 
if (-- nready == 0)
continue;    /* no more readable descriptors */
}
 
for (i = 0; i <= maxi; i++) { /* check all clients for data */
if ((sockfd = client[i] < 0)
continue;
if (FD_ISSET(sockfd, &rset)) {
if ((n = Read (scokfd, buf, MAXLINE)) == 0) {
/* connection closed by client */
Close(sockfd);
FD_CLR(sockfd, &allset);
client[i] = -1;
} else 
Writen(sockfd, buf, n);
if (--nready <= 0)
break;   /* mo more readable descriptors */
}
}
}
}
存在的问题:阻塞于只与单个客户相关的某个函数调用,会导致服务器被挂起。
  • 采用非阻塞式I/O
  • 让每个客户由单独的控制线程提供服务。
  • 对I/O设置一个超时。
1
 
  • pselect
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
#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);
struct timespec {
time_t tv_sec;      /* seconds */
long tv_nsec;      /* nanoseconds */
};
 
  • poll
1
2
3
4
#include <poll.h>
int poll (struct pollfd *fdarray, unsigned long nfds, int timeout);
  // Returns: count of ready descriptors, 0 on timeout, –1 on error
 

poll提供的功能于select类似,不过在处理流设备时,它能够提供额外的信息。

poll识别三类数据:普通、优先级带、高优先级

1
2
3
4
5
6
7
 
struct pollfd {
int fd;             /* descriptor to check */
short events;        /* events of interest on fd */
short revents;       /* events that occured on fd */
};
要测试的条件由events成员指定,函数在相应的revents成员中返回该描述符的状态。从而避免像select一样三个参数的都是值-结果参数。

常  債  POLLRINORM  POLLR.DBAND  POLLPRI  昼 〕 L 以 NOF  POLLERR  K)LLHUP  FOLGNVAL  图 6 一 23 列 出 了 用 于 指 定 even 匕 s 标 志 以 及 测 试 reve 吡 s 标 志 的 一 些 常 值 。  作 为 e 肱 的 输 入 吗 ? 作 为 “ “ 的 结 果 吗 ?  普 通 或 优 先 级 带 数 据 可 读  普 通 数 据 可 读  优 先 级 带 数 据 可 读  高 优 先 级 数 据 可 读  普 通 数 据 可 写  普 通 数 据 可 写  优 先 级 带 数 据 可 与  发 生 错 误  发 生 挂 起  描 述 符 不 是 一 个 打 开 的 文 件  图 6 一 23  p 。 11 函 数 的 输 入 “ e , 豳 和 返 回 ’ “ “

1
2
POLLRDNORM
nfds指定结构数组中元素的个数。

m “ 值  0  图 卜 24  永 远 等 待  立 即 返 回 , 不 阻 塞 进 程  等 待 指 定 数 目 的 臺 桫 数  p 。 11 的 直 忉 “ “ 碜 数 值

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
当不关心某个特定描述符,可以把对应的pollfd结构的fd成员设置为一个负值,poll函数将忽略这样的pollfd结构的events成员,返回时设置revents成员为0。
int main(int argc, char **argv)
{
int i, maxi, listenfd, connfd, sockfd;
int nready;
ssize_t n;
char buf[MAXLINE];
socklen_t clilen;
struct pollfd client[OPEN_MAX];
struct sockaddr_in cliaddr, servaddr;
 
listenfd = Socket(AF_INET, SOCK_STREAM, 0);
 
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(SERV_PORT);
 
Bind(listenfd, (SA *) &servaddr, sizeof(servaddr));
 
Listen(listenfd, LISTENQ);
 
client[0].fd = listenfd;
client[0].events = POLLRDNORM;
for (i = 1; i < OPEN_MAX; i++)
client[i].fd = -1; /* -1 indicates available entry */
maxi = 0; /* max index into client[] array */
for ( ; ; ) {

//wait for either a new connection or data on existing connection

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
nready = Poll(client, maxi + 1, INFTIM);
// since client[0] is used for the listening socket.
if (client[0].revents & POLLRDNORM) { /* new client connection */
clilen = sizeof(cliaddr);
connfd = Accept(listenfd, (SA *) &cliaddr, &clilen);
 
for (i = 1; i < OPEN_MAX; i++)
if (client[i].fd < 0) {
client[i].fd = connfd; /* save descriptor */
break;
}
if (i == OPEN_MAX)
err_quit("too many clients");
client[i].events = POLLRDNORM;
if (i > maxi)
maxi = i; /* max index in client[] array */
if (--nready <= 0)
continue; /* no more readable descriptors */
}
for (i = 1; i <= maxi; i++) { /* check all clients for data */
if ( (sockfd = client[i].fd) < 0)
continue;
if (client[i].revents & (POLLRDNORM | POLLERR)) {
if ( (n = read(sockfd, buf, MAXLINE)) < 0) {
if (errno == ECONNRESET) {
/* connection reset by client */
Close(sockfd);
client[i].fd = -1;
} else
err_sys("read error");
} else if (n == 0) {
/* connection closed by client */
Close(sockfd);
client[i].fd = -1;
} else
Writen(sockfd, buf, n);
if (--nready <= 0)
break;     /* no more readable descriptors */
}
}
}
}
 
  • epoll

The epoll API can be used either as an edge-triggered or a level-triggered interface and scales well to large numbers of watched file descriptors.

1
2
3
4
5
6
7
8
 
#include <sys/epoll.h>
int epoll_create(int size);
//returns a file descriptor referring to the new epoll instance. 
//the file descriptor should be closed by using close().
 
int epoll_ctl(int epfd, int op, in fd, struct epoll_event *event);
       // return: 0 if OK, -1 on error

将要交由内核管控的文件描述符加入epoll对象并设置触发条件。

valid values for the op argument:

  • EPOLL_CTL_ADD:

Register the target file descriptor fd on the epoll instance epfd and associate the event event with the internal file linked to fd.

  • EPOLL_CTL_MOD

Change the event event associated with the target file descriptor fd.

  • EPOLL_CTL_DEL

Remove (deregister) the target file descriptor fd from the epoll instance referred to by epfd. The event is ignored and can be NULL (but see BUGS below).

The struct epoll_event is defined as:

typedef union epoll_data {

void *ptr;

int fd;

uint32_t u32;

uint64_t u64;

} epoll_data_t;

struct epoll_event {

uint32_t events; /* Epoll events */

epoll_data_t data; /* User data variable */

};

available event types:

EPOLLIN :The associated file is available for read operations.

EPOLLOUT:The associated file is available for write(2) operations.

EPOLLPRI:对应的文件描述符有紧急的数据可读(这里应该表示有带外数据到来);

EPOLLERR: Error condition happened on the associated file descriptor.

EPOLLHUP:Hang up happened on the associated file descriptor.

EPOLLET: 将EPOLL设为边缘触发(Edge Triggered)模式,这是相对于水平触发(Level Triggered)来说的。

EPOLLONESHOT:只监听一次事件,当监听完这次事件之后,如果还需要继续监听这个socket的话,需要再次把这个socket加入到EPOLL队列里

1
2
3
int epoll_wait(int epfd, struct epoll_event *events, int maxevents, 
int timeout);
// wait for an I/O event on an epoll file descriptor

//returns the number of file descriptors ready for the requested I/O, or zero if no file descriptor, -1 on error.

1
 

The struct epoll_event is defined as:

typedef union epoll_data {

void *ptr;

int fd;

uint32_t u32;

uint64_t u64;

} epoll_data_t;

struct epoll_event {

uint32_t events; /* Epoll events */

epoll_data_t data; /* User data variable */

};

The data of each returned structure will contain the same data the user set with an epoll_ctl (EPOLL_CTL_ADD, EPOLL_CTL_MOD) while the events member will contain the returned event bit field.

While the usage of epoll when employed as a level-triggered interface does have the same semantics as poll(2), the edge-triggered usage requires more clarification to avoid stalls in the application event loop.

#define MAX_EVENTS 10 struct epoll_event ev, events[MAX_EVENTS]; int listen_sock, conn_sock, nfds, epollfd;

/* Code to set up listening socket, ’listen_sock’, (socket(), bind(), listen()) omitted */

epollfd = epoll_create1(0); if (epollfd == -1) {

perror(“epoll_create1”);

exit(EXIT_FAILURE);

}

ev.events = EPOLLIN;

ev.data.fd = listen_sock;

if (epoll_ctl(epollfd, EPOLL_CTL_ADD, listen_sock, &ev) == -1) {

perror(“epoll_ctl: listen_sock”);

exit(EXIT_FAILURE);

}

for (;;) {

​ nfds = epoll_wait(epollfd, events, MAX_EVENTS, -1);

if (nfds == -1) {

​ perror(“epoll_wait”);

​ exit(EXIT_FAILURE);

​ }

for (n = 0; n < nfds; ++n) {

if (events[n].data.fd == listen_sock) {

​ conn_sock = accept(listen_sock,

​ (struct sockaddr *) &addr, &addrlen);

if (conn_sock == -1) {

perror(“accept”);

exit(EXIT_FAILURE);

}

setnonblocking(conn_sock);

ev.events = EPOLLIN | EPOLLET;

ev.data.fd = conn_sock;

if (epoll_ctl(epollfd, EPOLL_CTL_ADD, conn_sock,

&ev) == -1) {

perror(“epoll_ctl: conn_sock”);

exit(EXIT_FAILURE);

}

} else {

do_use_fd(events[n].data.fd);

}

}

}

When used as an edge-triggered interface, for performance reasons, it is possible to add the file descriptor inside the epoll interface (EPOLL_CTL_ADD) once by specifying (EPOLLIN|EPOLLOUT). This allows you to avoid continuously switching between EPOLLIN and EPOLLOUT calling epoll_ctl with EPOLL_CTL_MOD.

EPOLL****事件的两种模型:

Level Triggered (LT) 水平触发

.socket接收缓冲区不为空 有数据可读 读事件一直触发

.socket发送缓冲区不满 可以继续写入数据 写事件一直触发

符合思维习惯,epoll_wait返回的事件就是socket的状态

Edge Triggered (ET) 边沿触发

.socket的接收缓冲区状态变化时触发读事件,即空的接收缓冲区刚接收到数据时触发读事件

.socket的发送缓冲区状态变化时触发写事件,即满的缓冲区刚空出空间时触发读事件

仅在状态变化时触发事件

ET还是LT?

LT的处理过程:

. accept一个连接,添加到epoll中监听EPOLLIN事件

. 当EPOLLIN事件到达时,read fd中的数据并处理

. 当需要写出数据时,把数据write到fd中;如果数据较大,无法一次性写出,那么在epoll中监听EPOLLOUT事件

. 当EPOLLOUT事件到达时,继续把数据write到fd中;如果数据写出完毕,那么在epoll中关闭EPOLLOUT事件

ET的处理过程:

. accept一个一个连接,添加到epoll中监听EPOLLIN|EPOLLOUT事件

. 当EPOLLIN事件到达时,read fd中的数据并处理,read需要一直读,直到返回EAGAIN为止

. 当需要写出数据时,把数据write到fd中,直到数据全部写完,或者write返回EAGAIN

. 当EPOLLOUT事件到达时,继续把数据write到fd中,直到数据全部写完,或者write返回EAGAIN

从ET的处理过程中可以看到,ET的要求是需要一直读写,直到返回EAGAIN,否则就会遗漏事件。而LT的处理过程中,直到返回EAGAIN不是硬性要求,但通常的处理过程都会读写直到返回EAGAIN,但LT比ET多了一个开关EPOLLOUT事件的步骤

LT的编程与poll/select接近,符合一直以来的习惯,不易出错

ET的编程可以做到更加简洁,某些场景下更加高效,但另一方面容易遗漏事件,容易产生bug

例子与测试

这里有两个简单的例子演示了LT与ET的用法(其中epoll-et的代码比epoll要少10行):

https://github.com/yedf/handy/blob/master/raw-examples/epoll.cc

https://github.com/yedf/handy/blob/master/raw-examples/epoll-et.cc

对于nginx这种高性能服务器,ET模式是很好的,而其他的通用网络库,很多是使用LT,避免使用的过程中出现bug

LT(Level Triggered,水平触发) 和 ET(Edge Triggered,边沿触发)

作者当年花费了九牛二虎之力也没能领悟这段“经文”。后来一个偶然的机会, 一个做电子设计的朋友给我讲明白了其中的道道。

为了弄明白LT(Level Triggered,水平触发) 和 ET(Edge Triggered,边沿触发), 我们先要了解,这个Level和Edge是什么涵义,Level翻译成中文这里准确的涵义应该是电平; Edge是边沿。

这两个词曾经是电子信号领域的一个专有名词。如果,用时序图来标示一个数字电信号“010”, 应该是类似下图所示:

img

  • 低电平表示0。
  • 高电平表示1。
  • 0向1变化的竖线就是上升沿。
  • 1向0变化的竖线就是下降沿。
  • 在0或者1的情况下触发的信号就是LT(Level Triggered,水平触发)
  • 在0向1、1向0变化的过程中触发的信号就是 和 ET(Edge Triggered,边沿触发)

0或1都是一个状态,而0向1、1向0变化则只是一个事件。

我们很直观的就可以得出结论,LT是一个持续的状态,ET是个事件性的一次性状态。

二者的差异在于Level Triggered模式下只要某个socket处于readable/writable状态, 无论什么时候进行epoll_wait都会返回该socket;

而Edge Triggered模式下只有某个socket从unreadable变为readable或 从unwritable变为writable时,epoll_wait才会返回该socket。

虽然有很多资料表明ET模式的销量会比LT稍高, 但ET模式的编程由于事件只通知一次,很容易犯错误导致程序假死,我们推荐epoll工作于LT模式。 除非你很清楚你选择的是什么。

第7章 套接字选项

第8章 基本UDP套接字编程

第11章 名字与地质转换

第14章 高级I/O函数

第15章 Unix域协议

第16章 非阻塞式I/O

第26章 线程

第30章 客户/服务器程序设计范式

Reference