




版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
1、第一章 TCP/IP简介1. OSI模型和TCP/IP模型2. TCP连接的建立TCP连接的建立需要经过3次数据传输(三次握手),步骤如下:(1) 服务器必须已经准备好接收客户的连接请求,这通过调用socket、bind和listen函数来完成。客户通过调用connect函数进行主动打开,这引起客户端发送一个SYN分节到服务器端。(2) 服务器收到客户端发来的SYN分节后,必须发送ACK对其进行确认,同时发送一个自己的SYN分节给客户端,表示接受客户端建立连接的请求。(3) 客户端发送ACK确认服务器SYN,建立连接成功。3. TCP连接的终止当数据传输完毕后,TCP需要发送4个分节终止该连接
2、,释放TCP连接的步骤如下:(1) 客户端应用进程调用close,调用close的结果就是发送一个FIN分节主动关闭连接。(2) 服务器收到FIN后执行被动关闭,发送ACK对客户端的FIN分节进行确认。(3) 当服务器将待发的数据发送完后,调用close关闭它的套接字,这导致它的TCP发送一个FIN分节给客户端。(4) 接收到服务器的FIN分节后,对其发送一个ACK确认分节,当前的连接被彻底关闭。第二章 套接字编程简介1. 套接字的类型套接字支持各种通信协议,目前Linux系统常用的协议有以下两种:(1) INET:IP版本4。(2) INET6:IP版本6。套接字类型是指创建套接字的应用程序
3、要使用的通信服务的类型。Linux系统支持多种套接字类型,最常用的有以下几种:(1) SOCK_STREAM:流式套接字,提供面向连接、可靠的数据传输服务,数据按字节流、按顺序收发,保证数据在传输过程中无丢失、无冗余。TCP协议支持该套接字。(2) SOCK_DGRAM:数据报套接字,提供面向无连接的服务,数据收发无序,不能保证数据的准确到达。UDP协议支持该套接字。(3) SOCK_RAW:原始套接字。允许对低于传输层的协议或物理网络直接访问例如可以接收和发送ICMP报。常用于检测新的协议。第三章 基本TCP套接字编程1. TCP套接字编程TCP套接字编程中,服务器端实现的步骤如下:(1)
4、使用socket()函数创建套接字。(2) 将创建的套接字绑定到指定的地址结构。(3) Listen()函数设置套接字为监听模式,使服务器进入被动打开的状态。(4) 接受客户端的连接请求,建立连接。(5) 接收、应答客户端的数据请求。(6) 终止连接。客户端实现的步骤相对比较简单:(1) 使用socket()函数创建套接字。(2) 调用connect()函数建立一个及TCP服务器的连接。(3) 发送数据请求,接收服务器的数据应答。(4) 终止连接。2. socket()函数客户端服务器端都存在,产生TCP套接字,作为TCP通信的传输端点#include <sys/socket.h>
5、int socket(int family, int type, int protocol)返回:非负套接字(sockfd)【0,1】成功;-1出错。family:协议族;type:套接字类型; protocol:一般为0,除原始套接字外。 Family(确定协议类型)type(指明产生套接字类型)AF_INET IPv4协议SOCK_STREAM 字节流套接口AF_INET6IPv6协议SOCK_DGRAM 数据报套接口【AF_LOCALunix域协议】SOCK_RAW 原始套接口AF_ROUTE 路由套接口【AF_KEY 密钥套接口】3. bind() 绑定函数绑定函数的作用就是为调用so
6、cket()函数产生的套接字分配一个本地协议地址,建立地址及套接字的对应关系。#include <sys/socket.h>int bind(int sockfd, const struct sockaddr *addr, socklen_len len)返回:0成功;-1出错并置errno Sockfd:套接字函数返回的套接字描述符server:指向特定协议的地址结构的指针addrlen:套接字地址结构的长度n 该函数指明套接字将使用本地的哪一个协议端口进行数据传送(IP地址和端口号),注意:协议地址addr是通用地址。n 一般而言,服务器调用此函数,而客户则很少调用它。n 绑定
7、地址时,可以指定地址和端口号,也可以指定其中之一,甚至一个也不指定。通配地址:INADDR_ANY,其值一般为0,它通知内核选择IP地址。IP地址端口结果n 通配地址0内核选择IP地址和端口号n 通配地址非0内核选择IP地址,进程指定端口n 本地IP 0进程指定IP地址,内核选择端口n 本地IP 非0进程指定IP地址和端口号n 若指定端口号为0,调用函数bind时,内核选择一个临时端口(在实际中,端口号都要指定);但若指定一个通配IP地址,则直到套接字已连接(TCP)或数据报已在套接字上发出(UDP),内核才选择一个本地IP地址。strict sockaddr_in addr;int port
8、 = 1234;int opt = SO_REUSEADDR;setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt); bzero(&server,sizeof(server);addr.sin_family = AF_INET;addr.sin_addr.s_addr = htonl(INADDR_ANY);addr.sin_port = htons(port);if (bind(fd, (struct sockaddr *)&addr, sizeof(addr) = -1)/* 错误处理 */4. lis
9、ten()监听函数#include <sys/socket.h>int listen(int sockfd, int backlog)返回:0成功;-1出错并置errno值; Sockfd:要设置的描述符backlog:规定请求队列中的最大连续数,对队列中等待服务请求的数目进行了限制n 函数listen仅被服务器调用,它完成两件事情:n 函数listen将未连接的套接字转化成被动套接字,指示内核应接受指向此套接字的连接请求;n 函数的第二个参数规定了内核为此套接字排队的最大连接个数;n 对于给定的监听套接字,内核要维护两个队列n 未完成连接队列n 已完成连接队列n 两个队列之和不超
10、过backlog;在TCP三次握手中监听套接字两个队列的位置如下图所示5. connect() 连接函数#include <sys/socket.h>int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);返回:0成功;-1出错; Sockfd:套接字描述符addr:指向服务器套接字地址结构的指针 addrlen:套接字地址结构的大小n 函数connect激发TCP的三路握手过程;仅在成功或出错返回;错误有以下几种情况:(1)如果客户没有收到SYN分节的响应,则返回ETIMEDOUT。(2)如果
11、对客户的SYN的响应是RST,则表明该服务器主机在指定的端口上没有进程在等待及之相连。函数返回错误ECONNREFUSED;(3)如果客户发出的SYN在中间路由器上引发一个目的地不可达ICMP错误,客户上的内核保存此消息,并按第一种情况,连续发送SYN,直到规定时间,返回保存的消息(即ICMP错误)作为EHOSTUNREACH或ENETUNREACH错误返回给进程6. accept()#include <sys/socket.h>int accept(int sockfd, struct sockaddr *cliaddr, socklen_t *addrlen);返回:非负描述字
12、(connfd)1,0OK;-1出错;Listenfd:套接字描述符cliaddr:套接字地址结构 addrlen:套接字地址结构长度n accept函数由TCP服务器调用;从已完成连接队列头返回下一个已完成连接;如果该队列空,则进程进入睡眠状态。n 函数返回的套接字为已连接套接字,应及监听套接字区分开来n 【该函数最多返回三个值:一个既可能是新套接字也可能是错误指示的整数,一个客户进程的协议地址(由cliaddr所指),以和该地址的大小(这后两个参数是值结果参数);也就是说,】服务器可以通过参数cliaddr来得到请求连接并获得成功的客户的地址和端口号;7. close()#include
13、<unistd.h>int close(int sockfd);返回:0OK;-1出错;Sockfd:要关闭的描述符n close函数缺省功能是将套接字做上“已关闭”标记,并立即返回到进程。这个套接字不能再为该进程所用。n 正常情况下,close将引发向TCP的四分节终止序列,但在终止前将发送已排队的数据;如果套接字描述符访问计数在调用close后大于0(在多个进程共享同一个套接字的情况下),则不会引发TCP终止序列(即不会发送FIN分节);8. Shutdown()#include <sys/socket.h>int shutdown(int sockfd, int
14、howto);返回:0OK;-1出错,并置相应的errno的值;Sockfd:要关闭的套接字描述符该函数立即发送FIN分节。shutdown根据参数howto关闭指定方向的数据传输;n SHUT_RD:关闭连接的读这一半,不再接收套接字中的数据且现留在接收缓冲区的数据作废;n SHUT_WD:关闭连接的写这一半(半关闭),当留在套接字发送缓冲区中的数据都被发送,后跟tcp连接终止序列,不管访问计数是否大于0;此后将不能在执行对套接字的任何写操作;n SHUT_RDWR:连接的读、写都关闭,这等效于调用shutdown两次,一次调用是用SHUT_RD,第二次用SHUT_WR。9. Read()
15、函数#include <unistd.h>int read(int fd, char *buf, int len);返回:大于0读写字节大小;-1出错;调用函数read时,有如下几种情况:n 套接字接收缓冲区接收数据,返回接收到的字节数;n tcp协议收到FIN数据,返回0;n tcp协议收到RST数据,返回1,同时errno为ECONNRESET;n 进程阻塞过程中接收到信号,返回1,同时errno为EINTR。read(connfd,buff,strlen(buff);10. write() 函数#include <unistd.h>int write(int fd
16、, char *buf, int len);返回:大于0读写字节大小;-1出错;Buf:用于发送信息的缓冲区 len:缓冲区大小调用函数write,有如下几种情况:n 套接字发送缓冲区有足够空间,返回发送的字节数;n tcp协议接收到RST数据,返回1,同时errno为ECONNRESET;n 进程阻塞过程中接收到信号,返回1,同时errno为EINTR。write(connfd,buff,strlen(buff);11. send() 发送函数#include <sys/types.h>#include <sys/socket.h>ssize_t send (int
17、fd, const void *msg, size_t len, int flags);返回:非0发送成功的数据长度;-1出错;flags 是传输控制标志,其值定义如下:n 0:常规操作,如同write()函数n MSG_OOB,发送带外数据(TCP紧急数据)。n MSG_DONTROUTE:忽略底层协议的路由设置,只能将数据发送给及发送机处在同一个网络中的机器上。12. recv() 接收函数#include <sys/types.h>#include <sys/socket.h>ssize_t recv(int fd, void *buf ,size_t len,
18、int flags);返回:大于0表示成功接收的数据长度;0: 对方已关闭,-1:出错。n flags是传输控制标志,其值定义如下:n 0:常规操作,如同read()函数;n MSG_PEEK:只查看数据而不读出数据,后续读操作仍然能读出所查看的该数据;n MSG_OOB:忽略常规数据,而只读带外数据;n MSG_WAITALL:recv函数只有在将接收缓冲区填满后才返回。13. 服务器的三种异常情况(选)(1)服务器主机崩溃时,已有的网络连接上发不出任何东西。n 同时假设应用程序发出数据后,然后阻塞于从套接字读取响应。n 由于服务器主机崩溃,因此客户tcp会持续重传数据分节,试图从服务器接收
19、一个ACK:源自Berkeley的实现将重传12次。当客户tcp最终放弃时,返回给客户一个错误,此时错误是ETIMEDOUT,或者是因为中间路由器判定服务器主机不可达,且以一个目的地不可达的ICMP消息响应,则错误是EHOSTUNREACH或ENETUNREACH。n 通过设置套接字选项可以更改tcp持续重传等待的超时时间。(2)服务器主机崩溃后重启在这种情况下,如果客户在主机崩溃重启前不主动发送数据,那么客户是不会知道服务器已崩溃的。在服务器重启后,客户向服务器发送一个数据分节;由于服务器重启后丢失了以前的连接信息,因此导致服务器主机的tcp响应RST;当客户tcp收到RST,向客户返回错误
20、,ECONNRESET如果客户对服务器的崩溃情况很关心,即使客户不主动发送数据也这样,就需要其他技术支持(3)服务器主机关机当Linux主机关机时,由init进程给所有运行的进程发信号SIGTERMn 如果服务器程序忽略了SIGTERM信号,则init进程会等待一段固定的时间,然后给所有还在运行的程序发信号SIGKILL;服务器将由信号SIGKILL终止,其终止时,所有打开的描述字被关闭,这导致向客户发送FIN分节;客户收到FIN分节后,能推断出服务器将终止服务。第四章 基本UDP套接口编程1. UDP套接字编程使用UDP套接字编程可以实现基于TCP/IP协议的面向无连接的通信,它分为服务器端
21、和客户端两部分,其实现过程如下图所示。在UDP套接字编程中,服务器端实现的步骤如下:(1) 使用socket90函数创建套接字。【socket()int()】(2) 将创建的套接字绑定到指定的地址结构。【bind()】(3) 等待接收客户端的数据请求。【recvfrom()】(4) 处理客户端请求。(5) 向客户端发送应答数据。【sendto()】(6) 关闭套接字。【close()】客户端实现的步骤如下(没有监听,没有接收):(1) 使用socket()函数创建套接字。(2) 发送数据请求给服务器。(3) 等待接收服务器的数据应答。(4) 关闭套接字。2. recvfrom() 函数#inc
22、lude <sys/types.h>#include <sys/socket.h>ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *from, int *fromlen);返回:大于0成功接收数据长度;-1出错;Sockfd buflen:调用socket() 函数生成的描述符,指向缓冲区的指针,读入的字节数from:及之通信的套接字地址结构addrlen:指向整数值的指针,发送者的套接字地址结构的长度n UDP套接字使用无连接协议,因此必须使用recvfrom
23、函数,指明源地址;n flags是传输控制标志,其值定义如下:n 0:常规操作,如同read()函数;n MSG_PEEK:只察看数据而不读出数据;n MSG_OOB:忽略常规数据,而只读取带外数据;n from 和fromlen 是“值结果”参数。3. sendto函数#include <sys/types.h>#include <sys/socket.h>ssize_t sendto(int sockfd, const void *msg, size_t len, int flags, const struct sockaddr *to, int tolen);返回
24、:大于0成功发送数据长度;-1出错;Sockfd:调用socket函数生成的描述符msg:缓冲区指针 len:发送的字节数to:数据将发送的协议地址 tolen:一个整数值,协议地址大小n UDP套接字使用无连接协议,因此必须使用sendto函数,指明目的地址;n flags是传输控制标志,其值定义如下:n 0:常规操作,如同write()函数;n MSG_OOB:发送带外数据;n MSG_DONTROUTE:忽略底层路由协议,直接发送。第五章 并发服务器1. 进程的基本概念进程定义了一个计算的基本单元,可以认为是一个程序的一次运行。它是一个动态实体,是独立的任务。它拥有独立的地址空间、执行堆
25、栈、文件描述符等。一个进程不能访问另一个进程的资源,一个进程的崩溃不会造成其他进程的崩溃;进程又是互相影响的,进程之间可以通过IP C机制相互通信。当进程间共享某一资源时,需注意两个问题:同步问题和通信问题。2. fork函数#include <sys/types.h>#include <unistd.h>pid_t fork(void)返回:父进程中返回子进程的进程ID, 子进程返回0, -1出错fork后,子进程和父进程继续执行fork()函数后的指令。子进程是父进程的副本。子进程拥有父进程的数据空间、堆栈的副本。但父、子进程并不共享这些存储空间部分。如果代码段是只
26、读的,则父子进程共享代码段。如果父子进程同时对同一文件描述字操作,而又没有任何形式的同步,则会出现混乱的状况;父进程中调用fork之前打开的所有描述字在函数fork返回之后子进程会得到一个副本。fork后,父子进程均需要将自己不使用的描述字关闭。3. 进程终止进程的终止存在两个可能:父进程先于子进程终止(init进程领养)子进程先于主进程终止对于后者,系统内核为子进程保留一定的状态信息:进程ID、终止状态、CPU时间等;当父进程调用wait或waitpid函数时,获取这些信息;当子进程正常或异常终止时,系统内核向其父进程发送SIGCHLD信号;缺省情况下,父进程忽略该信号,或者提供一个该信号发
27、生时即被调用的函数。什么叫“僵尸进程”?一个已经终止但是其父进程尚未对其进行善后处理的进程如何避免“僵尸进程的产生”:(原因在于主进程未对子进程进行资源回收)父进程调用SIGCHLD和wait(),waitpid() 来对子进程进行资源回收。4. wait函数#include <sys/types.h>#include <sys/wait.h>pid_t wait(int *stat_loc);返回:终止子进程的ID成功;-1出错;stat_loc存储子进程的终止状态(一个整数);如果没有终止的子进程,但是有一个或多个正在执行的子进程,则该函数将堵塞,直到有一个子进程终
28、止或者wait被信号中断时,wait返回。当调用该系统调用时,如果有一个子进程已经终止,则该系统调用立即返回,并释放子进程所有资源。5. waitpid函数pity waitpid(pid_t pid, int *stat_loc, int options);返回:终止子进程的ID成功;-1出错;stat_loc存储子进程的终止状态;当pid=-1,option=0时,该函数等同于wait,否则由参数pid和option共同决定函数行为,其中pid参数意义如下:-1:要求知道任何一个子进程的返回状态(等待第一个终止的子进程);>0:要求知道进程号为pid的子进程的状态;<-1:要求
29、知道进程号为pid的绝对值的子进程的终止状态Options最常用的选项是WNOHANG,它通知内核在没有已终止进程时不要堵塞。6. exit函数#include <stdlib.h>void exit(int status);本函数终止调用进程。关闭所有子进程打开的描述符,向父进程发送SIGCHLD信号,并返回状态。7. 多进程并发服务器下面用图例说明父进程调用fork生成子进程后,父、子进程对描述符的操作过程。当服务器调用accept() 函数时,连接请求从客户到达服务器时双方的状态如图5-3所示。客户的连接请求被服务器接收后,新的已连接套接字即connfd被创建,可通过此描述符
30、读、写数据,此时双方的状态如图5-4所示。服务器的下一步就是调用fork函数,如图5-5所示,给出了从fork() 函数返回后的状态。此时的描述符listenfd和connfd在父、子进程间共享。接下来就由父进程关闭已连接描述符,由子进程关闭监听描述符,当前双方的状态如图5-6所示。8. 线程线程是进程内的独立执行实体和调度单元,又称为“轻量级”进程(lightwight process);创建线程比进程快10100倍。一个进程内的所有线程共享相同的内存空间、全局变量等信息(这种机制又带来了同步问题),所以一个线程崩溃时,它会影响同一进程中的其他线程。9. pthread_create() 函
31、数pthread_create() 函数用于创建新进程,系统产生一个称为初始线程或主线程的单个线程,额外的线程需要由pthread_create()函数创建。#include <pthread.h>int pthread_create(pthread_t *tid, const pthread_attr_t *attr, void *(*func)(void *), void *arg);返回:成功时为0;出错时为正的Exxx值新线程由线程id标识:tid,新线程的属性attr包括:优先级、初始栈大小、是否应该是守护线程等等。线程的执行函数和调用参数分别是:func和arg;由于线
32、程的执行函数的参数和返回值类型均为void *,因此可传递和返回指向任何类型的指针;常见的返回错误值:EAGAIN:超过了系统线程数目的限制。ENOMEN:没有足够的内存产生新的线程。EINVAL:无效的属性attr值。10. pthread_join() 函数pthread_join()函数及进程的waitpid()函数功能类似,等待一个线程终止。#inlcude <pthread.h>int pthread_join(pthread_t tid, void *status);返回:成功时为0;出错时为正的Exxx值,不设置errortid指向所等待的线程ID,必须指定等待线程的
33、ID,该函数不能等待任意一个线程结束;被等待线程必须是当前进程的成员,并且不是分离的线程和守护线程。pthread_t pthread_self(void);返回:调用线程的线程id;11. pthread_detach() 函数pthread_detach()函数将指定的线程变成分离的。#include <pthread.h>int pthread_detach(pthread_t tid);参数tid指定要设置为分离的线程ID。调用成功返回0,否则返回错误码/12. pthread_exit() 函数pthread_exit()函数用于终止当前线程,并返回状态值。#includ
34、e <pthread.h>void pthread_exit (void *status);参数status指向函数的退出状态。该函数没有返回值。13. 还有两种方法可以使线程终止(1) 启动线程的函数pthread_create()的第3个参数返回。该返回值就是线程的终止状态。(2) 如果进程的main() 函数返回或者任何线程调用了exit() 函数,进程将终止,线程将随之终止。第六章 名字及地址转换编程1. 域名系统域名系统就是通常所说的DNS(DomainNameSystem)系统,而DNS系统主要用于主机名及IP地址间的映射。在DNS中的条目称为资源记录RR(Resour
35、ceRecord),它们主要有以下几类:(1) A。A记录将一个域名地址对应一个32bit的IPv4地址,比如:NETINANET是主机名称,是使用的一台主机;IN表示属于某种类型的记录;是IPv4地址。(2) AAAA。AAAA记录(称为“四A”记录)将主机名映射为128位的IPv6地址。选择“四A”的叫法是由于IPv6的128位地址是32位地址的四倍。比如:NETINAAAA551b: ce3e: e200: 20: 800: 2b37: 6426(3) NS。NS记录用于指定一个域名服务器,它负责定义由哪个域名服务器负责管理维护本区域的记录
36、,如:NSINNS (4) MX。MX记录用于指定一台主机的域名,所有发送到本域名的电子邮件都由这台主机接收。比如: IN MX NET IN MX NET IN MX 前一条记录意思是指定由主机来接收发送到这个域的电子邮件;后两条记录的意思是发送到NET.的电子邮件首先由自己接收,如果失败再由主机接收。(5) PTR。PTR记录(称为“指针记录”)将IP地址映射为主机名。(6)
37、 CNAME。CNAME代表CanonicalName(规范名字),作用是允许主机建立别名2. gethostbyname() 函数找主机名最基本的函数gethostbyname(),函数执行成功,返回一个指向结构hostent的指针,该结构中包含了该主机的所有IPv4地址或IPv6地址;失败返回空指针# include <netdb.h>struct hostent * gethostbyname (const char * hostname);参数hostname是主机的域名地址,函数将查询的结果作为参数返回。返回空指针-失败;如果成功此参数返回的非空指针指向如下的hosten
38、t结构:strut hostentchar * h_name;/*主机的正式名称*/char * * h_aliases;/*主机的别名列表*/inth_addrtype;/*主机地址类型*/inth_length;/*主机地址长度*/char * * h_addr_list;*主机IP地址的列表*/# define h_addr h_addr_list0/*在列表中的第一个地址*/gethostbyname()函数是怎样工作的gethostbyname函数首先在/etc/hosts文件中查找是否有匹配的主机名。如果没有,则根据在域名解析配置文件/etc/resolv.conf中指定的本地域名
39、服务器的地址向本地域名服务器发送地址解析请求。如果本地域名服务器能够解析,则返回UDP数据包说明结果,否则本地域名服务器将向上一层的域名服务器发送域名解析请求。3. gethostbyaddr()函数函数的作用是可以查询指定的IP地址对应的主机域名地址。# include <netdbh> struct hostent * gethostbyaddr(const char * addr, size_t len , int family );返回:若为非空指针,则表示成功;若为空指针,则表示出错,同时设置h_errno。该函数同样返回一个指向结构hostent的指针。参数addr不是
40、char *类型,而是一个真正指向含有IPv4或IPv6地址的结构in_addr或in6_addr的指针;len是此结构的大小,对于IPv4地址为4,对于IPv6地址为16;参数family为AF_INET或AF_INET6。4. getaddrinfo() 函数该函数在库函数中隐藏了所有协议依赖性,因此应用程序只需要处理由getaddrinfo填写的套接口地址结构即可。# include <netdbh>int getaddrinfo (const char * hostname, const char *service, const struct addrinfo * hint
41、s, struct addrinfo * * result);返回:若成功返回0,若出错返回非0。第九章 I/O编程1. I/O模型I/O模型分为5种不同的类型,阻塞I/O、非阻塞I/O、I/O复用、信号驱动I/O和异步I/O。2. select函数允许进程指示内核等待多个事件中的任意一个发生,并仅在一个或多个事件发生或经过指定的时间时才唤醒进程。#include<sys/select.h>#include<sys/time.h>int select(int maxfdp 1, fd_set*readset,fd_set*writeset, fd_set*execeps
42、et,const struct timeval * timeout);返回:准备好描述字的正数目,0为超时,-1为出错。timeval结构,这个结构可以提供秒数和毫秒数成员,形式如下:struct timevallong tv_sec;/second*/long tv_usec; /*microsecond*/这个timeval结构有以下3种可能:(1)永远等待下去:仅在有一个描述字准备好I/O时才返回,因此可以将参数timeout设置为空指针。(2)等待固定时间:在有一个描述字准备好I/O时返回,但不超过由timeout参数所指timeval结构中指定的秒数和微秒数。(3)根本不用等待:检查
43、描述字后立即返回,这称为轮询(polling)。a参数readset、writeset和execeptset指定让内核测试读、写、异常条件的描述字。select函数使用描述字集为参数readset(writeset或exceptset)指定多个描述字,描述字集是一个整数数组,每个数中的每一个对应于一个描述字。操作这些描述字的几个宏:voidFD_ZERO()fd_set * fdset);/*将所有位设为0*/voidFD_SET(int fd,fd_set*fdset);/*将fd位设为1*/voidFD_CLR(int fd,fd_set * fdset);/*将fd位设为0*/intFD
44、_ISSET(int fd,fd_set * fdset):/*检测fd位是否为1*/b参数readset、writeset、exceptset为值结果参数,调用select时,指定我们所关心的描述字,返回时结果指示那些描述字已准备好。c参数maxfdp1指定被测试的描述字的个数,它是被测试的最大描述字加1。3. shutdown函数及close不同处:(1)close将描述字的访问计数减1,仅在此计数为0时才关闭套接口。用shutdown可以激发TCP的正常连接终止序列,而不管访问计数。(2)close终止了数据传送的两个方向:读和写。由于TCP连接是全双工的,有很多时候要通知另一端已完成了
45、数据发送,即使那一端仍有许多数据要发送也是如此。shutdown函数可以仅仅关闭连接的读、写或两个方向都关闭。# include <sys/socket.h>int shutdown(int sockfd, int howto);返回:0表示成功,-1表示出错。参数sockfd为要关闭的套接口描述字。参数howto可以为以下常值:(1)SHUT_RD:关闭连接的读这一半,不再接收套接口中的数据,而且留在套接口接收缓冲区中的数据都作废。进程不能再对套接口执行任何读函数。调用此函数后,TCP套接口接收的任何数据都被确认,但数据本身扔掉。(2)SHUT_WD:关闭连接的写这一半,在TCP
46、场合下,这种情况称为半关闭(half-close)。当前留在套接口发送缓冲区中的数据都被发送,后跟正常的TCP连接终止序列,进程不能再执行对套接口的任何写函数。(3)SHUT_RDWR:连接的读这一半和写这一半都关闭,等同于调用函数shutdown()两次,第一次调用时用SHUT_RD,第二次调用时用SHUT_WR4. 非阻塞connect非阻塞connect有如下用途:(1) 可以在三次握手的同时做一些其他的处理。因为connect至少要花一个往返时间RTT完成。(2) 可以用这种技术同时建立多个连接,这在Web浏览器中很普遍。(3) 由于用select等待连接的完成,因此可以给select
47、设置一个时间限制,从而改变connect默认的超时时间(通常为75s或更多)。非阻塞connect有一些其他需要注意的问题:(1) 即使是非阻塞的,如果连接的服务器在同一台机器上,连接会立即建立。(2) 源自Berkeley的实现有两条及select和非阻塞I/O相关的规则:当连接建立成功时,描述字变成可写;当连接建立出错时,描述字变成既可读,又可写。第十章广播及多播编程1. 广播广播地址用IP地址“55”来表示,代表同一子网内所有的IP地址。广播的优点:1)网络设备简单,维护简单,布网成本低廉2)由于服务器不用向每个客户机单独发送数据,所以服务器流量负载极低。广播的
48、缺点:1)无法针对每个客户的要求和时间和时提供个性化服务。2)网络允许服务器提供数据的带宽有限,无法向众多客户提供更多样化、更加个性化的服务。3)广播禁止允许在Internet宽带网上传输。应用程序只能通过UDP方式发送广播。一般情况下,如果调用sendto,只能向非广播地址发送数据报。如果要发送广播数据报,必须告诉内核,可以通过设置SO_BROADCAST套接口选项来做到这一点。Int on=1;setsockopt(sockfd,SOL_SOCKET,SO_BROADCAST,&on,sizeof(int);2. 单播信息的接收和传递只在两个节点之间进行单播的优点:1)服务器和时响
49、应客户机的请求2)服务器针对每个客户不通的请求发送不通的数据,容易实现个性化服务。单播的缺点:1)服务器针对每个客户机发送数据流,在客户数量大、每个客户机流量大的流媒体应用中服务器不堪重负。2)全部使用单播协议,将造成网络主干不堪重负。3. 多播“多播”也可以称为“组播”多播IP地址就是D类IP地址,即至55之间的IP地址。采用多播方式,可以实现一次传送所有目标节点的数据,也可以达到只对特定对象传送数据的目的。多播是通过D类地址进行的,D类地址的前4位为1110,后面28位为群播的组标识。地址范围 到55特
50、殊的IPv4多播地址: 保留 本子网上所有主机 本子网上所有网关 NTP(网络时间协议)组多播的优点:1)需要相同数据流的客户端加入相同的组共享一条数据流,节省了服务器的负载。具备广播所具备的优点。2)由于组播协议是根据接受者的需要对数据流进行复制转发,所以服务端的服务总带宽不受客户接入端带宽的限制。3)此协议和单播协议一样允许在Internet宽带网上传输。多播的缺点:1)及单播协议相比没有纠错机制,发生丢包错包后难以弥补,但可以通过一定的容错机制和QOS加以弥补。2)现行网络虽然都支持组播的传输,但在客户认证、QOS等
51、方面还需要完善,这些缺点在理论上都有成熟的解决方案,只是需要逐步推广应用到现存网络当中。实战检验利用I/O复用技术编写一个处理多个客户数据的程序,要求实现对客户输入数据的反转回送。(P.149)TCP_client.c (客户端文件)#include <stdio.h>#include <string.h>#include <unistd.h>#include <stdlib.h>#include <sys/types.h> /* See NOTES */#include <sys/socket.h>#include &l
52、t;netinet/in.h>#include <netinet/ip.h> /* superset of previous */#define backlog 5#define BUFFSIZE 1024int main(int argc, char *argv)if(argc !=3) perror("argc != 3"); exit(EXIT_FAILURE); /*1.创建套接字*/ int sockfd = socket(PF_INET, SOCK_STREAM, 0); if(-1 = sockfd) perror("socket&
53、quot;); exit(EXIT_FAILURE); struct sockaddr_in serv_addr; memset(&serv_addr, 0, sizeof(serv_addr); serv_addr.sin_family = AF_INET; serv_addr.sin_port = htons(atoi(argv2); serv_addr.sin_addr.s_addr = inet_addr(argv1); if(-1 = connect(sockfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr) per
54、ror("connect"); close(sockfd); exit(EXIT_FAILURE); printf("connect success!n"); char buffBUFFSIZE; int recvbytes, sendbytes; while(1) fgets(buff, sizeof(buff), stdin); if(0 = strncmp(buff, "quit", 4) printf("client quit!n"); break; sendbytes = send(sockfd, buf
55、f, strlen(buff)+1, 0); if(sendbytes <= 0) perror("send"); break; close(sockfd); return 0;TCP_server.c (服务器文件)#include <stdio.h>#include <string.h>#include <unistd.h>#include <stdlib.h>#include <sys/types.h> /* See NOTES */#include <sys/socket.h>#include <netinet/in.h>#include <netinet/ip.h> /* superset of previous */#include <sys/time.h>#include <ar
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- GB/T 11856.1-2025烈性酒质量要求第1部分:威士忌
- GB 19081-2025饲料加工系统粉尘防爆安全规范
- 劳动合同范本 派遣
- 养殖场清粪车购销合同范本
- 区域销售协议合同范本医药
- 包装印刷公司采购合同范本
- 买宅地合同范例
- 上海住房合同范本
- 个人与团队提成合同范本
- 线上按摩技师合同范本
- 部编版小学(2024版)小学道德与法治一年级下册《有个新目标》-第一课时教学课件
- 税法(第5版) 课件 第13章 印花税
- 2024-2025学年广州市高二语文上学期期末考试卷附答案解析
- 咖啡店合同咖啡店合作经营协议
- 2025年山东铝业职业学院高职单招职业技能测试近5年常考版参考题库含答案解析
- 全套电子课件:技能成就梦想
- 2024年教育公共基础知识笔记
- 2025年江苏农林职业技术学院高职单招职业技能测试近5年常考版参考题库含答案解析
- 异构数据融合技术-深度研究
- 北京市朝阳区2024-2025学年七年级上学期期末考试数学试卷(含答案)
- 2024年湖南汽车工程职业学院单招职业技能测试题库标准卷
评论
0/150
提交评论