《unix-os教学课件》第九章 unix网络程序设计.ppt_第1页
《unix-os教学课件》第九章 unix网络程序设计.ppt_第2页
《unix-os教学课件》第九章 unix网络程序设计.ppt_第3页
《unix-os教学课件》第九章 unix网络程序设计.ppt_第4页
《unix-os教学课件》第九章 unix网络程序设计.ppt_第5页
已阅读5页,还剩111页未读 继续免费阅读

下载本文档

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

文档简介

第13章 Unix网络程序设计,掌握:网络体系结构、地址标识等基础 理解:套接字的基本功能和作用 掌握:基本的套接字函数编程方法 掌握:客户端程序设计方法 掌握:并行服务器程序的设计方法,13.1 计算机网络基本知识,通信子网 信息传输控制 信息流量控制 信息传递路径控制 资源子网 软件信息和数据信息的共享 硬件资源的共享 负载均衡的管理,计算机网络的目的,计算机网络:把分布在不同地理位置上的,具有独立功能的多台计算机、终端及其附属设备,用通信设备和通信线路连接起来,再配以相应的网络软件,以实现计算机资源共享。,互联网程序设计,电子科大计算机学院,LAN物理拓扑结构,网络体系结构,应用层,表示层,会话层,传输层,网络层,数据链路层,物理层,应用层,IPv4, IPv6,网络介质层,TCP,UDP,OSI模型,TCP/IP模型,OSI vs TCP/IP,通过路由器连接的两个网络,FTP 客户,TCP,IP,以太网驱 动程序,FTP 服务器,TCP,IP,以太网驱 动程序,以太网驱 动程序,以太网驱 动程序,IP,FTP协议,TCP协议,IP协议,IP协议,以太网,客户,服务器,数据进入协议栈时的封装,用户数据,用户数据,App头部,用户数据,TCP头部,用户数据,TCP头部,IP头部,用户数据,TCP头部,IP头部,以太网 头部,以太网 尾部,以太网帧,401500字节,FTP 客户,TCP,IP,以太网驱 动程序,TCP段,IP分组,网络中的几类地址,物理地址:即MAC地址 逻辑地址:即IP地址 端口地址: 域名地址,掌握IP地址与域名地址的关系,物理地址,48位:24位OUI,24位由厂商分配 平面地址,无结构 全球唯一 局部范围寻址 存在于数据链路层,局域网中,各个主机是以MAC地址进行数据递交,IP地址,IP地址标识着网络一个主机的位置。每个IP地址都是由32位(或128位)组成,分成两部分:网络号、主机号。 全球唯一,寻址容易 两种表示形式:二进制(计算机内部)、点分十进制,端口地址,process,process,process,process,process,process,IP地址平面,A机,B机,端口地址(cont.),TCP和UDP使用16位的端口号(用无符号整型表示) 为防止端口使用的混乱,将端口分为以下三类: “众所周知”的端口:01023,由IANA统一控制 注册的端口:102449151,这些端口虽不由IANA控制,但IANA登记这些端口的使用 动态或私有的端口:4915265535,DNS:域名地址,层次名字空间 便于记忆和使用 计算机通信时无法使用 域名地址解析: 名字到IP地址的解析(gethostbyname) IP地址到域名的解析(gethostbyaddr),网络中通信双方,网络中通信双方由以下五元组唯一确定: (本地IP地址、本地端口号、远程IP地址、远程端口号、协议类型),传输层协议的区别,UDP:用户数据报协议 UDP提供无连接服务 UDP缺乏可靠性支持,应用程序必须实现确认、超时、重传、流控等功能 UDP面向记录服务,UDP数据报格式,源端口,目的端口,长度,校验和,数据,0,15,31,#ifdef _FAVOR_BSD struct udphdr u_int16_t uh_sport; /* source port */ u_int16_t uh_dport; /* destination port */ u_int16_t uh_ulen; /* udp length */ u_int16_t uh_sum; /* udp checksum */ ; #else struct udphdr u_int16_t source; u_int16_t dest; u_int16_t len; u_int16_t check; ; #endif,传输层协议的区别,TCP:传输控制协议 TCP是面向连接的 TCP提供可靠性,实现了丢失重传,RTT的估算。 TCP通过给所发送数据的每一个段管理一个序列号进行排序。 TCP提供流量控制和拥塞控制:通告窗口、拥塞窗口。,TCP协议数据段格式,HLEN,窗口,序列号,确认号,源端口,目的端口,保留,码位,校验和,紧急指针,选项,填充字节,数据,U R G,A C K,P S H,R S T,S Y N,F I N,0,15,31,7,首部,数据区,TCP协议数据段格式(续),struct tcphdr WORD SourPort; WORD DestPort; DWORD SeqNo; DWORD AckNo; BYTE HLen; BYTE Flag; WORD Window; WORD ChkSum; WORD UrgPtr; /* Put options here. */ ;,TCP连接的建立,TCP连接的过程: 服务器必须准备好接受外来的连接。通过调用socket, bind, listen函数完成。称为被动打开。 客户通过调用connect进行主动打开。这引起客户TCP发送一个SYN分组,告诉服务器客户将在连接中发送的数据的初始序列号。 服务器必须确认客户的SYN,同时自己也得发送一个SYN分组。服务器以单个分节向客户发送SYN和对客户的SYN的ACK。 客户必须确认服务器的SYN。,TCP三路握手,客户,服务器,Socket Connect(阻塞) (主动打开),Socket,bind,listen (被动打开),SYN J,SYN K, ack J+1,ack K+1,Accept返回 Read阻塞,Connect返回,TCP连接关闭,客户,服务器,Close 主动关闭,被动关闭 Read返回0,FIN M,ack N+1,Close,ack M+1,FIN N,网络通信的实现原理系统调用,系统调用:应用程序和操作系统传递控制权(函数调用) 目的是从操作系统获得服务 一定的权限控制,应用程序1,由应用程序调用的系统函数,包括TCP/IP协议软件的操作系统内核,应用程序2,应用程序3,在用户地址空间中的应用程序,系统调用接口,系统地址中的协议软件,网络通信的两种基本方法,使用新的系统调用来访问TCP/IP 对于每个概念性的操作实现一个系统调用 创建新的系统调用并不明智 使用一般的I/O调用来访问TCP/IP 使用一般的I/O调用,但是进行了扩充,既可以用于I/O,又可以用于网络协议 混合方法 尽可能使用基本的I/O功能 增加一些函数来实现其它操作,LINUX中提供的基本I/O功能,理解基本I/O如何扩展功能,六个基本的系统函数:,操作,含义,Open Close Read Write Lseek Ioctl,为输入或输出操作准备一个设备或者文件 终止使用以前已打开的设备或者文件 从输入设备或者文件中得到数据 数据从应用程序存储器传到设备或文件中 转到文件或者设备中的某个指定位置 控制设备或者用于访问该设备的软件,基本I/O举例,int desc desc=open(“filename”, O_RDWR, 0) read(desc,buffer,128) close(desc),将Linux I/O用于TCP/IP,扩展文件描述符:可以用于网络通信 扩展read和write:可以用于网络接收/发送数据 额外功能的处理,增加新系统调用: 指明本地和远端的端口,远程IP地址 使用TCP还是UDP 启动传输还是等待传入连接 可以接受多少传入连接,socket套接字,TCP/IP协议存在于OS中,网络服务通过OS提供 在OS中增加支持TCP/IP的系统调用Berkeley套接字 如Socket,Connect,Send,Recv等函数 1981提出BSD4.1 UNIX,本书基于BSD4.4 版本,socket套接字API,来自UNIX系统 Berkeley套接字 ARPA要求伯克利分校将TCP/IP移植到UNIX中 需要创建一个接口,便于应用程序使用这个接口 进行网络通信 尽可能使用现有的系统调用,同时添加新的系统调用支持TCP/IP 这个系统被称为BSD UNIX 套接字,成为事实上的标准,指明一个协议接口,考虑:接口是专门针对TCP/IP, 还是可以为其它协议所用呢? 方法一:定义专门支持TCP/IP通信的一些函数 方法二:定义支持一般网络通信的函数,用参数是TCP/IP通信作为一种特例 Berkeley套接字使用第二种办法,具有通用性,TCP/IP是一个族 PF_INET 争论: 通用性没有必要,使应用程序难以阅读 通用性可以使程序员免于了解协议族的细节,套接字:描述符,OS将文件描述符实现为一个指针数组,指向一个内部的数据结构:进程描述符表的下标 套接字和文件类似,每个活动套接字使用一个小整数标识,进程的文件描述符和套接字描述符值不能相同 socket函数:创建套接字描述符(不是creat函数),针对套接字的系统数据结构,调用socket将创建一个新的描述符条目 结构的许多字段是其他的系统调用来填,0: 1: 2: 3: 4:,描述符表 (一个进程一张),family: PF_INET,service: SOCK_STREAM,Local IP:,Remote IP:,Local port:,Remote port:,:,主动套接字和被动套接字,创建方式相同,使用方式不同 等待传入连接的套接字被动,如服务器套接字 发起连接的套接字主动,如客户套接字 指明端点地址:创建时不指定,使用时指明 TCP/IP需要指明协议端口号和IP地址 TCP/IP协议族:PF_INET TCP/IP的地址族:AF_INET,类属地址结构,套接字系统定义的一般化的地址结构 (地址族,该族的端点地址),套接字的普通C定义结构 struct sockaddr u_char sa_len; u_short sa_family; char sa_data14;/14B 通用的地址结构(只是很适用于AF_INET族中的地址),TCP/IP的地址定义 struct sockaddr_in u_char sin_len; u_short sin_family; u_short sin_port;/2B struct in_addr sin_addr;/4B char sin_zero8;/8B未使用 IP专用的结构,套接字API的主要系统调用,套接字调用分为两组: 主调用:提供对下层功能的访问 实用例程:提供帮助 套接字带有参数,允许以多种方式来使用它们。 可被客户或服务器使用 可被TCP或UDP使用 可被特定或非特定的远程端点地址所使用,一些主调用的套接字API,功能:创建一个新的套接字,返回套接字描述符 参数说明: domain:域类型,指明使用的协议栈,如TCP/IP使用的是 PF_INET; type: 指明需要的服务类型: SOCK_DGRAM: 数据报服务,UDP协 SOCK_STREAM: 流服务,TCP协议 protocol:一般都取0 举例:,s=socket(PF_INET, SOCK_STREAM, 0),int socket( int domain, int type, int protocol),一些主调用的套接字API,功能: 同远程服务器建立主动连接,成功时返回0,若连接失败返回-1。 参数说明: Sockfd:套接字描述符,指明创建连接的套接字 Server_addr:指明远程端点:IP地址和端口号 sockaddr_len :地址长度 举例:,connect(s, remaddr, remaddrlen),int connect(int sockfd, struct sockaddr *server_addr, int sockaddr_len),功能: 在TCP连接上发送数据,返回成功传送数据的长度,出错时返回-1。 send会将外发数据复制到OS内核中,也可以使用send发送面向连接的UDP报文。 参数说明: sockfd:套接字描述符 data:指向要发送数据的指针 data_len:数据长度 flags:一直为0 举例,一些主调用的套接字API,send(s,req,strlen(req),0);,int send(int sockfd, const void * data, int data_len, unsigned int flags),功能:基于UDP发送数据报,返回实际发送的数据长度,出错时返回-1 参数说明: sockfd:套接字描述符 data:指向要发送数据的指针 data_len:数据长度 flags:一直为0 remaddr:远端地址:IP地址和端口号 remaddr_len :地址长度 举例:,一些主调用的套接字API,sendto(sockfd,buf,sizeof(buf),0,(struct sockaddr *),int sendto(int sockfd, const void * data, int data_len, unsigned int flags, struct sockaddr *remaddr,int remaddr_len),功能: 从TCP接收数据,返回实际接收的数据长度,出错时返回-1。如果没有数据,将阻塞,如果收到的数据大于缓存的大小,多余的数据将丢弃。 参数说明: Sockfd:套接字描述符 Buf:指向内存块的指针 Buf_len:内存块大小,以字节为单位 flags:一般为0 举例:,一些主调用的套接字API,recv(sockfd,buf,8192,0),int recv(int sockfd, void *buf, int buf_len,unsigned int flags);,功能:从UDP接收数据,返回实际接收的字节数,失败时返回-1. 参数说明: sockfd:套接字描述符 buf:指向内存块的指针 buf_len:内存块大小,以字节为单位 flags:一般为0 from:远端的地址,IP地址和端口号 fromlen:远端地址长度 举例:,一些主调用的套接字API,recvfrom(sockfd, buf, 8192, 0, (struct sockaddr *),int recvfrom(int sockfd, void *buf, int buf_len, unsigned int flags, struct sockaddr *from, int fromlen),功能: 撤销套接字; 如果只有一个进程使用,立即终止连接并撤销该套接字,如果多个进程共享该套接字,将引用数减一,如果引用数降到零,则撤销它。 参数说明: sockfd:套接字描述符 举例:close(socket_descriptor),一些主调用的套接字API,int close (int sockfd),int bind(int sockfd, struct sockaddr * my_addr, int addrlen),功能:为套接字指明一个本地端点地址 TCP/IP协议使用sockaddr_in结构,包含IP地址和端口号;服务器使用它来指明熟知的端口号,然后等待连接。 参数说明: Sockfd:套接字描述符,指明创建连接的套接字 my_addr:本地地址,IP地址和端口号 addrlen :地址长度 举例:,一些主调用的套接字API,bind(sockfd, (struct sockaddr *),功能:面向连接的服务器使用它将一个套接字置为被动模式,并准备接收传入连接。用于服务器,指明某个套接字连接是被动的 参数说明: Sockfd: 指明创建连接的套接字 input_queue_size: 该套接字使用的队列长度, 指定在请求队列中允许的最大请求数 举例:listen(sockfd,20),一些主调用的套接字API,int listen(int sockfd,int input_queue_size),功能:获取传入连接请求,返回新的连接的套接字描述符。 为每个新的连接请求创建了一个新的套接字,服务器只对新的连接使用该套接字,原来的监听套接字接受其他的连接请求。 新的连接上传输数据使用新的套接字,使用完毕,服务器将关闭这个套接字。 参数说明: sockfd:套接字描述符,指明正在监听的套接字 addr:提出连接请求的主机地址 addrlen:地址长度 举例:,一些主调用的套接字API,new_sockfd = accept(sockfd, (struct sockaddr *),int accept(int sockfd, void *addr, int addrlen),套接字API中的主要系统调用,read和write函数 在UNIX和Linux中,可以代替recv和send。 小结:,socket* connect* send(write)* recv(read)* close* bind,listen accept recvmsg sendmsg recvfrom sendto,shutdown getpeername getsockopt setsockopt,在程序中使用套接字调用,使用TCP的客户和服务器套接字函数调用序列,用于整数转换的实用例程,网络字节顺序:最高位字节在前 有些套接字例程要求参数按照网络字节顺序存储。如sockaddr_in 需要网络字节顺序和本地主机字节顺序进行转换的函数,坚持使用,便于移植。 分为短(short 16位)和长(long 32位)两种 htons():将一个短整数从本地字节顺序转换为网络字节顺序; ntohs():将一个短整数从网络字节顺序转换为本地字节顺序; htonl()和ntohl():类似如上,套接字调用参数使用的符号常量,UNIX系统提供了预定义的符号常量和数据结构来声明数据和指明参数: 使用何种服务: SOCK_DGRAM: 数据报服务,UDP协议 SOCK_STREAM: 流服务,TCP协议 PF_INET:使用TCP/IP协议族 AF_INET: 使用TCP/IP地址结构 需要include,引用出现这些定义的文本 #include #include ,客户端程序设计方法,应用程序如何发起通信成为客户 如何使用TCP或UDP与服务器联系 如何使用套接字调用与协议交互,初始化期望与之通信的服务器IP地址和协议端口号 分配套接字socket() 指明此连接需要在本地机器中的、任意的、未使用的协议端口,并允许TCP选择一个这样的端口 将这个套接字连接到服务器connect() 使用应用级协议与服务器通信send()/recv() 关闭连接close(),面向连接的客户端算法TCP,需要考虑的问题,应用程序必须指明的细节 客户还是服务器 端点地址 使用面向连接的还是无连接的服务 如何执行授权和防护准则 所需要的缓存的大小等 只需要概念性地了解套接字API 需要详细了解构造通信程序的各种方法,便于迅速作出设计决策。,客户端程序的体系结构,客户应用程序比较简单 不必明显地处理并发性 不需要特权,和常规的应用程序一样执行 不需要强行保护,依赖操作系统自动强迫执行保护,标识服务器的位置,在编译程序时,将服务器的域名或者IP地址说明为常量 执行快,但是服务器移动后不便 要求用户在启动程序时标定服务器 从稳定的存储设备中获得关于服务器的信息 如果文件不存在,客户软件就不能执行 使用某个单独的协议来找到服务器 只能在本地小环境下应用 根据实际情况,综合标识服务器地址,面向连接的客户端程序:步骤1,初始化服务器的地址结构,TCP/IP的地址定义 struct sockaddr_in u_char sin_len; u_short sin_family; u_short sin_port;/2B struct in_addr sin_addr;/4B char sin_zero8;/8B未使用 IP专用的结构,地址结构长度 地址的协议AF_INET 端口号 IP地址,分析地址参数,参数由字符串构成,客户使用参数语法: 域名方式(或IP地址)方式 域名和IP地址的确定:扫描参数,看是否含有字母。 全参数化的客户软件允许用户指明协议端口号和机器 使用两个参数: smtp 使用单个参数::smtp 遵循本地系统的约定:Linux使用单独参数指明服务器的机器地址和协议端口,查找域名,使用sockaddr_in结构指明服务器的地址 需要二进制表示的32bit IP地址 套接字对地址转换的支持(两个库例程) inet_addr: IP地址点分十进制到二进制的转换 接受一个点分十进制表示的字符串地址,返回一个等价的二进制地址 gethostbyname: 主机域名到二进制的转换 接受一个机器域名字符串,返回一个hostent结构,内含一个二进制表示的主机IP地址,hostent结构体的声明,struct hostent char *h_name; char *h_aliases; int h_addrtype; int h_length; char *h_addr_list; #define h_addr h_addr_list0,调用gethostbyname得到IP地址,Struct hostent *hptr; Char *examplenam=””; If(hptr=gethostbyname(examplenam) /*IP address is now in hptr-h_addr*/ else /*error in name-handle it*/ ,由名字查找某个熟知端口,getservbyname: 两个参数指明期望的服务和协议。返回servent类型的结构指针; 注意网络字节顺序和本地机器的表示方法;,struct servent char *s_name; char *s_aliases; int s_port; char *s_proto; ;,struct servent *sptr; if (sptr = getservbyname(“smtp”, “tcp”) /* port number is now in sptr -s_port*/ else /* error occurred-handle it */ ,由名字查找协议,getprotobyname: 由协议名返回协议号;返回一个protoent类型结构的地址,struct protoent char *p_name; char *p_aliases; char *p_proto; ;,struct protoent *pptr; if (pptr = getprotobyname(“udp”) /* official protocol number is now in pptr -p_proto*/ else /* error occurred-handle it */ ,分配套接字,使用socket函数 将协议和服务分别说明为PF_INET和SOCK_STREAM include语句包含一些定义常量的文件 对于TCP/IP,第三个参数没有用。,#include #include int s; /* socket descriptor */ s = socket ( PF_INET, SOCK_STREAM, 0);,选择本地协议端口号,服务器运行于熟知的端口上,客户不是 客户使用端口的规则: 该端口不与该机器其他进程使用端口冲突 该端口没有被分配给某个熟知服务 客户允许TCP自动选择本地端口 connect调用的一个效果就是所选择的本地端口能够满足上述准则,选择本地IP地址的基本问题,对于只挂在一个网络上的主机是简单的 正确的选择依赖于选路信息,但应用程序很少使用选路信息,实际中存在的问题: 一个主机可能具有多个IP地址 如果应用程序随机选择一个IP地址,可能选择了一个与IP地址的接口并不匹配的地址。 可能能够正确的工作。但是网络管理会困难和混乱,可靠性降低。 一般本地地址字段不填,允许客户自动选取本地IP地址,将TCP套接字连接到服务器,connect函数:允许TCP套接字发起连接 强迫执行下层的三次握手 超时或者建立连接后返回 三个参数: retcode = connect(s, remaddr, remaddrlen); s: 套接字的描述符 remaddr:一个sockaddr_in类型结构的地址 remaddrlen:第二个参数的长度,将TCP套接字连接到服务器,connect的四项任务 对指明的套接字进行检测:有效,没有连接 将第二个参数给出的端点地址填入套接字中 为此套接字选择一个本地端点地址 发起一个TCP连接,并返回一个值,使用TCP和服务器通信,客户发送请求,等待响应 发送请求:send; 等待响应:recv; TCP不保持记录的边界,面向流的概念,send(s, req, strlen(req), 0); while (n = recv (s, bptr, buflen, 0) 0) bptr +=n; buflen -=n; ,关闭TCP连接,close:从容关闭连接释放该套接字 常常需要在客户服务器之间协调关闭事宜 服务器不能关闭连接,不知客户请求是否完成 客户不知道服务器发出的数据是否全部到达 允许应用程序在一个方向关闭TCP连 shutdown(s, direction); direction:0不允许输入; 1不允许输出; 2双向关闭 部分关闭可以让服务器发送完最后一个响应后,关闭连接。,客户端程序举例,DAYTIME服务 功能:只要连接到来,服务器就构造包含当前日期时间的文本字符串发送,然后关闭连接。客户不用发送任何数据,发送了也会忽略。 周知端口:13,while( (n = read(s, buf, LINELEN) 0) bufn = 0; (void) fputs( buf, stdout ); ,参考例子daytime.c,面向无连接的客户端程序UDP,初始化期望与之通信的服务器IP地址和协议端口号 分配套接字socket() 指明这种通信需要本地机器中的、任意的、未使用的协议端口,并允许UDP选择一个这样的端口connect() 指明报文所要发往的服务器sendto() 使用应用级协议与服务器通信sendto()/recvform() 关闭连接close(),连接和非连接的UDP套接字,连接的UDP通信 客户使用connect调用指明远程端点地址 使用SOCK_DGRAM类型的套接字 不发起任何分组交换,不检查远程端点合法性 只是在套接字的数据结构记录远程端点的信息 不用重复指明远端地址收发报文 只和一个服务器交互比较方便 非连接的UDP通信 在每次发送报文的时候指明远程目的地 使用灵活,便于同不同的服务器通信,使用UDP和服务器通信,对于连接的UDP套接字 使用send发送报文 使用recv接收报文 每次send发送一个完整的报文 每次recv接收一个完整的报文,足够大缓存 不需要重复使用recv获得单个报文 对于非连接的UDP套接字 sendto: 发送报文,含有地址信息 recvfrom: 接收一个含有源地址的数据报,关闭UDP套接字和UDP特点,close: 关闭套接字,释放与之关联的资源 拒绝以后到达的报文 没有通知远程端点 shutdown: 在某个方向上终止进一步传输 不向另外一方发送任何报文,只是在本地套接字标明不期望在指定的方向传输数据 客户关闭输出以后,服务器并不知道 UDP提供的是不可靠的交互 必须自己设计协议实现可靠性,客户端程序举例UDP,TIME服务:允许一台机器从另外一台机器获得日期和时间。由于存在不同的时区,所有的时间日期信息必须用国际标准时间:UCT或UT 服务器应答前将本地时间转化为标准时间 客户收到应答时,将国际标准时间转化为本地时间 TIME协议规定由32bit的整数来表示:用于一台计算机使用另外一系统时钟来设置起事件,TIME服务,可以使用UDP(TCP)访问TIME服务 客户发出包含单个数据报的请求 服务器从传入的数据报中取出地址和端口号 服务器将当前时间编码为一个整数,使用上述地址和端口号发回给客户,(void) write(s, MSG, strlen(MSG); n = read(s, (char *),使用连接的UDP服务,ECHO服务,ECHO服务器返回从客户收到的所有数据,用户网络管理员测试可达性,调试协议软件,识别选路问题等 TCP ECHO服务:接收连接请求,从连接中读取数据,在该连接上将数据写回。直到客户终止传送。 UDP ECHO服务:接收整个数据报,根据数据报指明的端口号和地址,返回整个数据。,请编写TCP-ECHO.c和UDP-ECHO.c代码,while (fgets(buf, sizeof(buf), stdin) bufLINELEN = 0; outchars = strlen(buf); (void) write(s, buf, outchars); for (inchars = 0; inchars outchars; inchars+=n ) n = read(s, ,ECHO代码,服务器程序设计方法,服务器软件设计存在的问题 四种基本类型的服务器程序 本地地址结构的初始化 周知端口绑定及被动模式的设置 accept()函数调用 循环服务器的设计 面向连接的和非连接的设计方法 并发服务器的设计 多进程的多线程并发服务器设计 单进程中多线程的并发服务器设计,四种基本类型的服务器程序,循环的或者并发的 使用面向连接的或者无连接的传输,本地地址结构的初始化及绑定,初始化地址协议族 AF_INET 初始化周知端口 getservbyname() 初始化本地IP地址 对于多接口主机使用INADDR_ANY指明了一个通配地址,让该主机的任何一个IP地址都匹配,TCP/IP的地址定义 struct sockaddr_in u_char sin_len; u_short sin_family; u_short sin_port;/2B struct in_addr sin_addr;/4B char sin_zero8;/8B未使用 IP专用的结构,周知端口绑定及被动模式的设置,bind系统调用用于将描述符和该地址结构进行绑定 listen系统调用用于将套接字置于被动模式 input_queue_size参数指明套接字内部的请求队列长度 请求队列保存一组TCP传入连接请求,来自客户,都向这个服务器请求一个连接,int bind(int sockfd, struct sockaddr * my_addr, int addrlen),int listen(int sockfd, int input_queue_size),accept()系统调用,accept系统调用用于接受连接请求并使用这些连接 返回新的连接的套接字的描述符 新的套接字描述符用于为连接请求服务 原来的主套接字描述符用于在窗口等待请求 服务器接收连接,使用read获得来自客户的应用协议,使用write发回应答。 服务器结束连接,使用close释放套接字,ssock = accept(msock, (struct sockaddr *),面向连接的循环服务器的设计方法,面向连接的循环服务器基本算法 创建套接字并将其绑定到它所提供服务的熟知端口上;socket+bind 将该端口设置为被动模式,使其准备为服务器所用;listen 从该套接字上接收连接请求,获得该连接的新的套接字;ssock=accept(s, ) 重复地读取来自客户的请求,构造响应,按照应用协议向客户发回响应;send/recv(ssock) 当某个特定客户完成交互时,关闭连接,并返回步骤3以接受新的连接。close(ssock),面向连接的循环服务器的进程结构,使用一个单执行进程(线程) 使用两个套接字 一个套接字处理请求 另外一个套接字处理和客户的通信(临时的),面向连接的循环服务器程序应用举例,main() ssock = accept(msock, (struct sockaddr *) ,daytime服务器程序,参考daytimed.c,非连接的循环服务器程序设计方法,创建套接字并将其绑定到所提供服务的熟知端口上 重复读取来自客户的请求,构造响应,按照应用协议向客户发回响应。 无连接循环服务器的进程结构,非连接服务器应答构造,服务器从收到的请求中的源地址获得应答的地址。调用recvfrom得到数据和对方的地址。 retcode = recvfrom(s, buf, len, flags, from, fromlen); 无连接服务器使用sendto发生数据报:指明了发送的数据报和它将去的地址 retcode = sendto (s, msg, len, flags, toaddr, toaddrlen);,无连接循环服务器程序应用举例,while (1) alen = sizeof(fsin); recvfrom(sock, buf, sizeof(buf), 0, (struct sockaddr *) ,time服务器程序,参考udptimed.c,并发服务器程序的设计方法,两种形式的并发性:多进程(实现多线程)和单进程(实现多线程) 服务器创建多个进程,每个进程都有一个主执行线程 服务器在一个进程中创建多个执行线程,多线程实现并发服务器的基本算法,主线程最先开始执行在熟知端口上打开一个套接字,等待一个请求,并为每个请求创建一个从线程(可能在一个新进程中) 主线程不与客户直接通信,每个从线程处理一个客户的通信。 从线程构成响应并发送给客户后,这个从线程便退出。,多进程实现并发服务器的进程结构,服务器包括一个主进程,以及零个或者多个从进程。每个进程中运行一个线程 主服务器使用accept阻塞调用,节约CPU资源,连接到来的时候,accept马上返回一个新的套接字,与单个连接进行通信,多线程实现并发服务器的进程结构,面向连接的并发服务器设计算法,面向连接的服务器在多个连接之间实现并发(不是在各个请求之间),从1:由主线程传递来的连接请求开始 从2:用该连接与客户进行交互;读取请求并发回响应 从3:关闭连接并退出,主1:创建套接字并将其绑定到所提供服务的周知地址上,让该套接字保持为面向连接 主2:将该端口设置为被动模式 主3:反复调用accept以便接收来自客户的下一个连接请求,并创建新的从线程或者进程来处理响应,多进程实现并发服务器程序的应用实例,switch (fork() case 0: (void) close(msock); exit(TCPechod(ssock); default: (void) close(ssock); break; ,ECHO服务,参考tcpechod.c,面向连接的并发服务器实现方法小结,面向连接的并发技术 使用多进程的方式实现:fork 主进程的线程永远不会和任何客户打交道,只接受连接,创建一个从进程处理各个连接 从进程从主进程调用fork后立即执行 主进程关闭新连接所用的描述符的副本 从进程关闭主描述符的副本,无连接并发服务器程序设计算法,主1:创建套接字并将其绑定到所提供服务的熟知地址上。让该套接字保持为未连接的 主2:反复调用recvfrom接收来自客户的下一个请求,创建一个新的从线程来处理响应 从1:从来自主进程的特定请求以及到该套接字的访问开始 从2:根据应用协议构造应答,并用sendto将该应答发回给客户 从3:退出(即:从线程处理完一个请求后就终止),很少的无连接服务使用并发方式,多线程并发服务器程序设计方法,回顾进程的基本概念 进程是不活泼的。进程从来不执行任何东西,它只是线程的容器。若要使进程完成某项操作,它必须拥有一个在它的环境中运行的线程,此线程负责执行包含在进程的地址空间中的代码。 单个进程可能包含若干个线程,这些线程都“同时” 执行进程地址空间中的代码。 每个进程至少拥有一个线程,来执行进程的地址空间中的代码。当创建一个进程时,操作系统会自动创建这个进程的第一个线程,称为主线程。此后,该线程可以创建其他的线程。,线程的基本概念,线程由两个部分组成: 线程的内核对象,操作系统用它来对线程实施管理。内核对象也是系统用来存放线程统计信息的地方。 线程堆栈,它用于维护线程在执行代码时需要的所有参数和局部变量。 当创建线程时,系统创建一个线程内核对象。该线程内核对象不是线程本身,而是操作系统用来管理线程的较小的数据结构。可以将线程内核对象视为由关于线程的统计信息组成的一个小型数据结构。,线程的基本概念,线程总是在某个进程环境中创建。 系统从进程的地址空间中分配内存,供线程的堆栈使用。 新线程运行的进程环境与创建线程的环境相同。 新线程可以访问进程的内核对象的所有句柄、进程中的所有内存和在这个相同的进程中的所有其他线程的堆栈。这使得单个进程中的多个线程确实能够非常容易地互相通信。 线程只有一个内核对象和一个堆栈,保留的记录很少,因此所需要的内存也很少。,单线程程序与多线程程序,单线程程序,多线程程序,一个线程,两个线程,使用线程来实现并发,一个进程中有一个或者多个线程 Linux中的线程符合POSIX线程标准,1003.1c Linux中线程的特点 动态创建:pthread_create,具有上限 并发执行:多处理机上可以并行 抢先:系统自动在多个线程中调动CPU资源 私有局部变量:每个线程有自己的私有堆栈 共享全局变量:一个进程的所有线程共享全局变量 共享文件描述符:一个进程内的所有线程共享一组文件描述符 协调和同步函数:具有线程协调和同步执行的函数,线程的优点,多线程的进程和单线程的进程比较 更高的效率:上下文交换的额外开销减少 上下文交换:线程切换需要执行的指令 同一进程中的两个线程比不同进程中的两个线程切换要快 进程内的线程切换不用改变虚拟存储器的地址 共享存储器: 并发服务器中的多个副本需要相互通信或者访问共享的数据 利用线程容易构造监控系统,线程的缺点,由于线程间贡献存储器和进程状态,一个线程的动作可能对同一个进程内的其他线程产生影响。 两个线程如果同一时刻访问同一个变量,会产生相互干扰 将指针返回给一个静态的数据项的库函数不是线程安全的,覆盖将会导致错误 缺乏健壮性,一个线程出错,服务器将会终止整个进程,有关线程的函数,Unix/Linux支持的线程库标准是POSIX libpthread库 程序中需要包含pthread.h头文件,编译时需要加载lpthread库 pthread_t类型,用于声明线程标识符 pthread_t在头文件/usr/include/bits/pthreadtypes.h中定义: typedef unsigned long int pthread_t;,gcc o multithread multithread.c lpthread,线程创建是通过clone()的系统调用,有关线程的函数创建新线程,pthread_create()函数创建一个新线程 4个参数,第4个参数是运行函数的参数,int pthread_create (pthread_t * thread, const pthread_attr * tattr, void

温馨提示

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

评论

0/150

提交评论