《实战 Linux Socket编程》读书笔记.doc_第1页
《实战 Linux Socket编程》读书笔记.doc_第2页
《实战 Linux Socket编程》读书笔记.doc_第3页
《实战 Linux Socket编程》读书笔记.doc_第4页
《实战 Linux Socket编程》读书笔记.doc_第5页
免费预览已结束,剩余49页可下载查看

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

书名:实战 Linux Socket编程作者:Warren W.Gay翻译:詹俊鹄 于卫出版:西安电子科技大学出版社第一章 套接口简介1、套接口是通信中的一个端点,套接口创建后,就如同一个文件描述符,可以使用文件的I/O函数对它进行读、写和关闭操作。2、套接口和已打开的文件之间存在如下差别:l 不能在套接口上调用函数lseek(这个限制也适用于管道)l 套接口可以和网络地址关联,文件和管道却不能l 套接口具有很多能够通过ioctl进行查询和设置的选项l 套接口必须在正确的状态下才能实现输入和输出,而已打开文件在任何时候都可以进行读或写操作。3、套接口和管道不同,它允许进行进行双向通信,即可以使用同一个套接口描述符进行读和写【代码test1.2.c】。4、创建套接口#include #include int socketpair ( int domain, int type, int protocol, int sv2);domain:套接口的域名,表示使用哪个地址族,要么是AF_LOCAL要么是AF_UNIX。地址族的作用是指明使用哪一种地址类型。AF_LOCAL(AF_UNIX)表示使用本地地址规则来生成地址,而AF_INET则表示使用IP地址规则生成地址。type:套接口的类型,可选值为SOCK_STREAM、SOCK_DGRAMprotocol:使用的协议。一般情况下为0,这样可以使用适合所选domain的正确缺省协议。sv:套接口文件描述符数组。每个整型值代表一个套接口,类似于管道中的某一端的端点。如果函数调用成功,返回0,否则返回-1,错误值存放在errno中。5、socketpair用例请参看【代码test1.1.c】6、可以对套接口调用read、write、close 等函数,请参看【代码test1.2.c】7、使用close关闭端口,接收端会收到文件结束符标志,close是全关闭,也就是说关闭了套接口后,既不能读也不能写。shutdown半关闭,也就是说不能写,但是可以使用该套接口从读取对端发送的数据。#include int shutdown (int s, int how);how 有三个可选值:SHUT_RD、SHUT_WR、SHUT_RDWR如果函数调用成功,返回0,否则返回-1,错误值存放在errno中。仅对套接口的写端进行关闭,可以解决如下问题:l 内核缓冲区中任何将要发送的数据都将作废l 向远程套接口发送文件结束标志,告诉对方进程,不再会向对方发送数据l 本地半关闭的套接口仍然可以进行读操作l 不管套接口的访问记数为多少,shutdown都将访问数清0。如果使用dup或dup2复制了套接口描述符,则只有所有的描述符都被close才能关闭套接口,而shutdown则能够一次性关闭复制的所有套接口。因此不论是要全关闭或半关闭套接口,都应该使用shutdown,而不是close。然而shutdown不会释放文件描述符,因此shutdown执行后,还应该调用close释放文件描述符。8、当调用fork生成子进程时,任何在fork操作之前存在的套接口都将复制到子进程中【代码test1.2.c】。9、关闭从套接口的读入将忽视任何等待读入的数据,如果有更多的数据从远程端发送过来,也将同样被忽视掉,如果这时进程尝试从套接口进行读入,就会发生错误。第二章 域和地址族1、套接口不一定需要地址,比如函数socketpair就生成了一对相互连接但是没有地址的套接口,这就是所谓的无名套接口。2、匿名调用:在相互连接的两个套接口中有一个套接口不需要地址,例如连接到一个远程套接口时,只要确定远程套接口的地址,但是发出调用的本地套接口可以是匿名的。此时本地套接口不需要bind3、socktpair 和 socket 函数都允许使用其他协议,而不只是TCP/IP 协议。4、int socketpair ( int domain, int type, int protocol, int sv2);domain:套接口的域名,表示使用哪个地址族,要么是AF_LOCAL要么是AF_UNIX。地址族的作用是指明使用哪一种地址类型。AF_LOCAL(AF_UNIX)表示使用本地地址规则来生成地址,而AF_INET则表示使用IP地址规则生成地址(IPv4等)一般情况下,protocol参数的值为0,这使操作系统可以选择适合所选domain的正确的缺省协议5、通用地址结构#include struct socketaddrsa_family_t sa_family; /*地址族类型*/char sa_data14; /*地址数据*/所有的地址都要在结构中同样的位置定义 sa_family成员,因为它决定了怎样翻译结构中的包含地址信息的字节。6、生成本地地址例如使用本地的打印服务,则可以使用本地套接口(本地地址)与打印服务通信,而不需要使用TCP/IP进行通信。本地套接口往往被称为AF_UNIX域,因为这些地址使用UNIX文件名作为地址名。对应于AF_LOCAL和AF_UNIX地址的结构名是sockaddr_un,而对应于IP地址的结构名是sockaddr_in#include struct sockaddr_unsa_family_t sun_family; /*地址族*/char sun_path108; /*路径名*/sun_family:只能选为AF_LOCAL或AF_UNIX。sun_path:包含一个有效的UNIX路径名,这个字符串的末尾不需要一个空字符。l 【传统本地地址】的命名空间就是文件系统的路径名,一个进程可以使用任何有效的路径名来命名本地套接口路径,这里的有效指的是该进程可以访问这个路径名下的所有目录,并有权在该路径下生成套接口。【代码】sockaddr_un soc;soc.sun_family = AF_UNIX;strcpy(soc.sun_path, ”/tmp/my_socket”);bind( s, (struct sockaddr *)&soc, SUN_LEN(&soc);l 生成【本地抽象地址】,只需把路径名的第一个字节设置为空字节,而空字节后的其他部分是抽象名。对本地套接口生成抽象地址,从而避免生成文件系统对象。【代码】sockaddr_un soc;soc.sun_family = AF_UNIX;strcpy(soc.sun_path,”ZvirtualName”);soc.sun_path0=0;这里的Z是占位符(可以用任何一个字符作占位符),重要的是,需要在后面代码中将路径名的第一个字节设置为空字节。7、生成Internet(IPv4)套接口地址#include struct sockaddr_insa_family_t sin_family /*地址族,初始化为 AF_INET*/uint16_t sin_port; /*端口号,网络字节序*/struct in_addr sin_addr; /*Internet 地址,IP地址,网络字节序*/unsigned char sin_zero8 /*占位字符,不使用,用于使整个结构体以16字节对齐*/struct in_addr uint32_t s_addr;8、网络字节序小端:低序字节存储在低地址大端:高字节放在低地址。例如 0x 1234小端(Intel CPU):低地址(0x34)高地址(0x12)大端(网络字节序):低地址(0x12)高地址(0x34)现在网络上传输数据的标准顺序为大端字节序,使用下面的函数可以在主机字节序和网络字节序之间转换:#include unsigned long htonl (unsigned long hostlong);unsigned short htons (unsigned short hostshort);unsigned long ntohl(unsigned long netlong);unsigned short ntohs(unsigned short netshort);9、初始化一个通配的Internet地址使用通配地址,是因为有的主机安装了多块网卡,每块网卡有独立的IP地址,而且Linux允许为一块网卡指定不同的IP地址,如果我们指定通配地址,则系统会自动选择一条通往远程服务的路由,当连接建立时,内核最终将决定使用哪一个本地的套接口地址。当想内核自动分配本地端口号时,也可以使用通配的端口号,要使用这一点很简单,只需要将sinp_port 的值赋值为0。【代码】struct sockaddr_in addr_inet;memset(&addr_inet, 0, sizeof(sockaddr_in);addr_inet.sin_family = AF_INET;addr_inet.sin_port = ntohs(0);addr_inet.sin_addr.s_addr = ntohl(INADDR_ANY); / 通配的IP地址10、初始化一个特定的Internet地址【代码】struct sockaddr_in addr_inet;const unsigned char IPno=192, 168, 1, 100;memset(&addr_inet, 0, sizeof(sockaddr_in);addr_inet.sin_family = AF_INET;addr_inet.sin_port = ntohs(9000);memcpy( &addr_inet.sin_addr.s_addr, IPno, 4); / 通配的IP地址11、生成X.25地址用于定义X.25协议地址的结构是 sockaddr_x25#include struct sockaddr_x25sa_family_t sx25_family; /*只能是AF_X25*/x25_address sx25_addr; /*X.25 地址*/typedef struct char x25_addr16;【代码】int sock_x25;struct sockaddr_x25 adr_x25;const char x25_host=”79400900”;adr_x25.sx25_family = AF_X25;strcpy (adr_x25.sx25_addr.x25_addr, x25_host);sock_x25 = sock( AF_X25, SOCK_SEQPACKET, 0 );12、除了IPv4外,linux至少还支持以下三类地址族:l AF_INET6:IPv6l AF_AX25:业余无线电X.25协议l AF_APPLETALK:Linux下AppleTalk协议的实现。13、宏 AF_UNSPEC 代表不确定的地址族,它可以作为一个占位符,在不确定具体的地址族之前,可以使用它。在后续程序中可以修改。例如:unionsockaddr sa;sockaddr_un un;sockaddr_in in;sockaddr_in6 in6; u;u.sa_family = AF_UNSPEC;u.sa_family = AF_INET;第三章 地址转换函数1、一个Internet地址是由以下两个部分组成:网络地址:表示主机所在网络主机地址:用来区分同一网络中的不同主机2、网络掩码的作用在于把网络地址从IP地址中提取出来,实际上网络掩码与某一特定的IP地址进行“按位与”操作。3、如果你的系统没有直接连接到Internet,就不需要全球唯一的地址,这时就可以用私有IP地址来替代。私有IP地址的分配:分类最低最高掩码A55B55C554、inet_addr 函数#include #include #include unsigned long inet_addr (const char * string);作用:将 string 表示的点分十进制的IP地址转换成32位二进制表示法,次函数的返回值是这个32位二进制的【网络字节序】,如果string不是一个有效的IP地址,则返回INADDR_NONE。【代码】struct sockaddr_in adr_inet;adr_inet.sin_addr.s_addr = inet_addr(“68”);if(adr_inet.sin_addr.s_addr = INADDR_NONE) printf(“bad address”);【注意】在新程序里应避免使用 inet_addr,而应该使用 inet_aton 来替代。对于 inet_addr,即使输入 55 返回值仍然是 INADDR_NONE5、inet_aton 函数#include #include #include int inet_aton (const char * string, struct in_addr * addr);如果函数调用成功,返回一个非0值,否则返回0,并没有建立有效的errno值,所以当函数返回错误时,不要去测试errno 的值。【代码】struct sockaddr_in adr_inet;inet_aton(”23”, &adr_inet.sin_addr);6、inet_ntoa函数#include #include #include char * inet_ntoa (struct in_addr addr);作用:把套接口中的IP地址转换成字符串形式的点分十进制形式。返回值是函数内部定义的静态变量,一直到下次调用本函数前一直有效,这个特点在多线程程序中容易引入竞争窗口。【代码】struct sockaddr_in adr_inet;printf(“IP is: %s”, inet_ntoa(adr_inet.sin_addr);7、inet_network 函数#include #include #include unsigned long inet_network(const char * addr);作用:将点分十进制的IP地址转换成【主机字节序】的32位二进制IP地址,如果参数不正确,将返回 0xFFFFFF008、inet_lnaof 函数#include #include #include unsigned long inet_lnaof(struct in_addr addr);将套接口地址中的IP地址(网络字节序)转换成没有网络位的主机ID(主机字节序)。9、inet_netof 函数#include #include #include unsigned long inet_netof (struct in_addr addr);将套接口地址中的IP地址(网络字节序)转换成网络ID(主机字节序)。函数的返回值是右端对齐的,主机位被移出。10、inet_makeaddr函数#include #include #include struct in_addr inet_makeaddr (int net, int host);net:网络位,右端对齐的主机字节序。host:主机位,主机字节序。返回值是网络字节序。第四章 套接口的类型与协议1、协议族/地址族socket/socketpair 函数的domain 参数指定了所使用的协议族,而不是某个特定的协议在生成套接口之前,需要给定下面的参数:1)所使用的协议族,例如 PF_INET 表示使用Internet IP 协议族2)所使用的协议族中某个特定的协议,例如 IPPROTO_UDP 表明使用UDP 协议。3)所使用的地址族,例如 AF_INET 表明使用的是特定协议中的 Internet IP 地址。经验表明,对于任何一个给定的协议族,都只有一种地址格式。 AF_XXX:地址族PF_XXX:协议族应该使用诸如 AF_INET 这样的地址族常量,而使用 PF_INET 这样的协议族常量:struct sockaddr_un adr_unix;int sp2;int z = socketpair( PF_LOCAL, SOCK_STREAM, 0, sp);adr_unix.sun_family = AF_LOCAL;2、socket 函数该函数可用于生成所支持的任何协议族的套接口。#include #include int socket( int domain, int type, int protocal);返回:套接口描述符l domain:协议族(PF_LOCAL 本地UNIX套接口协议族,PF_INET 表示Internet IPv4协议族,PF_INET6 表示 IPv6 协议族)l type:套接口类型(SOCK_STREAM、SOCK_DGRAM、SOCK_SEQPACKET、SOCK_RAW)l protocol:协议类型。为0时相当于让内核根据参数domain和type的值选择正确的协议。如果函数调用成功,套接口的描述符就作为函数的返回值,如果返回值为 -1 就表明发生了错误,发生错误的原因值存放在 errno 中。套接口类型说明:l SOCK_SEQPACKET:通常用于X.25或AX.25等非Internet协议。它和SOCK_STREAM有一些差别:SOCK_STREAM 不保留消息的边界,而SOCK_SEQPACKET保留。如果发送端调用两次write发送数据,则接收端也会发生两次接收。domain 为 PF_INET6 时,如果要使用 TCP 协议,则套接口类型为 SOCK_SEQPACKET。SOCK_SEQPACKET套接口有如下特点:1. 保留消息边界。2. 数据字节的接收顺序与发送顺序保持一致。3. 保证发送的数据字节被无错地传送到接收端。4. 数据是通过一对连接的套接口进行传送的。l SOCK_RAW:表示编程者要一个原始的套接口界面,可以对通信和分组进行更直接的控制。需要编程者对协议和底层的分组结构非常熟悉。l SOCK_STREAM:流套接口,有连接。当编程者想在远程套接口上实现流IO时,就可以使用 SOCK_STREAM 套接口选项。套接口中流的概念与UNIX中管道的概念非常相近。写入管道(或套接口)一端的连续字节流可以在另一端接收,字节流中没有分界线,也没有边界;没有记录的长度或块的大小,在接收端也不存在分组的概念,在接收端获得的所有数据都返回到调用者的缓冲区中。有如下特点:1. 不保留消息的边界,接收端无法知道发送端共执行了多少次write调用,也无法知道在接收到的字节流中哪里是某一次write调用的开始和结束。例如发送端可能执行了两次write,分别发送了25和30个字节,而接收端一次性收到了55个字节。2. 有序性,可以保证接收到的数据字节与发送时的顺序完全一致。发送端发送的数据可能有多个包,在网络上传输的时候,先发的包可能后到,而套接口可以保证交给应用程序的数据是有序的。3. 有数据校验功能,能够保证收到的数据都是正确的。这些校验功能是底层处理的,不需要编程人员处理。如果底层进行数据纠错不成功,则发送端和接收端都将收到有关错误的报告。l SOCK_DGRAM:数据报套接口,无连接。有如下特点:1. 分组在发送以后,可能无序地到达接收端。2. 分组可能丢失,系统不会采取任何补救措施,接收端也不会知道分组丢失。3. 数据报分组大小有限制,如果超出限制,则有可能无法传送。4. 分组是在不建立连接的情况下被发送到远程进程的,允许本地进程每次将消息发送给不同IP地址上的同样的端口。3、当需要保留消息的边界时,可以在【本地套接口】上使用 SOCK_DGRAM,由于是本地套接口,不会经过网络传输,所以一般不会丢包。【代码】int s;s = socket( PF_LOCAL, SOCK_DGRAM, 0);4、当套接口生成后,它还处于“无名”状态,也就是说还没有地址。可以调用bind把地址绑定到套接口。5、在domain参数为PF_INET 和 SOCK_STREAM 套接口中,protocol参数为0意味着告诉内核选择 IPPROTO_TCP;如果是 SOCK_DGRAM 套接口类型,则使用 UDP(User Datagram Protocol) 协议。6、有关协议族的一些宏定义在下面的头文件中:#include 而实际上这个文件包含另外一个定义协议宏常量的头文件:/usr/include/bits/socket.h7、如何知道特定的协议族支持哪些套接口类型,可以通过下面步骤完成:1)进入目录:/usr/linux/src2)cd ./net3)ls F4)进入相关协议的目录5)用grep显示该子目录中所有文件中包含 SOCK_ 的行:grep SOCK_*.c8、IPV6(domain为 PF_INET6)支持三种套接口类型:SOCK_STREAM、SOCK_SEQPACKET、SOCK_DGRAM。第五章 为套接口绑定地址1、bind函数bind 的作用在于为无名套接口分配地址,函数语法如下:#include #include int bind( int sockfd, struct sockaddr * my_addr, int addrlen);sockfd:socket函数返回的套接口的描述符。my_addr:分配给套接口的地址(采用通用地址类型:struct sockaddr)addrlen:地址结构的长度如果函数调用成功,就返回0,否则返回-1,错误原因值放在errno中。2、提供给bind的套接口当前必须是无名状态(没有地址),我们不能使用bind为一个已有地址的套接口重新绑定另外一个地址。3、实例代码test5.1.c:int s;struct sockaddr_in adr_inet;s=socket(AF_INET, SOCK_STREAM, 0);memset(&adr_inet, 0, sizeof adr_inet);adr_inet.sin_family = AF_INET;adr_inet.sin_port = htons(9000);inet_aton(“”, &adr_inet.sin_addr);int z = bind ( s, (struct sockaddr *)&adr_inet, sizeof adr_inet);4、由于套接口是OS分配的,套接口只是一个描述符。系统提供了函数,通过套接口描述符可以得到【本地】套接口的地址等信息。#include int getsockname(int s, struct sockaddr * name, socklen_t * namelen);s:套接口描述符name:指向接收缓冲区的指针namelen:这个变量给出了接收缓冲区所能接收的最大字节数,当接收缓冲区写入后,这个值将被更新为实际写入的字节数,因此函数返回后,它可能小于等于原始值。这个参数不要赋常量值。实例:int s;struct sockaddr_in adr_inet;int z = getsockname ( s, (struct sockaddr * ) &adr_inet; sizeof adr_inet);5、获取与本地套接口连接的【远程】套接口的协议地址,可以使用函数getpeername:#include int getpeername(int s, struct sockaddr * name, socklen_t * namelen);6、套接口程序就可以做到只接收来自特定接口的连接请求,而忽视来自其他接口的请求。7、为了给通信指定一个特定的接口,需要做如下工作:(1)用socket函数生成套接口(2)用函数bind将想要接收连接的接口的IP地址绑定到本地套接口。代码例子:int s;struct sockaddr_in adr_inet;s=socket(AF_INET, SOCK_STREAM, 0);memset(&adr_inet, 0, sizeof adr_inet);adr_inet.sin_family = AF_INET;adr_inet.sin_port = htons(9000);inet_aton(“”, &adr_inet.sin_addr);int z = bind ( s, (struct sockaddr *)&adr_inet, sizeof adr_inet);8、怎样才能在任何一个接口上接收连接?(1)用socket函数生成套接口(2)用函数bind将 IP地址 INADDR_ANY绑定到本地套接口。代码例子:int s;struct sockaddr_in adr_inet;s=socket(AF_INET, SOCK_STREAM, 0);memset(&adr_inet, 0, sizeof adr_inet);adr_inet.sin_family = AF_INET;adr_inet.sin_port = htons(9000);adr_inet.sin_addr.s_addr = htonl ( INADDR_ANY );int z = bind ( s, (struct sockaddr *)&adr_inet, sizeof adr_inet);通过这种方式的套接口绑定既可以接收来自任何接口的请求,也可以通过任何本地套接口连接到远程套接口。INADDR_ANY 就是通常所称的通配地址。第六章 面向非连接的协议1、非连接的通信就是在通信建立之前不建立连接,非连接有如下优点:l 不需要建立连接,使用更加简单l 富有弹性,每一次的消息发送都可以定向到不同的接收者l 高效快速,不需要建立和拆除连接,只有消息本身被发送,避免了大量在网络中传递消息分组的开销。l 具备广播能力,可以将一个消息同时向多个接受者发送。缺点如下:l 通信过程不可靠l 多数据报的无序性l 消息的尺寸有限制2、UDP 数据报的最大尺寸略小于 64KB,但实际上许多UNIX主机只提供接近于 32KB 的最大尺寸,甚至还有小到 8KB 的。最终,套接口程序还会把这个最大尺寸限制为接收缓冲区的大小,许多程序限制 UDP 消息尺寸为 512 字节或略小于 512 字节。3、对于一个大尺寸的 UDP 分组,它将被分解为若干个较小的 IP 分组,然后在接收端重新组装,为了容纳接收到的分组,需要为重新组装的过程分配缓冲,而且对缓冲区的使用还有时间限制,这中间的任何一个条件不满足,都会引起 UDP 分组被丢弃。4、发送 UDP 数据:sendto 函数#include #include int sendto ( int s, const void * msg, int len, unsigned flags, const struct sockaddr * to, int tolen);s:套接口描述符,由socket函数生成。msg:指向容纳所要发送的数据报信息的缓冲区的指针。len:数据报信息的长度(字节数)。flags:指定一些选项,一般情况下置为0即可。to:指向某通用套接口地址的指针,这个地址是我们在程序中所指定的接收者的地址。tolen:地址参数to的长度。如果函数sendto调用成功,它的返回值就是所发送的字节数,如果调用不成功,函数的返回值为-1,错误值存在errno中。发送数据仅仅是将数据报发送出去,但并不能说明它已经被成功地接收了。flags取值:flags十六进制含义00x0000正常无特定选项MSG_OOB0x0001处理带外数据MSG_DONTROUTE0x0004旁路路由,使用直接接口MSG_DONTWAIT0x0040非阻塞,等待写入MSG_NOSIGNAL0x4000当另一端断开连接时,不生成SIGPIPE信号5、recvfrom 函数recvfrom 函数使我们能够在接收数据报的同时,也能得到发送者的地址:#include #include int recvfrom (int s, void * buf, int len, unsigned flags, struct sockaddr * from, int * fromlen);s:接收数据报的套接口的描述符buf:指向容纳接收数据报信息缓冲区的指针len:接收缓冲区的最大长度(字节数)flags:选项标志,一般情况下置为0from:指向套接口地址结构的指针,从这个地址结构可以获得发送者的地址fromlen:地址参数from 的长度,在调用recvfrom之前,这个指针所指向的整型值必须赋值以from地址结构的最大字节数;当函数返回后,这个整型值将被实际的地址大小所替代,也就是说fromlen既是如参也是出参。所以不要使用指向常量的指针。如果函数recvfrom调用成功,它的返回值就是所接收的字节数,如果调用不成功,函数的返回值为-1,错误值存在errno中。flags取值:flags十六进制含义00x0000正常MSG_OOB0x0001处理带外数据MSG_PEEK0x0002读入一个数据报,但不从内核中删除MSG_WAITALL0x0100进行阻塞,直到所有的要求满足MSG_ERRQUEUE0x2000从错误队列中接收一个分组MSG_NOSIGNAL0x4000当另一端断开连接时,不生成SIGPIPE信号6、UDP数据报服务器程序【代码】s = socket(AF_INET, SOCK_DGRAM, 0);bind ( s, (struct sockaddr *) &adr_inet, len_inet);for( ; ; )z = recvfrom ( s, dgram, sizeof dgram, 0, (struct sockaddr *)&adr_clnt, &len_inet);z = sendto ( s, dtfmt, strlen(dtfmt), 0, (struct sockaddr * )&adr_clnt, len_inet);服务器程序嗲用recvfrom,函数将阻塞,直到服务器接收到一个数据报。7、UDP数据报客户程序【代码】s = socket ( AF_INET, SOCK_DGRAM, 0);for( ; ; )z = sendto ( s, dgram, strlen(dgram), 0, (struct sockaddr * )&adr_srvr, len_inet );z = recvfrom ( s, dgram, sizeof dgram, 0, (struct sockaddr *)&adr, &x);客户端程序省略了bind函数,这样程序可以选择任何一个接口进行发送,在效果上,套接口就像被绑定了通配套接口地址,当程序等待响应时,他也可以使用任何一个接口来接收数据报,这时,套接口的端口号也是通配的。使用bind明确地指定通配地址,可以通过使用INADDR_NONE 达到目的,另外,为得到一个通配的端口号,可以将端口号指定为0,所以,指定IP和端口号分别为 INADDR_NONE和0所达到的效果和不调用bind是相同的。通配端口号则是从目前所有可用的端口号中随机挑选的。一般客户端可以不绑定地址,使用通配地址,而服务端为了使客户端有明确的地址,一般需要绑定地址。8、如果不清楚自己的终端使用什么字符作为文件结束符,可以执行下面的命令:stty a在结果中查找 eof = D 之类的字符串第七章 面向连接的协议客户端1、TCP/IP 的一个亮点就在于它的通信信道是可靠的,并且它对数据的传输也是有序的,在连接建立后,应用程序对套接口的读写就无需担心以下问题:l 分组的丢失l 超时和重发l 重复的分组l 分组的接收顺序混乱l 流控2、为了将数据安全地传送到远端,我们的程序只需做以下工作:l 与远程套接口建立 TCP/IP 连接。l 发送大量的数据。l 关闭套接口。丢失的分组会自动重发,直到被远程主机正确地接收。3、流控制TCP协议逻辑上位于 IP 协议的上层,它就像一个监督人一样,确保接收端不接收大于自身处理能力而导致过载的多余分组,当接收端感到目前的处理能力吃紧时,它就会通知发送端暂时不要发送数据;当它感觉目前的处理能力有富裕时,会通知发送端可以再次开始发送,这就是所谓的“流控制”。4、在Linux系统中有一个名为 /etc/services 的文件,这个文件将某个特定的 Internet 服务名映射到协议的端口号,它的路径名可以通过C语言的宏 _PATH_SERVICES 得到:#include printf (“File path is %s”, _PATH_SERVICES );这个文件的每一行都描述了一个 Internet 服务,它的格式如下:服务名 端口号/协议 别名Service-name:大小写敏感,易于理解的服务名Port:该服务的端口号Protocol:指定所使用的协议种类。Alias:别名,别名间使用制表符或空格分割,最多不能超过35个例如: ftp 21/tcptelnet 23/tcp5、getservent 函数#include struct servent * getservent ( void );函数的返回值是指向代表 /etc/services 文件中某一条目的结构的指针,当到达文件末尾时,就返回 NULL 指针,如果有错误发生,也返回NULL指针,错误的原因值在 errno 中getservent 所返回的指针仅在再次调用getservent之前有效。struct servent char * s_name;char * s_aliases; / 指向字符串数组的指针,最后一个指针为 NULLint s_port; / 网络字节序char * s_proto;【代码:打印出 /etc/services 文件的每个条目】int x;struct servent * sp;for(;)if ( ! (sp = getservent () ) break;printf (“service:%s, port : %d, protocol:%s ”, sp-s_name, ntohs ( sp-s_port), sp-s_proto);for( x=0; sp-s_aliasesx!=NULL; x+) printf(“%s”, sp-s_aliasesx);6、setservent 函数#include void setservent ( int stayopen );函数的作用是回卷到/etc/services 文件的开始处。stayopen 非0时,表示不需要重新打开 /etc/services 文件,而只需要对文件进行回卷,这样可以提高性能;为0时,表示将现关闭已经打开的 /etc/services 文件,然后重新打开,并为调用 getservent 做好准备。7、函数 endservent 用于关闭/etc/services 文件#include void endservent ( void );8、getservbyname 函数通过名字和协议查询服务#include struct servent * getservbyname ( const char * name, const char * proto );name:要查询的服务名称,例如:telnetproto:使用的协议,例如tcp如果函数查找不到对应的条目,返回值就是NULL,否则返回一个指向 servent 结构的指针。这个返回的指针仅在再次调用 getservbyname之前有效。9、getservbyport 通过端口和协议查询服务#include struct servent * getservbyport( int port, const char * proto );如果函数查找不到对应的条目,返回值就是NULL,否则返回一个指向 servent 结构的指针。这个返回的指针仅在再次调用 getservbyport之前有效。10、/etc/protocols 文件包含已定义的Internet协议值。#include struct protoent * getprotoent ( void );struct protoent char * p_name; / 协议名,例如 “tcp”char * p_aliases; / 别名清单char p_proto; / 协议号,例如tcp的协议号是6这个函数每一次调用返回 /etc/protocols 中的一个条目,当到达文件末尾或有错误发生时,就返回NULL函数 getprotoent 返回的指针仅在再次调用 getprotoent 之前有效。11、setprotoent 对打开的文件进行回卷#include struct protoent * setprotoen

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论