呵呵,终于要开始传说中的大部头之旅了。其实Unix网络编程这本书12年暑假就买了,可是当时的网络知识一穷二白,而且也有各种事情,所以卷1和卷2连塑料膜都没有划开就被我供奉在了书架上,直到现在... 大三上的时候学习了Computer Network和Operating System,觉得基础已经积累得差不多了,那么,就开始吧。
【PS】小舟呢很喜欢听广播剧,那么也就仿照广播剧的样式,来个第n期第n期的吧。不怕慢,就怕断啊~希望能坚持到出完结篇!不想挖坑,握拳!
-------------------------------------------------------我是开始填坑的分界线------------------------------------------
一、概述
要编写通过计算机网络进行通信的程序,首先要确定它们之间用于通信的协议(protocol).
网络应用多采用C/S结构来组织。 客户(用户进程) <---协议--->服务器(服务器进程)
本书的焦点是TCP/IP协议族。
二、一个简单的时间获取客户程序
1 #include "unp.h" 2 3 int 4 main(int argc, char **argv) 5 { 6 int sockfd, n; 7 char recvline[MAXLINE + 1]; 8 struct sockaddr_in servaddr; 9 10 if (argc != 2) 11 err_quit("usage: a.out <IPaddress>"); 12 //socket函数创建一个网际(AF_INET)字节流套接字(SOCK_STREAM),其实就是TCP套接字。 13 //生成一个整数描述符,之后connect和read就用该描述符来标识这个套接字 14 if ( (sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) 15 err_sys("socket error"); 16 17 //把服务器额IP地址和端口号填入一个网际套接字结构(一个名为servaddr的sockaddr_in结构变量) 18 bzero(&servaddr, sizeof(servaddr)); 19 servaddr.sin_family = AF_INET; 20 //网际套接字结构结构中IP地址和端口号必须使用特定格式,调用库函数htons(host to network shortint)转换端口 21 servaddr.sin_port = htons(13); /* daytime server */ 22 if (inet_pton(AF_INET, argv[1], &servaddr.sin_addr) <= 0)//调用inet_pton("呈现形式到数值")转换IP 23 err_quit("inet_pton error for %s", argv[1]); 24 25 if (connect(sockfd, (SA *) &servaddr, sizeof(servaddr)) < 0) 26 err_sys("connect error"); 27 28 while ( (n = read(sockfd, recvline, MAXLINE)) > 0) {//read返回0时说明读取完毕 29 recvline[n] = 0; /* null terminate */ 30 if (fputs(recvline, stdout) == EOF) 31 err_sys("fputs error"); 32 } 33 if (n < 0) 34 err_sys("read error"); 35 36 exit(0); 37 }
三、协议无关性
上面的协议只是适用于IPv4,如果需要支持IPv6需要修改程序代码。-->更好的做法是编写协议无关性程序。
四、错误处理:包裹函数
程序都必须检查每个函数调用是否返回错误。在函数调用发生处错误时,调用自己的err_quit或是err_sys函数输出一个出错消息并终止函数的执行。这是我们经常要做到的事儿,所以我们可以通过定义包裹函数(wrapper function)来缩短程序。包裹函数完成实际的函数调用,检查返回值,并在发生错误时终止进程。
##Unix errno值
一个Unix函数发生错误时,全局变量errno会被置为一个指明该错误类型的正值,函数本身返回-1。err_sys函数可以查看errno变量的值并输出相应的出错消息。errno值只在发生错误时设置,0值不表示任何错误。
PS:在全局变量中存放errno值对于共享所有全局变量的多个线程并不合适。线程函数遇到错误时,把errno值作为函数返回值返回调用者。
五、一个简单的时间获取服务器程序
1 #include "unp.h" 2 #include <time.h> 3 4 int 5 main(int argc, char **argv) 6 { 7 int listenfd, connfd; 8 struct sockaddr_in servaddr; 9 char buff[MAXLINE]; 10 time_t ticks; 11 12 //创建TCP套接字 13 listenfd = Socket(AF_INET, SOCK_STREAM, 0); 14 15 16 bzero(&servaddr, sizeof(servaddr)); 17 servaddr.sin_family = AF_INET; 18 servaddr.sin_addr.s_addr = htonl(INADDR_ANY);//IP地址为INADDR_ANY,若是服务器有多个网络接口, 19 //服务器进程就可以在任何网络接口上接受客户连接 20 servaddr.sin_port = htons(13); /* daytime server */ 21 22 //把服务器众所周知的接口绑定到套接字上 23 Bind(listenfd, (SA *) &servaddr, sizeof(servaddr)); 24 25 //把套接字转换成监听套接字,使外来连接可以再该套接字上由内核接受 26 Listen(listenfd, LISTENQ);//LISTENQ:最大客户连接数 27 28 for ( ; ; ) { 29 connfd = Accept(listenfd, (SA *) NULL, NULL); 30 31 ticks = time(NULL); 32 //snprintf第二个参数限制缓冲区大小,可以保证缓冲区不溢出 33 snprintf(buff, sizeof(buff), "%.24s\r\n", ctime(&ticks)); 34 Write(connfd, buff, strlen(buff)); 35 36 //这个程序一次只能处理一个客户请求,如果要实现一对多的效果,可以使用fork为每个客户连接创建子进程;若需要长时间运行,需把程序改为一个 37 //Unix守护进程(daemon)运行 38 Close(connfd); 39 } 40 }
六、本书中C/S程序示例索引表
这个应该是看完后面部分之后作为一个目录吧,先mark下,学完再回头看。
七、OSI模型
OSI模型是ISO提出的。【每次看到这句话我总有一种微妙的感觉...】
八、BSD网络支持历史
BSD考古贴,有兴趣的详细看吧。
九、测试用网络及主机
本书网络拓扑图:
几个Unix网络基本命令:
(1)netstat -ni 提供网络接口信息和名称
(2)netstat -r 展示路由表
(3)ifconfig eth0 获取每个接口的详细信息
(4)ping -b XXX.XXX.XXX.XXX 根据上一步找出的广播地址找出在网中的主机
十、Unix标准
单一的Unix规范第三版有多个名称,简称为POSIX规范。它是两个长期发展的标准团体各自努力的汇合,由Austin CSRG最终团结起来。
十一、64位体系结构
现有32位Unix系统编程模型:ILP32模型
64位Unix系统编程模型:LP64模型
所以必须考虑LP64对现有API的影响。(使用专门设计的数据类型)
十二、小结
不得不说,Stevens这本书不愧是网络编程方面的扛鼎之作,覆盖面很广,而其中的代码注释又非常地详尽。其实上学期OS实验做得半死的原因也就是因为从来没有接触过OS的系统调用,其中的各种参数的使用完全是小白小白,而这里却是交代了参数的前世今生,觉得好像之前很恐怖的System Call也变得和蔼了不少。嗯,这是一个好现象,继续加油!