。。。。。。。(先把理论放一下)
struct sockaddr_in
{
__SOCKADDR_COMMON (sin_);
in_port_t sin_port; /* Port number. */
struct in_addr sin_addr; /* Internet address. */
/* Pad to size of `struct sockaddr'. */
unsigned char sin_zero[sizeof (struct sockaddr) -
__SOCKADDR_COMMON_SIZE -
sizeof (in_port_t) -
sizeof (struct in_addr)];
};
确实符合POSIX标准,不过没有增加额外字段
struct osockaddr
{
//uint8_t sa_len;书上还有这一句话
unsigned short int sa_family;
unsigned char sa_data[14];
};
struct sockaddr_in serv;
/* fill in serv{} */
bind(sockd, (struct sockadd *) &serv, sizeof(serv));
/* IPv6 address */
struct in6_addr
{
union
{
uint8_t __u6_addr8[16];
#ifdef __USE_MISC
uint16_t __u6_addr16[8];
uint32_t __u6_addr32[4];
#endif
} __in6_u;
#define s6_addr __in6_u.__u6_addr8
#ifdef __USE_MISC
# define s6_addr16 __in6_u.__u6_addr16
# define s6_addr32 __in6_u.__u6_addr32
#endif
};
/* Ditto, for IPv6. */
struct sockaddr_in6
{
__SOCKADDR_COMMON (sin6_);
in_port_t sin6_port; /* Transport layer port # */
uint32_t sin6_flowinfo; /* IPv6 flow information */
struct in6_addr sin6_addr; /* IPv6 address */
uint32_t sin6_scope_id; /* IPv6 scope-id */
};
struct sockaddr-un cli; /* Unix domain */
socklen_t len;
len= sizeof(cli);
/ len is a value */
getpeername( unixfd,(SA *) &cli, &len);
/* len may have changed */
注:当函数被调用时,结构大小是一个值(此值告诉内核该结构的大小,使内核在写此结构时不至于越界),当函数返回时,结构大小又是一个结果(它告诉进程内核在此结构中确切存储了多少信息),这种参数类型叫值结果参数
### 3.4字节排序函数
+ 大端:高字节放起始地址,如0x0102,内存中放的是0x0102
+ 小端:高字节放高地址,低字节放低地址如0x0102,内存中放的是0x0201
+ 网际协议使用大端字节序传送多字节整数
#include
uint16_t htons (uint16t host16bitvalue): //h:host
uint32_t htonl (uint32t host32bitvalue); //n:network
uint16_t ntohs (uint16t net16bitvalue); //s:short
uint32_t ntohl (uint32t net32bitvalue); //l:long
#ifdef __USE_MISC
/* Copy N bytes of SRC to DEST (like memmove, but args reversed). */
extern void bcopy (const void *__src, void *__dest, size_t __n)
__THROW __nonnull ((1, 2));
/* Set N bytes of S to 0. */
extern void bzero (void *__s, size_t __n) __THROW __nonnull ((1));
/* Compare N bytes of S1 and S2 (same as memcmp). */
extern int bcmp (const void *__s1, const void *__s2, size_t __n)
__THROW __attribute_pure__ __nonnull ((1, 2));
以后不要用memset了,bzero()好记多了
__BEGIN_NAMESPACE_STD
/* Set N bytes of S to C. */
extern void *memset (void *__s, int __c, size_t __n) __THROW __nonnull ((1));
/* Compare N bytes of S1 and S2. */
extern int memcmp (const void *__s1, const void *__s2, size_t __n)
__THROW __attribute_pure__ __nonnull ((1, 2));
__BEGIN_NAMESPACE_STD
/* Copy N bytes of SRC to DEST. */
extern void *memcpy (void *__restrict __dest, const void *__restrict __src,
size_t __n) __THROW __nonnull ((1, 2));
/* Copy N bytes of SRC to DEST, guaranteeing
correct behavior for overlapping strings. */
extern void *memmove (void *__dest, const void *__src, size_t __n)
__THROW __nonnull ((1, 2));
__END_NAMESPACE_STD
/* Copy no more than N bytes of SRC to DEST, stopping when C is found.
Return the position in DEST one byte past where C was copied,
or NULL if C was not found in the first N bytes of SRC. */
#if defined __USE_MISC || defined __USE_XOPEN
extern void *memccpy (void *__restrict __dest, const void *__restrict __src,
int __c, size_t __n)
__THROW __nonnull ((1, 2));
#endif /* Misc || X/Open. */
/* include inet_pton */
int
inet_pton(int family, const char *strptr, void *addrptr)
{
if (family == AF_INET) {
struct in_addr in_val;
if (inet_aton(strptr, &in_val)) { //调用了上一节讲的函数,返回的是C字符串形式的IP地址
memcpy(addrptr, &in_val, sizeof(struct in_addr));
return (1);
}
return(0);
}
errno = EAFNOSUPPORT;
return (-1);
}
/* end inet_pton */
/* include sock_ntop */
char *sock_ntop(const struct sockaddr *sa, socklen_t salen)
{
char portstr[8];
static char str[128]; /* Unix domain is largest */
switch (sa->sa_family) {
case AF_INET: {
struct sockaddr_in *sin = (struct sockaddr_in *) sa;
//地址转换:成功则返回c字符串形式的IP地址,str指定转换格式
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);
}
//。。。。。。
ssize_t /* Read "n" bytes from a descriptor. */
readn(int 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) /* 查找EINTR错误,表示系统被一个捕获信号中断 */
nread = 0; /* and call read() again */
else
return(-1);
//如果读成功了
} else if (nread == 0)
break; /* EOF */
//计算漏读的字节数,再读文件
nleft -= nread;
ptr += nread;
}
return(n - nleft); /* return >= 0 */
}
/* end readn */
int socket(int family, int type, int protocol)
int connect(int sockfd, const struct sockaddr *servaddr, socklen_t addrlen);
int bind(int sockfd, const struct sockaddr *myaddr, socklen_t addrlen);
getsockname()
来返回协议地址把一个未连接的套接字转换成一个被动套接字,即从CLOSE转换到LISTEN状态
int listen(int sockfd, int backlog);
int accept(int sockfd, struct sockaddr *cliaddr, socklen_t *addrlen);
已连接套接字描述符
,它的第一个参数叫监听套接字描述符int close(sockfd);
:可以用来关闭套接字,并终止TCP连接int getsockname(int sockfd, struct sockaddr *localaddr, socklen_t *addrlen);
:返回本地IP和端口号 int getpeername(int sockfd, struct sockaddr *peeraddr, socklen_t *addrlen);
6.查看:netstat -ant | grep 9877 ,得到下面的输出结果(-代替空格)
tcp—0—-0 0.0.0.0:9877———-0.0.0.0:*————-LISTEN
tcp—-0—-0 127.0.0.1:39960—-127.0.0.1:9877—-TIME_WAIT
7.查看进程:ps -ef –forest -o pid,ppid,tty,stat,args,wchan | sed -n ‘/tcp/p’
110361 109527 pts/18 S \_ ./tcpserv01 XDG_VTNR=7 inet_csk_accept
110366 110361 pts/18 Z | \_ [tcpserv01] \_ sed -n /tcp/p XDG_VTNR= pipe_wait
可以看到僵死进程
SIGCLD:在一个进程终止或者停止时,将SIGCHLD信号发送给其父进程;设置僵死进程是为了维护子进程信息,不处理可能导致耗尽资源
#include
pid_t wait(int *statloc);
pid_t waitpid(pid_t pid, int *statloc, int option);
+ 服务器:已由TCP排队,等待进程调用accept()却受到RST,稍后执行accept()
+ 处理方法:
+ Berkeley:完全在内核中处理已终止的连接,服务器进程根本看不到
+ 大多数SVR4:返回一个EPROTO(protocol error:协议错误)
+ POSIX:errno值必须是ECONNABORTED(software caused connection abort,软件引起的连接终止),因为流子系统其他一些致命协议相关事件时也会返回EPROTO
杀死服务器子进程(模拟服务器进程崩溃),server发送FIN,然而client阻塞在fgets(同时应对两个文件描述符,套接字和用户输入),从未无法返回ACK,此时键入一行文本,客户端将发送到服务器,此时有两种情况:
+ 1.调用readline(),然后收到RET,由于前面的FIN,readline()立刻返回0,收到未预期的EOF,提示出错信息:server terminated prematurely,(服务器过早终止)
+ 先收到RET,调用readline()时返回一个ECONNRESET(connection reset by peer),对方复位连接错误
上节情况下的客户端不理会readline()的错误反而写入更多的数据到服务器,即一个进程向某个已收到RST的套接字执行写操作,内核会向进程发送一个SIGPIPE信号,默认行为是终止进程。无论是捕获还是忽略该信号都将返回EPIPE错误。没有特殊事情要做就设置成SIG_IGN(忽略)
+ 需要注意:如果使用多个套接字,该信号的提交没法告诉我们是哪一个套接字出错了
- 所以要么不理会
- 要么从信号函数返回后再处理来自wirite的EPIPE
int select(int maxfdp1,fd_set *readset, fd_set *writeset, fd_set *exceptset, const struct timeval *timeout);
struct timeval {
long tv_sec; /* seconds */
long tv_usec; /* microseconds ,许多UNIX向上舍10ms整数倍,再加调度延迟,时间更不准确*/
}
fd_set rset;
FD_ZERO(&rset);
FD_SET(1, &rset);
FD_SET(4, &rset);
FD_SET(5, &rset);
int shutdown(int sockfd, int howto)
见代码注释:github链接
#include
#include
#include
int pselect(int maxfdp1, fd_set *readset, fd_set *writeset, fd_set *execptset, const struct timespec *timeout,const sigset_t *sigmask);
相比较于select:
+ 1.使用timespec 而不用timeval
struct timespec {
time_t tv_sec; //秒
long tv_nsec; //注意:这个表示纳秒
}
功能与select类似,不过在处理流设备时能提供更多的信息
#include
int poll(struct pollfd *fdarray, unsigned long nfds, int timeout);
struct pollfd {
int fd ; //需要检查的描述符,负数将被忽略
short events; //测试条件
short revents; //返回的描述符状态
}
见代码注释:github链接
#include
int getsockopt(int sockfd, int level, int optname, void *optname, void *optval, socklen_t *optlen);
int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen);
均返回:成功0,出错-1
#include
int getsockopt(int sockfd, int level, int optname, void *optname, void *optval, socklen_t *optlen);
int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen);
均返回:成功0,出错-1
见注释:代码链接
accept一直要到三次握手完成以后才返回给服务器已连接的套接字,想在三次握手完成时确保这些套接字选项中的某一个是给 已连接套接字 设置的,必须先设置 监听套接字
7.5.1 SO_BROADCAST
- 本选项开启或禁止进程发送广播(仅数据报套接字支持,且需在支持广播消息的网络上如以太网和令牌环网)
- 可以防止没有设置成广播时发送广播数据:如UDP发送一个的目的地址是一个广播地址,但是该选项没设置,就会返回EACCES错误
7.5.2 SO_DEBUG
仅由TCP支持,选项开启时内核将为TCP在该套接字发送和接收所有分组保留详细信息,可用trpt查看
7.5.3 SO_DONTROUTE
规定外出分组将绕过底层协议的正常路由机制,用来绕过路由表,以强制将分组从特定的接口发出
7.5.4 SO_ERROR(可获取不能设置)
套接字发生错误时,将套接字的so_error变量设置为为Unix Exxx值中的一个,也叫待处理错误(pending error),可以用下面两种方式中的一种立即通知进程
+ 1.阻塞在select时,返回设置RW中一个或两个条件
+ 2.信号驱动IO模型:产生SIGIO信号,进程通过访问SO_ERROR获取so_error的值getsockopt()返回的整个数值就是待处理错误,处理后复位为0
+ 阻塞在read()且没有数据且so_error非0,返回-1,error设置为so_error的值,so_error设置为0。
+ 阻塞在read()但有数据,则返回数据
+ 调用write时so_error非0,返回-1,error设置为so_error的值,so_error设置为07.5.5 SO_KEEPALIVE
设置保活选项后,2小时后(期间没有数据)TCP自动发送保活探测分节(keep-alive probe),会导致三种情况
+ 1.以期望ACK响应,进程得不到通知
+ 2.响应RST,表对端已崩溃并重启,套接字的待处理错误设置为ECONNRESET
+ 3.没有任何响应,间隔75s再发8个探测分节,11m15s后放弃且带错李错误设置为ETIMEOUT.如果收到ICMP错误就返回相应错误。
+这是一个清理通向不可达客户的半开连接的好方法
7.5.6 SO_LINGER
本选项指定close()函数对面向连接的协议如何操作,默认立即返回,如果有数据残留将尝试把这些数据发送给对端
要求传送给内核如下结构:
struct linger { int l_onoff; /*0=off,l_linger被忽略,>nonzero=on*/ int l_linger; /*linger time*/ }
- linger=0:丢弃缓冲区的任何数据,发送RST给对端,没有四分节终止序列,可避免TCP的TIME_WAIT状态。可能引发错误:在2MSL内创建另一个化身,刚终止的连接上的旧的分节不被正确的传递到新的化身上
- linger!=0,套接字关闭时内核拖延一段时间:进程将睡眠到所有数据已发送并被确认或延滞时间到
- 套接字是非阻塞型的,延滞时间到之前数据没发送完返回EWOULDBLOCK错误
- close()成功返回只能说明发送的数据和FIN已有对端确认,但不代表进程已经读取,所以改用shutdown好一点,当然也能用应用级ACK
7.5.7 SO_OOBINLINE
- 带外数据将被留存在正常的输入队列中(即在线留存),此时接收函数的MSG_OOB标志不能用来读取带外数据
7.5.8 SO_CVBUF和SO_SNDBUF
- 套接字接收缓冲区中可用空间大小限定了TCP通告对端窗口的大小
- 注意顺序:窗口规模选项是在建立连接时用SYN分节得到的,所以客户需在connect之前,serv需在listen之前
- 根据快恢复算法,缓冲区大小至少应是MSS值的四倍,最好是偶数倍
7.5.9 SO_RCVLOWAT和SO_SNDLOWAT(低水位标记)
- 接收低水位标记:select返回可读时接收缓冲区所需的数据量,T/UDP、SCTP默认为1
- 发送缓冲区:select()返回可写时发送缓冲区所需的可用空间。tcp默认2048,UDP的发送缓冲区的可用字节数从不改变(不保留副本)
7.5.10 SO_RCVTIMEO和SO_SNDTIMEO
设置超时值,默认设置为0,即禁止超时
7.5.11 SO_REUSEADDR和SO_REUSEPORT(重用地址端口)
- 监听服务器终止,子进程继续处理连接,重启监听服务器时会出错,而开启了SO_REUSEADDR就不会。
- SO_REUSEADDR允许同一个端口的多个服务器实例(只要不同的本地IP地址即可),通配地址捆绑一般放到最后
- SO_REUSEADDR允许同一个端口捆绑同一个进程的不同套接字
- SO_REUSEADDR允许UDP完全重复捆绑(一般来说),用于多播
7.5.12 SO_TYPE
- 本选项返回套接字类型,返回值是一个诸如SOCK_STREAM或SOCK_DGRAM之类的值,通常由启动时继承了套接字的进程使用
7.5.12 SO_USELOOPBACK
仅用于路由域(AF_ROUTE)套接字,默认打开。开启时,相应套接字将接收在其上发送的任何数据报的一个副本。
级别为IPPROTO_IP(get/setsockopt()的第二个参数)
7.6.1 IP_HDRINCL
- 如果是给原始IP套接字设置的,必须自己构造首部,下列情况例外:
- 见Page168
7.6.2 IP_OPTIONS
允许在IPv4首部总设置IP选项
7.6.3 IP_RECVDSTADDR
- 开启导致所收到的UDP数据报的目的地址由recvmsg作为辅助函数返回
7.6.4 IP_RECVIF
- 开启导致所收到的UDP数据报的接收接口索引由recvmsg函数作为辅助函数返回
7.6.5 IP_TOS
- 本套接字选项允许我们为TCP、UDP、SCTP设置IP首部中的服务类型字段。。。
7.6.6 IP_TTL
- 用于设置或获取系统用在从某个给定套接字发送的单播分组上的默认TTL值
- 级别为IPPROTO_ICMPV6
- ICMP6_FILTER:用于获取或设置一个icmp6_filter结构
- 级别为:IPPORTO_IPV6
7.8.1 IPV6_CHECKSUM
- 指定用户数据中校验和所处位置的字节偏移
7.8.2 IPV6_DONTFRAG
- 禁止为UDP套接字或原始套接字自动插入分片首部,将外出分组中大小超过发送接口MTU的那些分组将被丢弃
7.8.3 IPV6_NEXTHOP
- 将外出数据报的吓一跳地址指定为一个套接字地址结构
7.8.4 IPV6_PATHMTU
- 返回路径MTU发现功能确定的当前MTU
7.8.5 IPV6_RECVDSTOPTS
- 任何接收的IPv6地址都将由recvmsg作为辅助函数返回,默认关闭
7.8.6 IPV6_RECVHOPLIMIT
- 开启后,任何接收的跳限字段都将由recvmsg作为辅助函数返回
7.8.7 IPV6_RECVHOPOPTS
- 开启时,任何接收的IPv6跳选项作为辅助函数返回
。。。不懂这节在说什么
级别:IPPROTO_TCP
- 该选项允许设置或获取TCP连接的最大分节大小,返回值是TCP可以发送给对端的最大数据量
7.9.2 TCP_NODELAY
- 本选项将禁止TCP的Nagle算法(防止一个连接在任何时刻有多个小分组待确认)
- Nagle算法通常和ACK延滞算法(delayed ACK algorithm),该算法使得TCP在接收到数据后延滞一段时间(50-200ms)再确认
+对于客户以若干小片发送数据向服务器发送单个逻辑请求:首选方法是调用writev(),次一点是两次数据放到缓冲区然后调用write(),最次方法是先设置TCP_NODELAY再调用两次write()
级别:IPPROTO_SCTP
………………………………………(后面再看)
#include
int fcntl(int fd, int cmd, .../* int arg */);
O_NONBLOCK
(非阻塞式IO),O_ASYNC
(信号驱动式IO)int flag;
/* Set a socket as nonblocking */
if((flag=fcntl(fd, F_GETFL, 0)) < 0){ //必须要先获取其他文件标志
err_sys("F_GETFL, error");
}
flag |=O_NONBLOCK; //或运算,打开非阻塞标志
if(fcntl(fd, F_SETFL, flags) <0 ){
err_sys("F_SETFL error");
}
flag &=~O_NONBLOCK; //与运算,关闭非阻塞标志
if(fcntl(fd, F_SETFL, flags) <0 ){
err_sys("F_SETFL error");
}
#include
ssize_t recvfrom(int sockfd, void *buff, size_t nbytes, int flag, struct sockaddr *from, socklen_t *addrlen);
ssize_t sendto(int sockfd, const void *buff, size_t nbytes, int flag, const struct sockaddr *to, socklen_t addrlen);
代码地址
代码地址
仅在进程已将其UDP套接字连接到恰恰一个对端后,这个异步 错误才返回给进程
这里的connect()不同于TCP,只检查时候存在立即可知的错误,记录对端IP地址和端口号,然后返回给进程。连接后主要发生三点变化:
显式
connect()好一点/etc/resolv.conf:保存服务器主机IP的文件
代码是书上的
+ 当连接一个没有运行的udp服务器的程序时,连接不会出错,但是发送数据时会返回一个目的端口不可达的ICMP错误,被内核映射成ECONNREFUSED,UnixWare内核不会返回这种错误(page200)
netstat -s
:显示丢失的数据包8.1
recvfrom返回2048个字节的数据,它不会返回大于一个数据报大小的数据
#include
int sctp_bindx(int sockfd, const struct sockaddr *addrs, int addrcnt, int flags);
#include
int sctp_connectx(int sockfd, const struct sockaddr *addrs, int addrcnt);
Returns: 0 for success, –1 on error
#include
int sctp_getpaddrs(int sockfd, sctp_assoc_t id, struct sockaddr **addrs);
Returns: the number of peer addresses stored in addrs, –1 on error
#include
void sctp_freepaddrs(struct sockaddr *addrs);
#include
int sctp_getladdrs(int sockfd, sctp_assoc_t id, struct sockaddr **addrs);
Returns: the number of local addresses stored in addrs, –1 on error
#include
void sctp_freeladdrs(struct sockaddr *addrs);
ssize_t sctp_sendmsg(int sockfd, const void *msg, size_t msgsz, const struct sockaddr *to, socklen_t tolen, uint32_t ppid, uint32_t flags, uint16_t stream, uint32_t timetolive, uint32_t context);
Returns: the number of bytes written, –1 on error
ssize_t sctp_recvmsg(int sockfd, void *msg, size_t msgsz, struct sockaddr *from, socklen_t *fromlen, struct sctp_sndrcvinfo *sinfo, int *msg_flags);
Returns: the number of bytes read, –1 on error
int sctp_opt_info(int sockfd, sctp_assoc_t assoc_id, int opt void *arg, socklen_t *siz);
Returns: 0 for success, –1 on error
int sctp_peeloff(int sockfd, sctp_assoc_t id);
Returns: a new socket descriptor on success, –1 on error
struct sctp_tlv {
u_int16_t sn_type;
u_int16_t sn_flags;
u_int32_t sn_length;
};
/* notification event */
union sctp_notification {
struct sctp_tlv sn_header; //用于解释类型值
struct sctp_assoc_change sn_assoc_change;
struct sctp_paddr_change sn_paddr_change;
struct sctp_remote_error sn_remote_error;
struct sctp_send_failed sn_send_failed;
struct sctp_shutdown_event sn_shutdown_event;
struct sctp_adaption_event sn_adaption_event;
struct sctp_pdapi_event sn_pdapi_event;
};
.。。。先放一下,留给我的时间不多了。
lsmod | grep sctp
:查看是否安装了sctp#include
//函数的局限是只能返回IPv4地址
struct hostent *gethostbyname (const char *hostname);
Returns: non-null pointer if OK,NULL on error with h_errno set
struct hostent {
char *h_name; /* 主机的规范名字 */
char **h_aliases; /* 别名 */
int h_addrtype; /* host address type: AF_INET */
int h_length; /* length of address: 4 */
char **h_addr_list; /* ptr to array of ptrs with IPv4 addrs */
};
#include
struct hostent *gethostbyaddr (const char *addr, socklen_t len, int family);
Returns: non-null pointer if OK, NULL on error with h_errno set
/etc/services
:名字到端口的映射#include
struct servent *getservbyname (const char *servname, const char *protoname);
Returns: non-null pointer if OK, NULL on error
struct servent {
char *s_name; /* official service name */
char **s_aliases; /* alias list */
int s-port; /* port number, network-byte order:网络字节序 */
char *s_proto; /* protocol to use */
};
struct servent *sptr;
sptr = getservbyname("domain", "udp"); /* DNS using UDP */
sptr = getservbyname("ftp", "tcp"); /* FTP using TCP */
sptr = getservbyname("ftp", NULL); /* FTP using TCP */
sptr = getservbyname("ftp", "udp"); /* this call will fail */
#include
struct servent *getservbyport (int port, const char *protoname);
Returns: non-null pointer if OK, NULL on error
struct servent *sptr;
sptr = getservbyport (htons (53), "udp"); /* DNS using UDP */
sptr = getservbyport (htons (21), "tcp"); /* FTP using TCP */
sptr = getservbyport (htons (21), NULL); /* FTP using TCP */
sptr = getservbyport (htons (21), "udp"); /* this call will fail */
#include
int getaddrinfo (const char *hostname, const char *service, const struct addrinfo *hints, struct addrinfo **result) ;
Returns: 0 if OK, nonzero on error (see Figure 11.7)
struct addrinfo {
int ai_flags; /* AI_PASSIVE, AI_CANONNAME */
int ai_family; /* AF_xxx */
int ai_socktype; /* SOCK_xxx */
int ai_protocol; /* 0 or IPPROTO_xxx for IPv4 and IPv6 */
socklen_t ai_addrlen; /* length of ai_addr */
char *ai_canonname; /* ptr to canonical name for host */
struct sockaddr *ai_addr; /* ptr to socket address structure */
struct addrinfo *ai_next; /* ptr to next structure in linked list */
};
。。。后面的太多先放一下
#include
const char *gai_strerror (int error);
Returns: pointer to string describing error message
#include
void freeaddrinfo (struct addrinfo *ai);
#include "unp.h"
struct addrinfo *host_serv (const char *hostname, const char *service, int family, ints socktype);
Returns: pointer to addrinfo structure if OK, NULL on error
函数源码
#include "unp.h"
int tcp_connect (const char *hostname, const char *service);
Returns: connected socket descriptor if OK, no return on error
函数源码
用到该函数的一个例子
#include "unp.h"
int tcp_listen (const char *hostname, const char *service, socklen_t *addrlenp);
Returns: connected socket descriptor if OK, no return on error
#include "unp.h"
int udp_client (const char *hostname, const char *service, struct sockaddr **saptr, socklen_t *lenp);
Returns: unconnected socket descriptor if OK, no return on error
函数源码
+ 协议无关获取时间客户程序
#include "unp.h"
int udp_connect (const char *hostname, const char *service);
Returns: connected socket descriptor if OK, no return on error
函数源码
#include "unp.h"
int udp_server (const char *hostname, const char *service, socklen_t *lenptr);
Returns: unconnected socket descriptor if OK, no return on error
函数源码
+ 协议无关获取时间服务器程序
int getnameinfo (const struct sockaddr *sockaddr, socklen_t addrlen, char *host, socklen_t hostlen, char *serv, socklen_t servlen, int flags) ;
Returns: 0 if OK, nonzero on error (see Figure 11.7)
注意以下几点:
+ gethostbyname, gethostbyaddr, getservbyname, and get servbyport are not re-entrant 因为 all return a pointer to 同一个 static structure.
+ inet_pton and inet_ntop are always 可重入(re-entrant).
+ Historically(历史原因), inet_ntoa is not re-entrant, but some implementations(一些实现) that support threads provide a re-entrant version that uses thread-specific data(线程特定数据).
+ ……
+ 在信号处理函数中应尽量不使用任何可重入函数
#include
struct hostent *gethostbyname_r (const char *hostname, struct hostent *result, char *buf, int buflen, int *h_errnop) ;
struct hostent *gethostbyaddr_r (const char *addr, int len, int type, struct hostent *result, char *buf, int buflen, int *h_errnop) ;
Both return: non-null pointer if OK, NULL on error
守护进程:后台启动且不与任何控制终端关联
#include
void syslog(int priority, const char *message, ... );
syslog(LOG_INFO|LOG_LOCAL2, "rename(%s, %s): %m", file1, file2);
#include
void openlog(const char *ident, int options, int facility);//首次调用syslog前调用
void closelog(void);//在进程不需要发送日志消息时调用
/etc/inetd.conf
设置由inetd启动的程序步骤:
+ 1.在/etc/services
文件中服务名和端口
+ 2.添加程序路径:/etc/inetd.conf
,问题是找不到这个文件
+ 3.给inet发送sIGHUP信号
+ 4.日志存放文件:/var/adm/messages(根据/etc/syslog.conf),这两个文件也找不到
#include
ssize_t recv(int sockfd, void *buff, size_t nbytes, int flags);
ssize_t send(int sockfd, const void *buff, size_t nbytes, int flags);
Both return: number of bytes read or written if OK, –1 on error
#include
ssize_t readv(int filedes, const struct iovec *iov, int iovcnt);
ssize_t writev(int filedes, const struct iovec *iov, int iovcnt);
Both return: number of bytes read or written, –1 on error
struct iovec {
void *iov_base; /* starting address of buffer */
size_t iov_len; /* size of buffer */
};
#include
ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags);
ssize_t sendmsg(int sockfd, struct msghdr *msg, int flags);
Both return: number of bytes read or written if OK, –1 on error
struct msghdr {
void *msg_name; /* protocol address,指向一个套接字的地址结构 */
socklen_t msg_namelen; /* size of protocol address */
struct iovec *msg_iov; /* scatter/gather array,指定输出缓冲区数组*/
int msg_iovlen; /* # elements in msg_iov */
void *msg_control; /* ancillary data (cmsghdr struct),指定可选辅助数据 */
socklen_t msg_controllen; /* length of ancillary data */
int msg_flags; /* flags returned by recvmsg() */
};
struct msghdr msg;
struct cmsghdr *cmsgptr;
/* fill in msg structure */
/* call recvmsg() */
for (cmsgptr = CMSG_FIRSTHDR(&msg); cmsgptr != NULL;
cmsgptr = CMSG_NXTHDR(&msg, cmsgptr)) {
if (cmsgptr->cmsg_level == ... &&
cmsgptr->cmsg_type == ... ) {
u_char *ptr;
ptr = CMSG_DATA(cmsgptr);
/* process data pointed to by ptr */
}
}
获悉已排队的数据量:
- 1,使用非阻塞IO
- 2,使用MSG_PEEK标志
- 3,ioctl的第三个参数
使用标准IO容易引起缓冲问题,可以用setvbuf迫使变为行缓冲
/dev/poll
#include
#include
#include
int kqueue(void);
int kevent(int kq, const struct kevent *changelist, int nchanges, struct kevent *eventlist, int nevents, const struct timespec *timeout) ;
void EV_SET(struct kevent *kev, uintptr_t ident, short filter, u_short flags, u_int fflags, intptr_t data, void *udata);
尽管总的来说应该避免编写不可移植的代码,然而对于一个任务繁重的网络应用程序来说,使用各种可能得方式为他在特定的主机上进行优化也相当普遍