网络程序设计第2章.ppt
- 文档编号:18720378
- 上传时间:2023-10-18
- 格式:PPT
- 页数:105
- 大小:2.35MB
网络程序设计第2章.ppt
《网络程序设计第2章.ppt》由会员分享,可在线阅读,更多相关《网络程序设计第2章.ppt(105页珍藏版)》请在冰点文库上搜索。
UNIX套接字网络编程接口的产生与发展过程套接字与UNIX操作系统的关系套接字编程的基本概念面向连接的套接字编程无连接的套接字编程原始套接字Linux系统的网络编程接口,本章提要,第2章UNIX中的套接字网络编程接口,2.1.1问题的提出站在应用程序实现的角度,应用程序如何方便地使用协议栈软件进行通信呢?
如果能在应用程序与协议栈软件之间提供一个软件接口,就可以方便客户与服务器软件的编程。
2.1UNIX套接字网络编程接口的产生与发展,套接字应用程序编程接口是网络应用程序通过网络协议栈进行通信时所使用的接口,即应用程序与协议栈软件之间的接口,简称套接字编程接口(SocketAPI)。
它定义了应用程序与协议栈软件进行交互时可以使用的一组操作,决定了应用程序使用协议栈的方式、应用程序所能实现的功能、以及开发具有这些功能的程序的难度。
2.1UNIX套接字网络编程接口的产生与发展,加州大学伯克利(Berkley)分校开发并推广了一个包括TCP/IP互联协议的UNIX,称为BSDUNIX(BerkeleySoftwareDistributionUNIX)操作系统,套接字编程接口是这个操作系统的一个部分。
后来的许多操作系统并没有另外搞一套其它的编程接口,而是选择了对于套接字编程接口的支持。
由于这个套接字规范最早是由Berkeley大学开发的,一般将它称为BerkeleySockets规范。
2.1.2套接字编程接口的起源与应用,要想实现套接字编程接口,可以采用两种实现方式:
一种是在操作系统的内核中增加相应的软件,网络程序中用系统调用的方法来实现(Unix/Linux)一种是通过开发操作系统之外的函数库,网络程序中采用调用库函数的方法来实现(Windows),2.1.3套接字编程接口的两种实现方式,UNIX操作系统对文件和所有其它的输入/输出设备采用一种统一的的操作模式,就是“打开-读-写-关闭”(open-read-write-close)的I/O模式。
当TCP/IP协议被集成到UNIX内核中的时候,相当于在UNIX系统中引入了一种新型的I/O操作,就是应用程序通过网络协议栈来交换数据。
2.1.4套接字通信与UNIX操作系统的输入/输出,在UNIX系统的实现中,套接字是完全与其他I/O集成在一起的。
操作系统和应用程序都将套接字编程接口也看作一种输入/输出机制。
但是,用户进程与网络协议的交互作用实际要比用户进程与传统的I/O设备相互作用要复杂得多。
2.1.4套接字通信与UNIX操作系统的输入/输出,其次,使用套接字的应用程序必须说明许多细节。
仅仅提供open、read、write、close四个过程远远不够。
为避免单个套接字函数参数过多,套接字编程接口的设计者定义了多个函数。
2.1.4套接字通信与UNIX操作系统的输入/输出,2.2套接字编程的基本概念,套接口是对网络中不同主机上应用进程之间进行双向通信的端点的抽象,一个套接口就是网络上进程通信的一端,提供了应用层进程利用网络协议栈交换数据的机制。
图2.1电插座与电话插座的作用,2.2.1什么是套接字(SOCKET),2.2.1什么是套接字(SOCKET)?
我们应当从多个层面来理解套接字这个概念的内涵。
从套接字所处的地位来讲,套接字上联应用进程,下联网络协议栈,是应用程序通过网络协议栈进行通信的接口,是应用程序与网络协议栈进行交互的接口。
图2.2应用进程、套接口、网络协议栈及操作系统的关系,进程、套接口、协议栈、操作系统的关系,从实现的角度来讲,非常复杂。
套接字是一个复杂的软件机构,包含了一定的数据结构,包含许多选项,由操作系统内核管理。
从使用的角度来讲,非常简单。
对于套接字的操作形成了一种网络应用程序的编程接口(API)。
本书把这一套操作套接字的编程接口函数称作套接字编程接口,套接字是它的操作对象。
总之,套接字是网络通信的基石。
套接字编程接口,2.2.2套接字的特点,1通信域套接字存在于通信域中,通信域是为了处理一般的进程通过套接字通信而引入的一种抽象概念,套接字通常只和同一域中的套接字交换数据。
如果数据交换要穿越域的边界,就一定要执行某种解释程序。
本课程中,仅仅针对Internet域,并且使用Internet协议族(即TCP/IP协议族)来通信。
2套接字具有三种类型,每一个正被使用的套接字都有它确定的类型,只有相同类型的套接字才能相互通信。
(1)数据报套接字(DatagramSOCKET)数据报套接字提供无连接的、不保证可靠的独立的数据报传输服务。
在Internet通信域中,数据报套接字使用UDP数据报协议形成的进程间通路,具有UDP协议为上层所提供的服务的所有特点。
数据报套接字基于UDP协议,图2.3在Internet通信域中,数据报套接字基于UDP协议,
(2)流套接字(StreamSOCKET),流套接字提供双向的、有序的、无重复的、无记录边界的、可靠的数据流传输服务。
在Internet通信域中,流套接字使用TCP协议形成的进程间通路,具有TCP协议为上层所提供的服务的所有特点,在使用流套接字传输数据之前,必须在数据的发送端和接收端之间先建立连接,如下页图2.4所示。
流式套接字基于TCP协议,图2.4在Internet通信域中,流式套接字基于TCP协议,(3)原始套接字(RAWSOCKET),原始套接字允许对较低层次的协议,如IP、ICMP直接访问,或用于检验新的协议的实现。
3套接字由应用层通信进程创建,并为其服务,套接字由应用层的通信进程创建,并为其服务,这就是说,每一个套接字都有一个相关的应用进程,操作该套接字的代码是该进程的组成部分。
4使用确定的IP地址和传输层端口号,套接字编程时,往往在生成套接字的描述符后,要将套接字与计算机上的特定的IP地址和传输层端口号相关联,这个过程称为绑定。
一个套接口要使用一个确定的三元组网络地址信息,才能使它在网络中唯一地被标识。
(1)不管是采用对等模式或者客户机/服务器模式,通信双方的应用程序都需要开发。
(2)双方所交换数据的结构和交换数据的顺序有特定的要求,不符合现在成熟的应用层协议的要求时。
甚至,有时需要自己去开发应用层协议,自己设计最适合的数据结构和信息交换规程。
2.2.3套接字的应用场合,2.2.4套接字使用的数据类型和相关的问题,1三种表示套接字地址的结构在套接字编程接口中,专门定义了三种结构型的数据类型,用来存储协议相关的网络地址,在套接字编程接口的函数调用中要用到它们。
三种表示套接字地址的结构,
(1)sockaddr结构,针对各种通信域的套接字,存储它们的地址信息:
structsockaddrunsignedshortsa_family;/地址家族charsa_data14;/协议地址,
(2)sockaddr_in结构,专门针对Internet通信域,存储套接字相关的网络地址信息,例如IP地址、端口号等信息。
structsockaddr_inshortintsin_family;/地址家族unsignedshortintsin_port;/2字节端口号structin_addrsin_addr;/4字节IP地址unsignedcharsin_zero8;/8字节全0/最后包含一个8字节的全0的域,以使该结构在大小上与sockaddr相同,三种表示套接字地址的结构,(3)in_addr结构,专门用来存储IP地址:
structin_addrunsignedlongs_addr;/4字节IP,三种表示套接字地址的结构,(4)这些数据结构组合使用的一般用法:
首先,定义一个sockaddr_in的结构实例,并将它清零。
比如:
structsockaddr_inmyad;memset(,一般,三种结构组合起来使用:
然后,为这个myad结构赋值,比如:
myad.sin_family=AF_INET;myad.sin_port=htons(8080);myad.sin_addr.s_addr=htonl(INADDR_ANY);注1:
地址家族有AF_INET、AF_DECnet、等字符常量可选,在Internet域内进行通信时,应赋值AF_INET。
注2:
INADDR_ANY字符常量表示该主机的任何一个IP地址。
一般,三种结构组合起来使用:
第三步:
在函数调用中使用时,将这个结构强制转换为sockaddr类型。
如:
accept(listenfd,(sockaddr*)(,一般,三种结构组合起来使用:
2主机字节顺序和网络字节顺序,多字节数据的字节顺序大端模式(bigendian):
高字节放到低地址上小端模式(littleendian):
高字节放到高地址上在具体计算机中多字节数据的存储顺序称为主机字节顺序。
不同的机器内存中主机字节顺序不相同,与CPU设计有关:
Motorola68000、PowerPC系列-大端模式(bigendian)Intelx86系列-小端模式(littleendian)多字节数据在网络协议报头中的顺序,称为网络字节顺序。
网络通信时一律使用统一的大端模式网络字节顺序,避免主机间通信时识别兼容问题。
2主机字节顺序和网络字节顺序,网络应用程序要在不同的计算机中运行,多字节数据在各机器内存中存放的主机字节顺序可能不同,可能是大端模式,也可能是小端模式;但是,在网络上传输时,在信包协议报头字段中的网络字节顺序是一定的,都是大端模式。
所以,应用程序在编程的时候,在把主机内存变量中的IP地址和端口号装入网络通信套接字的时候,应当把它们从主机字节顺序转换为网络字节顺序;相反,在主机输出显示时,应从收到的网络字节顺序转换为主机字节顺序。
在不同机器上运行网络程序,解决兼容性问题的途径:
往网络上发送前:
将IP地址、端口号转换成网络字节序;从网络上接收后:
将IP地址、端口号转换成主机字节序。
四个转换函数,套接字编程接口特为解决这个问题设置了四个函数:
htons():
短整数主机字节顺序转换为网络字节顺序,用于端口号。
htonl():
长整数主机字节顺序转换为网络字节顺序,用于IP地址。
说明h代表host,n代表network;s代表short,l代表long,ntohs():
短整数网络字节顺序转换为主机字节顺序,用于端口号。
ntohl():
长整数网络字节顺序转换为主机字节顺序,用于IP地址。
这四个函数将被转换的数值作为函数的入口参数,函数返回值是转换后的结果。
四个转换函数,说明h代表host,n代表network;s代表short,l代表long,3两个IP地址转换函数,在因特网中,IP地址常常用点分十进制的表示方法,但在套接字中,IP地址是无符号长整型数,套接字编程接口设置了两个函数,专门用于两种形式的IP地址的转换。
(1)inet-addr函数,unsignedlonginet_addr(constchar*cp)入口参数cp:
点分十进制形式IP地址字符串;返回值:
无符号长整型的网络字节顺序的IP地址。
例:
(2)inet_ntoa函数,char*inet_ntoa(structin_addrin)入口参数in:
包含无符号长整型IP地址的in_addr结构变量;返回值:
指向点分十进制IP地址字符串的指针。
例:
通常,我们使用域名来标识站点,可以将文字型的主机域名直接转换成IP地址:
structhostent*gethostbyname(constchar*name);入口参数name:
是站点的主机域名字符串返回值:
是指向hostent结构的指针hostent结构包含主机名,主机别名数组,返回地址的类型(一般是AF_INET),地址长度字节数,已符合网络字节顺序的IP地址等。
4域名解析函数,上述gethostbyname()域名解析函数的返回结构hostent为structhostentchar*h_name;/主机名char*h_aliases;/主机别名列表shorth_addrtype;/返回地址的类型shorth_length;/地址的长度char*h_addr_list;/主机IP地址列表(主机可能多个IP)#defineh_addrh_addr_list0;/主机IP地址列表中的第一个地址;比如,可以用下面的程序,将主机域名解析出IP地址:
h=gethostbyname(某域名);printf(“hostname:
%s”,hh_name);printf(“IPaddressis:
%s”,inet_ntoa(*(structin_addr*)hh_addr);,4域名解析函数,2.3面向连接的套接字编程,2.3.1套接字的工作过程,2.3.2UNIX套接字编程接口的系统调用,1创建套接字SOCKET()SOCKET过程创建一个套接字并返回一个整型描述符:
intsocket(intproto_family,inttype,intprotocol);,2BIND()绑定套接字到指定的地址,intbind(intsockfd,structsockaddr*my_addr,intaddrlen);,3Listen()启动监听,intlisten(intsockfd,intqueue_size);,举例:
listen(sockfd,10);,图2.6监听套接字使用缓冲区接纳多个客户端的连接请求,4ACCEPT()接受连接请求,intaccept(intsockfd,structsockaddr*addr,int*addrlen);举例:
intclientfd;/定义响应套接字描述符变量intaddrlen=sizeof(sockaddr);/获得套接字地址结构长度structsockaddr_incltsockaddr;/定义用于返回客户端地址的结构clientfd=accept(listenfd,(sockaddr*)(/接受客户连接请求,5CONNECT()请求建立连接,intconnect(intsockfd,structsockaddr*service_addr,intaddrlen);举例:
if(connect(sockfd,(structsockaddr*)(&serv_addr),sizeof(structsockaddr)0)连接服务器有错时的报错处理,并退出,6READ()和WRITE()读/写套接字,intread(intsockfd,void*buffer,intlen);intwrite(intsockfd,void*buffer,intlen),7SEND()和RECV()向套接字发送/接收,intsend(intsockfd,char*buf,intlen,intflags);intrecv(intsockfd,char*buf,intlen,intflags);,8CLOSE()关闭套接字,intclose(intsockfd);,2.3.3面向连接的套接字编程实例,1实例的功能服务器对来访的客户计数,并向客户报告这个计数值。
客户建立与服务器的一个连接并等待它的输出。
每当连接请求到达时,服务器生成一个可打印的ASCII串信息,将它在连接上发回,然后关闭连接。
客户显示收到的信息,然后退出。
1实例的功能,例如,对于服务器接收的第10次客户连接请求,该客户将收到并打印如下信息:
Thisserverhasbeencontacted10times.,2实例程序的命令行参数实例是UNIX环境下的C程序,客户和服务器程序在编译后,均以命令行方式执行。
服务器程序执行时可以带一个命令行参数,是用来表示接受客户端请求时的服务器监听套接字的协议端口号。
这个参数是可选的。
如果不指定端口号,代码将使用程序内定的缺省端口号5188。
2实例程序的命令行参数,客户程序执行时可以带两个命令行参数:
一个是服务器所在计算机的主机名,另一个是服务器监听的协议端口号。
这两个参数都是可选的。
如果没有指定协议端口号,客户使用程序内定的缺省端口5188。
如果一个参数也没有,客户使用缺省端口5188和缺省主机名localhost,localhost是映射到客户所在计算机的一个别名。
允许客户与本地机上的服务器通信,对调试是很有用的。
3客户程序代码/*-*程序:
client.c*目的:
创建一个套接字,通过网络连接一个服务器,并打印来自服务器的信息*语法:
clienthostport*host-运行服务器的计算机的名字*port-服务器监听套接字所用协议端口号*注意:
两个参数都是可选的。
如果未指定主机名,客户*使用缺省主机名localhost;如果未指定端口号,客户*将使用PROTOPORT中给定的缺省协议端口号。
*-*/,3客户程序代码,#include#include/*UNIX下,套接字的相关包含文件。
*/#include#include#include#include#include#definePROTOPORT5188/*缺省协议端口号*/externinterrno;charlocalhost=“localhost”;/*缺省主机名*/,3客户程序代码,main(intargc,char*argv)structhostent*ptrh;/*指向主机列表中一个条目的指针*/structsockaddr_inservaddr;/*存放服务器端网络地址的结构*/intsockfd;/*客户端的套接字描述符*/intport;/*服务器端套接字协议端口号*/char*host;/*服务器主机名指针*/intn;/*读取的字符数*/charbuf1000;/*缓冲区,接收服务器发来的数据*/,memset(char*),3客户程序代码,/*检查主机参数并指定主机名*/if(argc1)host=argv1;/*如果指定了主机名参数,就使用它*/elsehost=localhost;/*否则,使用缺省值*/,3客户程序代码,/*将主机名转换成相应的IP地址并复制到servaddr结构中*/ptrh=gethostbyname(host);/*从服务器主机名得到相应IP地址*/If(char*)ptrh=null)/*检查主机名的有效性,无效则退出*/fprintf(stderr,”invalidhost:
%sn”,host);exit
(1);memcpy(,3客户程序代码,/*创建一个套接字*/sockfd=socket(AF_INET,SOCK_STREAM,0);if(sockfd0)fprintf(stderr,”socketcreationfailedn”);exit
(1);/*请求连接到服务器*/if(connect(sockfd,(structsockaddr*),3客户程序代码,/*从套接字反复读数据,并输出到用户屏幕上*/n=recv(sockfd,buf,sizeof(buf),0);while(n0)write(1,buf,n);/*1stdout设备(即显示器),0stdin设备n=recv(sockfd,buf,sizeof(buf),0);/*关闭套接字*/closesocket(sockfd);/*终止客户程序*/exit(0);,3客户程序代码,4服务器实例代码/*-*程序:
server.c*目的:
分配一个套接字,然后反复执行如下几步:
*
(1)等待客户的下一个连接*
(2)发送一个短消息给客户*(3)关闭与客户的连接*(4)转向
(1)步*命令行语法:
serverport*port服务器端监听套接字使用的协议端口号*注意:
端口号可选。
如果未指定端口号,服务器使用PROTOPORT中*指定的缺省端口号*-*/,4服务器实例代码,#include#include#include#include#include#include#definePROTOPORT5188/*监听套接字的缺省协议端口号*/#defineQLEN6/*监听套接字的请求队列大小*/intvisits=0;/*对于客户连接的计数*/,main(argc,argv)intargc;char*argv;structhostent*ptrh;/*指向主机列表中一个条目的指针*/structsockaddr_inservaddr;/*存放服务器网络地址的结构*/structsockaddr_inclientaddr;/*存放客户网络地址的结构*/intlistenfd;/*监听套接字描述符*/intclientfd;/*响应套接字描述符*/intport;/*协议端口号*/intalen;/*地址长度*/charbuf1000;/*供服务器发送字符串所用的缓冲区*/,4服务器实例代码,memset(char*)/*否则,使用缺省端口号*/,4服务器实例代码,if(port0)/*测试端口号是否合法*/servaddr.sin_port=htons(u_short)port);else/*打印错误信息并退出*/fprintf(stderr,”badportnumber%sn”,argv1);exit
(1);/*创建一个用于监听的流式套接字*/listenfd=SOCKET(AF_INET,SOCK_STREAM,0);if(listenfd0)fprintf(stderr,“socketcreationfailedn”);exit
(1);,4服务器实例代码,/*将本地地址绑定到监听套接字*/if(bind(listenfd,(structsockaddr*),4服务器实例代码,while
(1)/*服务器主循环-接受和处理来自客户端的连接请求*/alen=sizeof(clientaddr);/*接受客户端连接请求,并生成响应套接字*/if(clientfd=accept(listenfd,(structsockaddr*)/*关闭监听套接字*/,4服务器实例代码,关于阻塞的问题,图2.7服务器进程因调用ACCEPT()而被阻塞,2.3.4进程的阻塞问题和对策,1什么是阻塞阻塞是指一个进程执行了一个函数或者系统调用,该函数由于某种原因不能立即完成,因而不能返回调用它的进程,导致进程受控于这个函数而处于等待的状态,进程的这种状态称为阻塞。
2.3.4进程的阻塞问题和对策,图2.8RECV()函数的两种执行方式,2能引起阻塞的套接字调用,在Berkeley套接字网络编程接口的模型中,套接字的默认行为是阻塞的,具体地说,在一定情况下,有以下操作套接字的系统调用会引起进程阻塞:
(1)accept()
(2)read()、recv()和readfrom()(3)write()、send()和sendto()(4)connect()(5)select()(6)closesocket(),3阻塞工作模式带来的问题,图2.9采用阻塞工作模式的服务器不能很好地为多个客户服务,采用阻塞工作模式的单进程服务器是不能很好地同时为多个客户服务的。
下图是一个例子。
4一种解决方案,利用UNIX/Linux操作系统的fork()系统调用,编制多进程并发执行的服务器程序。
可以创建子进程。
对于每一个客户端,用一个专门的进程为它服务,通过进程的并发执行,来实现对多个客户的并发服务。
Unix/Linux系统调用fork()的特点,进程在调用fork()时,会将自己拷贝一份,一份为父进程,另一份为子进程。
CPU会分别调度执行。
在各进程中调用fork()后的返回值:
父进程:
fork()返回值为正(为新建的子进程的进程号)子进程:
fork()返回值为0,出错时:
fork()返回值为负。
因此,根
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 网络程序设计