UNP学习笔记(2)

接着前几天的UNP学习,由于中间的章节之前在其它书上都看过,所以直接跳到UNP的第三部分《高级套接字编程》

第12章 IPv4与IPv6的互操作性

这一章内容比较少

1、P279:在双栈主机中(IPv4与IPv6协议栈),接收数据链路通过查看以太网类型字段,把每个数据帧传递给相应的IP模块( v4或v6)。

2、协议栈中的网络层有一个地址映射机制,网络层会结合传输层套接字类型(v4或v6),把相应的地址(v6或v4)进行转换,形成一个等价的映射地址

3、地址映射对应用层透明


第13章 守护进程和inetd超级服务器

1、P286:守护进程(daemon)是在后台运行且不与任何控制终端关联的进程。

2、P286:因为守护进程没有控制终端,所以当有事发生时它们得有输出消息的某种方法可用。syslog函数是输出这些消息的标准方法,它把这些消息发送给syslogd守护进程。

3、P287:在ubuntu中,syslog的配置文件路径:/etc/rsyslog.d/50-default.conf,主要根据日志类型以及发送进程类型,配置不同的日志记录文件路径,如:

kern.* /dev/console

表示所有内核日志都写到/dev/console中

4、P291:打开/dev/null作为本守护进程的标准输入、标准输出和标准错误输出的重要性。A:保证守护进程对read、write等操作不会因为这些描述符未打开而出错;B:如果未打开这些描述符,当守护进程创建了套接字时,会按顺序占用这些描述符,万一守护进程调用了perror之类的函数时,会错误地把出错信息发送给套接字

5、P293:Unix系统包含许多服务器,如FTP、Telnet、Rlogin等,inetd守护进程可以实现统一的接入与监听,简化服务器的编程工作,有如下好处:

A、通过由inetd处理普通守护进程的大部分启动细节以简化守护程序的编写。这么一来每个服务器不再有调用daemon_init函数的必要

B、单个进程(inetd)就能为多个服务等待外来的客户请求,以此取代每个服务一个进程的保法,减少系统中的进程总数


注:把套接字dup到0、1、2描述符,于是子进程自标准输入读实际是从所处理的套接字读,往标准输出或标准错误输出写实际上是往所处理的套接字写。



第14章 高级I/O函数

1、P300:在涉及套接字的IO操作上设置超时的方法有以下3种

(1)调用alarm,在指定超时期满时产生SIGALRM信号,利用信号触发时会中断系统调用的特性,此时系统调用一般返回-1并且设置errno为EINTR,这个方法不建议使用

(2)使用select设置超时

(3)使用SO_RCVTIMEO和SO_SNDTIMEO套接字超时选项,但只能用于套接字,且对于设置connect超时无效。如果IO操作超时,返回-1并设置errno为EWOULDBLOCK

2、P305:send和recv的flags参数:

MSG_DONTROUTE:绕过路由表查找(send)

MSG_DONTWAIT:仅本操作非阻塞(send、recv)

MSG_OOB:发送或接收带外数据(send、recv)

MSG_PEEK:窥看外来消息(recv)

MSG_WAITALL:等待所有数据(recv),如果系统支持本标志,就可以省掉readn函数,告知内核要不在尚未读入请求数目的字节之前让一个读操作返回

3、P315:Unix的缓冲规则:

A、标准错误输出总是不缓冲

B、标准输入和标准输出完全缓冲,除非它们指代终端设备(这种情况下它们行缓冲)

C、所有其他IO流都是完全缓冲,除非它们指代终端设备(这种情况下它们行缓冲)


第15章 Unix域协议

1、P324:使用Unix域套接字有以下3个理由:
A、Unix域套接字往往比通信两端位于同一个主机的TCP套接字快;
B、Unix域套接字可用于在同一个主机上的不同进程之间传递描述符;
C、Unix域套接字较新的实现把客户的凭证(用户ID和组ID)提供给服务器,从而能够提供额外的安全检查措施。
Unix域中用于标识客户和服务器的协议地址是普通文件系统中的路径名,即Unix域套接字的服务器和客户端使用一个约定的路径名,代替IP地址和端口号,有点类似于传统IPC通信机制中的全局唯一标识符

unix域套接字地址结构:

struct sockaddr_un
{
	sa_family_t sun_family;
	char sun_path[104];
};
可以看出,没有地址信息,也没有端口信息,只需要一个sun_path就能替代IP地址和端口(因为就在同一台主机上)

2、P326:socketpair函数创建两个随后连接起来的套接字,属于Unix域套接字,是一个全双工的管道

3、P327:Unix套接字函数注意:

A、与Unix域套按字关联的路径名应该是一个绝对路径名,而不是一个相对路径名

B、Unix域字节流套接字类似TCP套接字:提供一个无记录边界的字节流接口

C、Unix域数据报套接字类似UDP套接字:提供一个保留记录边界的不可靠的数据报服务

D、如果对于某个Unix域套接字的connect调用发现这个监听套接字的队列已满,调用就立即返回一个ECONNREFUSED错误

