《Unix网络编程》卷1:套接字联网API(第3版):非阻塞I/O、ioctl操作、路由套接字
2017-08-20 22:22
531 查看
全书共31章+附录。
计划安排:吃透这本书,一天三章+源码,并实测代码做当天笔记,CSDN见。
时间安排:计划时间1.5个月 == 6个周末 == 12天。
2017.08.05 第01-03章:TCP/IP简介、传输层、套接字编程简介
2017.08.06 第04-06章:基本TCP编程、TCP客户端/服务器程序、I/O复用
2017.08.12 第07-09章:套接字选项、基本UDP编程、基本SCTP编程(略)
2017.08.13 第10-12章:SCTP客户端/服务器程序例子(略)、名字与地址互换、IPv4和IPv6互操作性
2017.08.19 第13-15章:守护进程和inetd超级服务器、高级I/O、Unix域协议
2017.08.20 第16-18章:非阻塞I/O、ioctl操作、路由套接字
2017.08.26 第19-21章:密钥管理套接字、广播、多播
2017.08.27 第22-24章:高级UDP编程、高级SCTP编程、带外数据
2017.09.02 第25-27章:信号驱动I/O、线程、IP选项
2017.09.03 第28-30章:原始套接字、数据链路访问、客户端/服务器程序设计范式
2017.09.09 第31章-附录:流。附录:IPv4/6协议、调试技术
2017.09.10 整理、总结:思维导图。
这就意味着当发出一个不能立即完成的套接字调用时,其进程将被投入睡眠,等待相应操作完成。
可能阻塞的套接字调用可分为以下4类:
(1) 输入操作:read、readv、recv、recvfrom、recvmsg五个函数。
如果某个进程对一个阻塞的TCP套接字调用这些输入函数之一,而且该套接字的接收缓冲区中没有数据可读,该进程将被投入睡眠,直到有一些数据到达。
(2) 输出操作:write、writev、send、sendto、sendmsg五个函数。
对于阻塞的套接字,如果其发送缓冲区中没有空间,进程将被投入睡眠,直到有空间为止。
(3) 接受外来连接:accept函数。
如果对一个阻塞的套接字调用accept函数,并且尚无新的连接到达,调用进程将被投入睡眠。
(4) 发起外出连接:connect函数(TCP)。
TCP连接的建立涉及三路握手过程,而且connect函数一直要等到客户收到对于自己的SYN的ACK为止才返回。
使用非阻塞式I/O的版本,防止进程在可做任何有效工作期间发生阻塞。
#include <sys/ioctl.h>
int ioctl(int d, int request, .../* void *arg */);
网络相关的请求(request)划分为6类:
· 套接字操作(是否位于带外标记等)
· 文件操作(设置或清除非阻塞标志等)
· 接口操作(返回接口列表,获取广播地址等)
· ARP高速缓存操作(创建、修改、获取、删除)
· 路由表操作(增加或删除)
· 流系统
网络相关的ioctl请求总结:
(1) <root>进程可以通过写出到路由套接字而往内核发送消息,比如路径的增加和删除。
(2) <root>进程可以通过从路由套接字读入而自内核接收消息。
(3) <all>进程可以使用sysctl函数倾泻出路由表或列出所有已配置的接口。
#include <net/if_dl.h>
struct sockaddr_dl {
uint8_t sdl_len;
sa_family_t sdl_family; /* AF_LINK */
uint16_t sdl_index; /* system assigned index, if>0 */
uint8_t sdl_type; /* IF_ETHER, ect. from <net/if_types.h> */
uint8_t sdl_nlen; /* name length, starting in sdl_data[0] */
uint8_t sdl_alen; /* link-layer address length */
uint8_t sdl_slen; /* link-layer selector length */
char sdl_data[12]; /* mininum work area, can be larger; contains i/f name and link-layer address */
};
读和写相关结构体:
struct rt_msghdr;
struct if_msghdr;
struct ifa_msghdr;
struct ifma_msghdr;
struct if_announcemsghdr;
#include <sys/param.h>
#include <sys/sysctl.h>
int sysctl (int *name, u_int namelen, void *oldp, size_t *oldenp, void *newp, size_t newlen);
特别备注:
由于一些特定的网络编程头文件暂时没办法查找到,很多例程比较难以验证,从代码中先学习其中的编程特点吧,后续针对概述的知识面文字内容会多于例程。
2017.08.20
16-18章基本完成...
计划安排:吃透这本书,一天三章+源码,并实测代码做当天笔记,CSDN见。
时间安排:计划时间1.5个月 == 6个周末 == 12天。
2017.08.05 第01-03章:TCP/IP简介、传输层、套接字编程简介
2017.08.06 第04-06章:基本TCP编程、TCP客户端/服务器程序、I/O复用
2017.08.12 第07-09章:套接字选项、基本UDP编程、基本SCTP编程(略)
2017.08.13 第10-12章:SCTP客户端/服务器程序例子(略)、名字与地址互换、IPv4和IPv6互操作性
2017.08.19 第13-15章:守护进程和inetd超级服务器、高级I/O、Unix域协议
2017.08.20 第16-18章:非阻塞I/O、ioctl操作、路由套接字
2017.08.26 第19-21章:密钥管理套接字、广播、多播
2017.08.27 第22-24章:高级UDP编程、高级SCTP编程、带外数据
2017.09.02 第25-27章:信号驱动I/O、线程、IP选项
2017.09.03 第28-30章:原始套接字、数据链路访问、客户端/服务器程序设计范式
2017.09.09 第31章-附录:流。附录:IPv4/6协议、调试技术
2017.09.10 整理、总结:思维导图。
>>第16章丶非阻塞式I/O
套接字的默认状态是阻塞的。这就意味着当发出一个不能立即完成的套接字调用时,其进程将被投入睡眠,等待相应操作完成。
可能阻塞的套接字调用可分为以下4类:
(1) 输入操作:read、readv、recv、recvfrom、recvmsg五个函数。
如果某个进程对一个阻塞的TCP套接字调用这些输入函数之一,而且该套接字的接收缓冲区中没有数据可读,该进程将被投入睡眠,直到有一些数据到达。
(2) 输出操作:write、writev、send、sendto、sendmsg五个函数。
对于阻塞的套接字,如果其发送缓冲区中没有空间,进程将被投入睡眠,直到有空间为止。
(3) 接受外来连接:accept函数。
如果对一个阻塞的套接字调用accept函数,并且尚无新的连接到达,调用进程将被投入睡眠。
(4) 发起外出连接:connect函数(TCP)。
TCP连接的建立涉及三路握手过程,而且connect函数一直要等到客户收到对于自己的SYN的ACK为止才返回。
非阻塞读和写:str_cli函数
使用非阻塞式I/O的版本,防止进程在可做任何有效工作期间发生阻塞。/* str_cli函数代码演示 */ /* include nonb1 */ #include "unp.h" void str_cli(FILE *fp, int sockfd) { int maxfdp1, val, stdineof; ssize_t n, nwritten; fd_set rset, wset; char to[MAXLINE], fr[MAXLINE]; char *toiptr, *tooptr, *friptr, *froptr; // 使用fctnl把3个描述符都设置为非阻塞模式 val = Fcntl(sockfd, F_GETFL, 0); Fcntl(sockfd, F_SETFL, val | O_NONBLOCK); val = Fcntl(STDIN_FILENO, F_GETFL, 0); Fcntl(STDIN_FILENO, F_SETFL, val | O_NONBLOCK); val = Fcntl(STDOUT_FILENO, F_GETFL, 0); Fcntl(STDOUT_FILENO, F_SETFL, val | O_NONBLOCK); toiptr = tooptr = to; /* initialize buffer pointers */ friptr = froptr = fr; stdineof = 0; maxfdp1 = max(max(STDIN_FILENO, STDOUT_FILENO), sockfd) + 1; for ( ; ; ) { FD_ZERO(&rset); FD_ZERO(&wset); if (stdineof == 0 && toiptr < &to[MAXLINE]) FD_SET(STDIN_FILENO, &rset); /* read from stdin */ if (friptr < &fr[MAXLINE]) FD_SET(sockfd, &rset); /* read from socket */ if (tooptr != toiptr) FD_SET(sockfd, &wset); /* data to write to socket */ if (froptr != friptr) FD_SET(STDOUT_FILENO, &wset); /* data to write to stdout */ Select(maxfdp1, &rset, &wset, NULL, NULL); /* end nonb1 第一部分:初始化并调用select */ /* include nonb2 */ if (FD_ISSET(STDIN_FILENO, &rset)) { if ( (n = read(STDIN_FILENO, toiptr, &to[MAXLINE] - toiptr)) < 0) { if (errno != EWOULDBLOCK) err_sys("read error on stdin"); } else if (n == 0) { #ifdef VOL2 fprintf(stderr, "%s: EOF on stdin\n", gf_time()); #endif stdineof = 1; /* all done with stdin */ if (tooptr == toiptr) Shutdown(sockfd, SHUT_WR);/* send FIN */ } else { #ifdef VOL2 fprintf(stderr, "%s: read %d bytes from stdin\n", gf_time(), n); #endif toiptr += n; /* # just read */ FD_SET(sockfd, &wset); /* try and write to socket below */ } } if (FD_ISSET(sockfd, &rset)) { if ( (n = read(sockfd, friptr, &fr[MAXLINE] - friptr)) < 0) { if (errno != EWOULDBLOCK) err_sys("read error on socket"); } else if (n == 0) { #ifdef VOL2 fprintf(stderr, "%s: EOF on socket\n", gf_time()); #endif if (stdineof) return; /* normal termination */ else err_quit("str_cli: server terminated prematurely"); } else { #ifdef VOL2 fprintf(stderr, "%s: read %d bytes from socket\n", gf_time(), n); #endif friptr += n; /* # just read */ FD_SET(STDOUT_FILENO, &wset); /* try and write below */ } } /* end nonb2 第二部分:从标准输入或套接字读入 */ /* include nonb3 */ if (FD_ISSET(STDOUT_FILENO, &wset) && ( (n = friptr - froptr) > 0)) { if ( (nwritten = write(STDOUT_FILENO, froptr, n)) < 0) { if (errno != EWOULDBLOCK) err_sys("write error to stdout"); } else { #ifdef VOL2 fprintf(stderr, "%s: wrote %d bytes to stdout\n", gf_time(), nwritten); #endif froptr += nwritten; /* # just written */ if (froptr == friptr) froptr = friptr = fr; /* back to beginning of buffer */ } } if (FD_ISSET(sockfd, &wset) && ( (n = toiptr - tooptr) > 0)) { if ( (nwritten = write(sockfd, tooptr, n)) < 0) { if (errno != EWOULDBLOCK) err_sys("write error to socket"); } else { #ifdef VOL2 fprintf(stderr, "%s: wrote %d bytes to socket\n", gf_time(), nwritten); #endif tooptr += nwritten; /* # just written */ if (tooptr == toiptr) { toiptr = tooptr = to; /* back to beginning of buffer */ if (stdineof) Shutdown(sockfd, SHUT_WR); /* send FIN */ } } } } } /* end nonb3 第三部分:写到标准输出或套接字 */
/* gf_time函数:返回指向时间字符串的指针,包括微秒(12:34:56.123456) */ char *gf_time(void) { struct timeval tv; time_t t; static char str[30]; char *ptr; if (gettimeofday(&tv, NULL) < 0) err_sys("gettimeofday error"); t = tv.tv_sec; /* POSIX says tv.tv_sec is time_t; some BSDs don't agree. */ ptr = ctime(&t); strcpy(str, &ptr[11]); /* Fri Sep 13 00:00:00 1986\n\0 */ /* 0123456789012345678901234 5 */ snprintf(str+8, sizeof(str)-8, ".%06ld", tv.tv_usec); return(str); }
非阻塞connect:时间获取客户程序
/* 代码演示 */ #include "unp.h" int connect_nonb(int sockfd, const SA *saptr, socklen_t salen, int nsec) { int flags, n, error; socklen_t len; fd_set rset, wset; struct timeval tval; // fcntl把套接字设置为非阻塞模式 flags = Fcntl(sockfd, F_GETFL, 0); Fcntl(sockfd, F_SETFL, flags | O_NONBLOCK); // 发起非阻塞connect。EINPROGRESS表示连接建立已经启动但是尚未完成。 error = 0; if ( (n = connect(sockfd, saptr, salen)) < 0) if (errno != EINPROGRESS) return(-1); /* Do whatever we want while the connect is taking place. */ if (n == 0) goto done; /* 连接建立 connect completed immediately */ FD_ZERO(&rset); FD_SET(sockfd, &rset); wset = rset; tval.tv_sec = nsec; tval.tv_usec = 0; // 调用select等待套接字变为可读或可写 if ( (n = Select(sockfd+1, &rset, &wset, NULL, nsec ? &tval : NULL)) == 0) { close(sockfd); /* timeout */ errno = ETIMEDOUT; return(-1); } if (FD_ISSET(sockfd, &rset) || FD_ISSET(sockfd, &wset)) { len = sizeof(error); if (getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &error, &len) < 0) return(-1); /* Solaris pending error */ } else err_quit("select error: sockfd not set"); done: Fcntl(sockfd, F_SETFL, flags); /* restore file status flags */ if (error) { close(sockfd); /* just in case */ errno = error; return(-1); } return(0); }
>>第17章丶ioctl操作
网络程序,特别是服务器程序,经常在程序启动执行后使用ioctl获取所在主机全部网络接口信息,包括:接口地址、是否支持广播、是否支持多播等等。ioctl函数
此函数影响由fd参数引用的一个打开的文件。#include <sys/ioctl.h>
int ioctl(int d, int request, .../* void *arg */);
网络相关的请求(request)划分为6类:
· 套接字操作(是否位于带外标记等)
· 文件操作(设置或清除非阻塞标志等)
· 接口操作(返回接口列表,获取广播地址等)
· ARP高速缓存操作(创建、修改、获取、删除)
· 路由表操作(增加或删除)
· 流系统
网络相关的ioctl请求总结:
/* 输出主机的硬件地址 - 代码演示 */ #include "unpifi.h" #include <net/if_arp.h> int main(int argc, char **argv) { int sockfd; struct ifi_info *ifi; unsigned char *ptr; struct arpreq arpreq; struct sockaddr_in *sin; sockfd = Socket(AF_INET, SOCK_DGRAM, 0); for (ifi = get_ifi_info(AF_INET, 0); ifi != NULL; ifi = ifi->ifi_next) { printf("%s: ", Sock_ntop(ifi->ifi_addr, sizeof(struct sockaddr_in))); sin = (struct sockaddr_in *) &arpreq.arp_pa; memcpy(sin, ifi->ifi_addr, sizeof(struct sockaddr_in)); if (ioctl(sockfd, SIOCGARP, &arpreq) < 0) { err_ret("ioctl SIOCGARP"); continue; } ptr = &arpreq.arp_ha.sa_data[0]; printf("%x:%x:%x:%x:%x:%x\n", *ptr, *(ptr+1), *(ptr+2), *(ptr+3), *(ptr+4), *(ptr+5)); } exit(0); }
>>第18章丶路由套接字
路由套接字支持3种类型的操作:(1) <root>进程可以通过写出到路由套接字而往内核发送消息,比如路径的增加和删除。
(2) <root>进程可以通过从路由套接字读入而自内核接收消息。
(3) <all>进程可以使用sysctl函数倾泻出路由表或列出所有已配置的接口。
#include <net/if_dl.h>
struct sockaddr_dl {
uint8_t sdl_len;
sa_family_t sdl_family; /* AF_LINK */
uint16_t sdl_index; /* system assigned index, if>0 */
uint8_t sdl_type; /* IF_ETHER, ect. from <net/if_types.h> */
uint8_t sdl_nlen; /* name length, starting in sdl_data[0] */
uint8_t sdl_alen; /* link-layer address length */
uint8_t sdl_slen; /* link-layer selector length */
char sdl_data[12]; /* mininum work area, can be larger; contains i/f name and link-layer address */
};
读和写相关结构体:
struct rt_msghdr;
struct if_msghdr;
struct ifa_msghdr;
struct ifma_msghdr;
struct if_announcemsghdr;
sysctl函数
这个函数使用类似简单网络管理协议中管理信息库的名字。#include <sys/param.h>
#include <sys/sysctl.h>
int sysctl (int *name, u_int namelen, void *oldp, size_t *oldenp, void *newp, size_t newlen);
/* sysctl函数判断UDP校验是否开启 */ #include "unproute.h" #include <netinet/udp.h> #include <netinet/ip_var.h> #include <netinet/udp_var.h> /* for UDPCTL_xxx constants */ int main(int argc, char **argv) { int mib[4], val; size_t len; mib[0] = CTL_NET; mib[1] = AF_INET; mib[2] = IPPROTO_UDP; mib[3] = UDPCTL_CHECKSUM; len = sizeof(val); Sysctl(mib, 4, &val, &len, NULL, 0); printf("udp checksum flag: %d\n", val); exit(0); }
特别备注:
由于一些特定的网络编程头文件暂时没办法查找到,很多例程比较难以验证,从代码中先学习其中的编程特点吧,后续针对概述的知识面文字内容会多于例程。
2017.08.20
16-18章基本完成...
相关文章推荐
- 《Unix网络编程》卷1:套接字联网API(第3版):广播、多播、信号驱动I/O、线程
- 《Unix网络编程》卷1:套接字联网API(第3版):套接字选项、基本UDP编程
- 《Unix网络编程》卷1:套接字联网API(第3版):守护进程和inetd超级服务器、高级I/O、Unix域协议
- 《Unix网络编程》卷1:套接字联网API(第3版):基本TCP编程、TCP客户端/服务器程序、I/O复用
- 《Unix网络编程》卷1:套接字联网API(第3版):简介、传输层、套接字编程
- 《Unix网络编程》卷1:套接字联网API(第3版):名字与地址互换、IPv4和IPv6互操作性
- 图灵社区 : 图书 : UNIX网络编程 卷1:套接字联网API(英文版•第3版)
- UNIX网络编程第3版.第1卷,套接字联网API:英文影印版
- UNIX网络编程.卷1,套接字联网API(第3版)(中文版)(Stevens经典著作,两位顶级网络编程专家应邀执笔修订)
- Unix网络编程--卷一:套接字联网API 读书笔记
- UNIX网络编程卷一:套接字联网API(学习笔记一)
- 套接字联网API之二 select作用和案例
- Unix网络编程卷一套接字联网API环境搭建
- Unix网络编程代码 第17章 ioctl操作
- UNIX网络编程---路由套接字(十八)
- UNIX网络编程卷一:套接字联网API-整理
- linux api笔记(2) 网络编程(一)如何判断非阻塞套接字是否连接成功
- UNIX网络编程 -- 第十六章 ioctl操作
- UNIX网络编程--ioctl操作(十七)
- Visual Basic Winsock API操作模块(基于API方式的socket同步阻塞通讯类)