E、在一个未绑定的Unix域套接字上发送数据报不会自动给这个套接字捆绑一个路径名,这一点与UDP套接字不同:在一个未绑定的UDP套接字上发送UDP数据报将导致给这个套接字捆绑一个临时端口。意味着必须对数据报发送端套接字绑定一个路径名,否则数据报接收端无法发回应答数据报。

4、P328:Unix域字节流协议的服务器程序

int					listenfd, connfd;
	pid_t				childpid;
	socklen_t			clilen;
	struct sockaddr_un	cliaddr, servaddr;//使用sockaddr_un地址结构
	void				sig_chld(int);

	listenfd = Socket(AF_LOCAL, SOCK_STREAM, 0);//一样

	unlink(UNIXSTR_PATH);//解除早前的绑定
	bzero(&servaddr, sizeof(servaddr));
	servaddr.sun_family = AF_LOCAL;//注意
	strcpy(servaddr.sun_path, UNIXSTR_PATH);//注意,UNIXSTR_PATH是一个自定义的绝对路径

	Bind(listenfd, (SA *) &servaddr, sizeof(servaddr));//一样

	Listen(listenfd, LISTENQ);//一样

	Signal(SIGCHLD, sig_chld);
<span style="white-space:pre">	</span>
<span style="white-space:pre">	</span>//下面的都一样
	for ( ; ; ) {
		clilen = sizeof(cliaddr);
		if ( (connfd = accept(listenfd, (SA *) &cliaddr, &clilen)) < 0) {
			if (errno == EINTR)
				continue;		/* back to for() */
			else
				err_sys("accept error");
		}

		if ( (childpid = Fork()) == 0) {	/* child process */
			Close(listenfd);	/* close listening socket */
			str_echo(connfd);	/* process request */
			exit(0);
		}
		Close(connfd);			/* parent closes connected socket */
	}

5、P329:Unix域字节流协议客户端程序

	int					sockfd;
	struct sockaddr_un	servaddr;//使用sockaddr_un地址结构

	sockfd = Socket(AF_LOCAL, SOCK_STREAM, 0);//一样

	bzero(&servaddr, sizeof(servaddr));
	servaddr.sun_family = AF_LOCAL;//注意
	strcpy(servaddr.sun_path, UNIXSTR_PATH);//注意,这样必须与服务器的相同!!!

	Connect(sockfd, (SA *) &servaddr, sizeof(servaddr));//一样

	str_cli(stdin, sockfd);		/* do it all */


6、P329:Unix域数据报协议的服务器程序

	int					sockfd;
	struct sockaddr_un	servaddr, cliaddr;//使用sockaddr_un地址结构

	sockfd = Socket(AF_LOCAL, SOCK_DGRAM, 0);//一样

	unlink(UNIXDG_PATH);
	bzero(&servaddr, sizeof(servaddr));
	servaddr.sun_family = AF_LOCAL;//注意
	strcpy(servaddr.sun_path, UNIXDG_PATH);//注意

	Bind(sockfd, (SA *) &servaddr, sizeof(servaddr));//绑定一个路径

	dg_echo(sockfd, (SA *) &cliaddr, sizeof(cliaddr));//开始recvfrom


6、P330:Unix域数据报协议的客户程序
	int					sockfd;
	struct sockaddr_un	cliaddr, servaddr;//使用sockaddr_un地址结构

	sockfd = Socket(AF_LOCAL, SOCK_DGRAM, 0);//一样

	bzero(&cliaddr, sizeof(cliaddr));		/* bind an address for us */
	cliaddr.sun_family = AF_LOCAL;
	strcpy(cliaddr.sun_path, tmpnam(NULL));

	Bind(sockfd, (SA *) &cliaddr, sizeof(cliaddr));

	bzero(&servaddr, sizeof(servaddr));	/* fill in server's address */
	servaddr.sun_family = AF_LOCAL;
	strcpy(servaddr.sun_path, UNIXDG_PATH);

	dg_cli(stdin, sockfd, (SA *) &servaddr, sizeof(servaddr));

	exit(0);
与UDP客户不同的是,当使用Unix域数据报协议时,必须显式bind一个路径名到套接字,这样服务器才会有能回射应答的路径名。

7、P331:在两个进程间传递描述符的步骤:

A、创建一个字节流或数据报的Unix域套接字

B、发送进程通过调用返回描述符的任一Unix函数打开一个描述符,传递的描述符类型不限

C、把描述符作为辅助数据,调用sendmsg发送后,即使发送进程在调用sendmsg之后但在接收进程调用recvmsg之前关闭了描述符,对于接收进程它仍然保持打开状态。发送一个描述符会使该描述符的引用计数加1。

D、接收进程调用recvmsg在Unix域套接字上接收这个描述符(接收辅助数据)。传递一个描述符并不是传递一个描述符号,而是涉及在接收进程中创建一个新的描述符,而这个新描述符和发送进程中飞行前的那个描述符指向内核中相同的文件项。

8、P332:通过执行另一个程序来打开文件的优势在于,另一个程序可以是一个setuid到root的程序,能够打开我们通常没有打开权限的文件。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值