资源描述
,*,单击此处编辑母版标题样式,单击此处编辑母版文本样式,第二级,第三级,第四级,第五级,网络编程语言和套接字基础知识概述,网络应用程序模型,1,内容提要,操作系统应用空间编程,可用各种高级语言 C/C+,Java,delphi,VB,C语言常见注意事项,文件操作,字符串操作,函数指针应用,程序结构,有关资料和注意事项,常用C语言开发环境,套接字基础知识,2,操作系统的应用空间编程,可以使用常见的高级语言编程,Java:具有良好的可移植性,但需要虚拟机,VB,delphi:使用简单,只用于Windows环境,C/C+:高效,各种操作系统支持的语言,使用C语言作为网络编程语言的优势,具有多种操作系统支持,几乎所有,执行效率高,编程灵活,要求高,可以进行系统内核编程,具有大量开源项目,3,C语言文件操作,fopen和open的区别,fopen是ANSI C的标准,可移植,open更加底层,linux系统,打开设备,fwrite和fread,二进制,记录操作,fgetc,fgets和fputc,fputs,字符和字符串操作,fseek,ftell,feof,如何获取文件大小?,4,读取内容赋值给数据结构变量,数据结构例子,struct student,char name12;,short age;,int weight;,struct student*man;,man=(struct student*)malloc(sizeof(struct student);,.,num=fread(man,sizeof(struct student),1,filestream);,man-age+;,.,fwrite(man,sizeof(struct student),1,filestream);,5,字符串函数,char*p,str100;,数组名str是恒定的指针,p是指针变量;,sprintf (fprintf,printf),将多个变量格式化输出到一个字符串中。(输出到文件中,输出到屏幕中),memset(str,0,100):置初值为0;,memcpy,strcpy:拷贝内存或者字符串;,memcmp,strcmp:内存比较或者字符串比较;,strstr,strchr:字符串查找或者字符查找;,strlen:字符串长度,6,将数据结构表现为字符串,sprintf(p,name=%snage=%dnweight=%dnn,man-name,man-age,man-weight);,p=name=zhangshan(换行)age=18(换行)score=66(换行)(换行),需要先执行:p=str;不然p没有空间。,如何从p这个字符串中找到分数值?,p=strstr(str,weight=);,if(p!=NULL),weight=atoi(p+7);,7,如何表示链表,struct student,char name12;,short age;,int weight;,struct student*next;,;,struct student*head,*last,*tmp;,for(i=0;inext=tmp;,last=tmp;,8,函数指针的应用,函数名可以作为一个函数的指针,可以定义一个指针变量来保存函数名,例子,结构定义:,struct net_proto_family,intfamily;,int(*create)(struct net*net,struct socket*sock,int protocol,int kern);,struct module*owner;,inet,;,函数定义:int create_TCP(,struct net*net,struct socket*sock,int protocol,int kern),.,赋值:inet.create=create_TCP;,使用:int ret=inet.create(pnet,psock,tcp,kern);,9,C语言程序构成,头文件,宏定义固定值用有意义的名字表示,数据结构定义,类定义,函数定义和全局外部变量定义,C文件,包含头文件,定义变量,函数实现,分类,有序,缩进规范,注释,空格.,10,C开发环境,linux,gcc+gdb,makefile的编写,Windows,VC+6.0,VS系列,C+BUILDER,Qt,Eclipse C+,ACE,11,文本界面和图形用户界面,文本界面,所有输入是getc,getch,gets之类,所有输出用printf,不美观,命令行,适用控制台程序,服务器,图形用户界面,Qt,VC,VS,需要理解C+,理解windows消息机制,实用于客户端程序,12,C+,VC+,MFC,C+是C语言的超集,增加了面向对象的特色;适合开发大型软件;,VC+使用了MS对C+的扩展,是可视化的C+开发环境;,MFC是MS基础类库,以C+类的形式封装了Windows API,并且包含一个应用程序框架,以减少应用程序开发人员的工作量,具体内容参见有关资料课件;,13,网络应用程序基础知识,什么是套接字,网络通信标准,网络应用程序的模型,C/S,P2P,混合模式,网络地址及其他问题,套接字的实现简述,14,什么是套接字,TCP/IP协议存在于OS中,网络服务通过OS提供,在OS中增加支持TCP/IP的,系统调用,Berkeley套接字,如,s,ocket,,c,onnect,,s,end,,r,ecv等,socket是,1981提出于BSD4.1 UNIX,15,什么是套接字,1.,套接字是一个主机本地应用程序所创建的,为操作系统所控制的接口,(“,门”,).,2.,应用进程通过这个接口,使用传输层提供的服务,跨网络发送,(/,接收,),消息到,(/,从,),其他应用进程。,3.Client/server,模式的通信接口,套接字接口,.,process,TCP with,buffers,variables,socket,由应用程序,开发者控制,由操作系统控制,host or,server,process,TCP with,buffers,variables,socket,controlled by,application,developer,controlled by,operating,system,host or,server,internet,16,套接字,API,来自UNIX系统,,目前在linux,windows皆实现。,Berkeley套接字,ARPA要求伯克利分校将TCP/IP移植到UNIX中,需要创建一个接口,便于应用程序使用这个接口 进行网络通信,尽可能使用现有的系统调用,同时添加新的系统调用支持TCP/IP。,这个系统被称为BSD UNIX 套接字,成为事实上的标准,17,Winsock,伯克利套接口在Windows 平台上的移植版本称为Winsock。,它不仅包含,BSD socket,的大部分函数,还包含一组针对Windows 系统的扩展库函数,通常以字母WSA 打头,编程人员能充分利用windows 的消息机制以及Win32 平台下的高性能I/O模型。,不同的TCP/IP 协议栈供应商需要提供自己的Winsock 接口实现的动态链接库,标准winsock,库,如,WS2_32.dll,等,18,什么是,网络应用程序,网络应用程序:,能够运行在不同的端系统并通过网络彼此通信的程序,例如Web:Web服务器软件和浏览器软件通信,实现应用层协议,应用层,传输层,网络层,数据链路层,物理层,应用层,传输层,网络层,数据链路层,物理层,应用层,传输层,网络层,数据链路层,物理层,没有应用程序软件运行在网络核心设备上,网络核心设备不在应用层起作用,这种设计方法促进了应用程序的研发,19,2:应用层,一些网络应用,电子邮件,Web,即时讯息,远程登陆,P2P,文件共享,在两台计算机之间的两个帐户之间的文件传输,多用户网络游戏,流式存储视频片段,因特网电话,实时视频会议,20,2:应用层,网络应用程序,模型(,体系结构,),客户机,/,服务器体系结构,P2P,体系结构,客户机,/,服务器和,P2P,混合的体系结构,21,2:应用层,客户机,/,服务器体系结构,服务器:,总是打开的主机,具有固定的、众所周知的,IP,地址,主机群集常被用于创建强大的虚拟服务器,客户机,:,同服务器端通信,可以间断的同服务器连接,可以拥有动态,IP,地址,客户机相互之间不直接通信,22,2:应用层,纯,P2P,体系结构,没有总是打开的服务器,任意一对主机直接相互通信,对等方间歇连接并且可以改变IP地址,例如:Gnutella,,emule,优点:自扩展性,缺点:难以管理,23,2:应用层,客户机,/,服务器和,P2P,混合,emule,文件直接在对等方之间交换,文件搜索通过服务器,中心服务器记录对等方内容,对等方查询中心服务器来决定要求的文件位置,即时讯息,两个聊天用户之间是P2P,注册、查询通过服务器,用户上线时要在中心服务器上进行注册,用户与中心服务器联系以找出在线伙伴,24,2:应用层,进程通信,进程:运行在端系统中的程序,同一主机上的两个进程通过内部进程通信机制进行通信,不同主机上的进程通过交换报文相互通信,客户机进程,:,发起通信的进程,服务器进程,:,等待联系的进程,注意:具有,P2P,体系结构的应用程序有客户机进程和服务器进程。,25,2:应用层,进程与计算机网络的接口,-,套接字,进程通过它的套接字在网络上发送和接收报文,套接字类比于门户,发送进程把报文推出门户,发送进程假定门户到另外一侧之间有运输设施,该设施可以传送报文到接收进程,由操作系统控制,进程,具有缓存,、变量的,TCP,套接字,主机或,服务器,进程,具有缓存,、变量的,TCP,套接字,主机或,服务器,Internet,由应用开发者控制,套接字又叫做应用程序编程接口,API,用户通过,API,对传输层的控制仅限于:,(1),选择传输协议,;(2),能设定几个参数,26,2:应用层,网络,进程寻址,为了一个进程能接收报文,它需要一个标识,主机有唯一的,32,位,IP,地址,问:,主机的,IP,地址足够标识进程吗?,答:,不能。因为一台主机上能够运行许多进程。,主机上的进程标识包括IP地址和,端口号,常用应用程序的端口号:,Web服务:80,邮件服务:25,报文如何定位一个进程,UDP:目的IP和端口,TCP:目的IP和端口,源IP和源端口,27,2:应用层,套接字的类型,网络系统提供了三种不同类型的套接口:,流式套接口(SOCK_STREAM),TCP,数据报套接口(SOCK_DGRAM),UDP,原始套接口(SOCK_RAW),ICMP,28,套接字的类型,SOCK_STREAM:流套接口,对应于TCP 协议,。也称面向连接的套接口、TCP 套接口等。,SOCK_DGRAM:数据报套接口,对应于UDP 协议,,也称,无连接套接口、面向消息套接口、UDP套接口等。,SOCK_RAW:原始套接口,可以读写ICMP、IGMP 报文;从IP 头起构造自己的报文;接受发向本机的但TCP/IP栈不能够处理的IP包。,29,地址,表示,与地址操作函数,sockaddr_in INET 协议族地址结构,in_addr IPv4地址结构,s,ockaddr 通用地址结构,地址操作函数,30,INET,协议族地址结构,sockaddr_in,地址结构名中的最后两个字母“in”,是Internet 的简写,说明该结构仅适用于采用TCP/IP协议的网络。,结构定义如下:,struct sockaddr _ in,short sin_family;/地址族,u_short sin_p,or,t;/端口号,struct in_addr sin_addr;/IP地址,char sin_zero 8 ;,;,31,INET,协议族地址结构,sockaddr_in,sin_family:地址族,一般填为AF_INET。,另一组和AF_XXX 类似的PF_XXX 常量,与AF_INET 相对应有PF_INET。,历史上,PF_XXX 被设计用于表示协议族,而AF_XXX 用于表示地址族。最初的设想是单个协议族可以支持多个地址族,PF_XXX 用于套接口的创建,AF_XXX 用于套接口地址结构。,在,一般操作系统中,,PF_XXX 被定义为与AF_XXX 值完全相同。,sin_port:16 位的IP 端口,必须注意字节序问题。,sin_addr:32位的IPv4 地址。,32,INET,协议族地址结构,sockaddr_in,sin_zero:8,个字节的,0,值填充,惟一的作用是使,sockaddr_in,结构大小与通用地址结构,sockaddr,相同。,一般在给结构体赋值之前先将其全部初始化为零。,下面的两个函数经常被用来完成清零工作,由于前者仅适用于,Win32,平台,推荐使用后者。,VOID ZeroMemory(PVOID destination,SIZE_T lenqth);,void*memset(void*dest,int c,size _t count);,33,IPv4,地址结构,in_addr,用于存储32位IPv4 地址的数据结构,其定义如下:,struct in,_,addr,union,struct u_char s_b1,s_b2,s_b3,s_b4;S_un_b;,struct u_short s_w1,s_w2;S_un_w;,u_long S_addr;,S_un;,#define s_addr S_un.S_addr,#define s_host S_un.S_un_b.s_b2,#define s_net S_un.S_un_b.s_b1,#define s_imp S_un.S_un_w.s_w2,#define s_impno S_un.S_un_b.s_b4,#define s_lh S_un.S_un_b.s_b3,;,34,IPv4,地址结构,in_addr,有三种赋值接口:S_addr,S_un_b,,S_un_w。,最常用的赋值接口是S_addr和S_un_b。,S _ addr:32 位的无符号整数,对应32 位IPv4 地址。,要将地址202.119.9.199 赋给in_addr 结构,可以使用如下代码:,in_addr addr;,addr.S_un.S_addr=inet_addr(“202.119.9.199”);,其中,ine,t_,addr 函数用于转换点串IP 地址。,上面代码可简写为:,in_addr addr;,addr.s_addr=inet_addr(“202.119.9.199”);,35,IPv4,地址结构,in_addr,假设主机上有多块以太网卡,每块网卡都配有,IP,地址,并且不关心应用程序具体使用哪个接口,那么在给,addr.s_addr,赋值时可用常量,INADDR_ANY,。它在,winsock2.h,中被定义为(,u _ long)0X00000000,,即本地的任意以太网接口,IP,地址。,代码如下:,in_addr addr;,addr.s_addr=INADDR_ANY;,36,IPv4,地址结构,in_addr,S_un_b,:包含,4,个,8,位无符号整数,组合起来表示,IPv4,地址:,s_b1.s_b2.s_b3.s_b4,。,例子,In_addr addr;,addr.S_un.S_un_b.s_b1=202;,addr.S_un.S_un_b.s_b2=119;,addr.S_un.S_un_b.s_b3=9;,addr.S_un.S_un_b.s_b4=199;,37,通用地址结构,sockaddr,struct sockaddr,u_short sa_ family;/address family/,char sa_data14;/up to 14 bytes of protocol address,;,在最初设计套接口函数接口时,面临着这样的选择:是专门开发一套为,TCP/IP,协议所用的,API,,还是提供一种通用的编程接口以服务于多种网络协议。,两者之间的差别非常明显,如果采用前者,那么提供的函数接口就会相对简单,,对于后者,程序员在使用时必须提供足够的信息(参数)来告诉接口自己所采用的协议族。,38,通用地址结构,sockaddr,以,connect,函数为例(该函数一般用于主动建立,TCP,连接),:,int connect(SOCKET s,const struct sockaddr FAR*name,int namelen);,为了使其适用于不同的网络协议环境,它的第二个参数并不是,struct sockaddr_in*,,而是,struct sockaddr*,。,在使用涉及到这种地址结构的函数接口时,必须强制将,struct sockaddr_in,指针转化为,struct sockaddr,指针。,39,地址操作函数,1,函数,inet_addr(),将包含点分格式的,IPv4,地址字符串转化为,in_addr,地址结构适用的,32,位整数。,其定义如下:,unsigned long inet_addr(const char FAR*cp);,参数:,cp,点分,IPv4,字符串。,如果没有错误发生,函数返回,32,位的地址信息。,如果,cp,字符串包含的不是合法的,IP,地址,那么函数返回,INADDR_NONE,。,40,地址操作函数,2 函数inet_ntoa()将一个in_addr 地址值转化为标准的点分IP 地址字符串。,定义如下:char FAR,*,inet_ntoa(struct in_addr in);,in:IPv4 地址结构。,返回值:如果没有错误发生,函数inet_ntoa 返回一指向包含点分IP 地址的静态存储区字符指针;否则返回NULL。,注释:保存在该指针指向的存储区中的信息仅确保在下一次Winsock 调用之前有效,因此应该及时加以复制。,41,地址操作函数,3,.,gethostbyname()完成的是域名解析功能。,函数定义如下:,struct hostent FAR*gethostbyname(const char FAR*name);,name:待解析的域名字符串,如,。,返回值:,如果没有错误发生,函数返回包含域名地址信息的HOSTENT 结构数据,在HOSTENT 结构中有一个h_addr_list 域,它是一个NULL 结尾的IP 地址列表。,有错误时,,返回空指针。,42,不同的主机对字节值的存储顺序不同,小序在前,(,Little,-endian),在起始地址处存放整数的低序号字节,大序在前,(Big-endian),在起始地址处存放整数的高序号字节。,计算机究竟,采用,那种字节存储顺序由各自的设计决定,Windows系列的操作系统使用的是小序在前的存储方式,Sun OS和Solaris等采用的大序在前的存储方式。,网络字节顺序,43,编程举例,SOCKADDR_IN,addrSrv;,addrSrv.sin_addr.S_un.S_addr=inet_addr(127.0.0.1);,char recvBuf100;,char tempBuf100;,sprintf(tempBuf,%s say:%s,inet_ntoa(addrSrv.sin_addr),recvBuf);,/,将,sin_addr,储存的,IP(127.0.0.1),转换成字符串形式。,44,主机字节序,计算机各自的“主机字节”,(Host-byte),来表示的。,网络字节序,统一为:,“大序在前”,需要注意字节序的地方,16位整数,32位整数,45,在,Winsock,中,有一系列的函数可用于多字节数的转换,把它们从主机字节顺序转换成网络字节顺序,反之亦然。下面,4,个,API,函数便将一个数从主机字节顺序转换成网络字节顺序:,htonl(),:参数是主机字节顺序的一个,4,字节数,函数返回网络字节顺序的数;,htons(),:参数是主机字节顺序的一个,2,字节数,函数返回网络字节顺序的数;,WSAHtonl(),:参数是主机字节顺序的一个,4,字节数,函数返回网络字节顺序的数;,WSAHtons(),:参数是主机字节顺序的一个,2,字节数,函数返回网络字节顺序的数,。,46,基本,Socket API,函数,socket,(),,bind,(),,connect,(),,accept,(),,listen,(),,getsockname,(),,getpeername,(),,gethostbyname,(),,close,()和,shutdown,(),47,socket,头文件,Windows,下包含的头文件:,#include,Linux,下包含的头文件:,#include,函数原型,int socket,(,int family,int type,int protocol,),;,48,协议族,socket,函数中的,family,值的常量定义:,协 议 族(,family,),含 义,AF_INET,IPv4,协议,AF_INET6,IPv6,协议,AF_LOCAL,Unix,域协议,AF_ROUTE,路由套接口,AF_KEY,密钥套接口,AF_LINK,数据链路层接口,AF_OSI,OSI,协议,AF_DECnet,DECnet,协议,AF_SNA,IBM,的,SNA,协议,AF_CCITT10,CCITT,协议,如,X.25,49,type,类 型(,type,),含 义,SOCK_STREAM,字节流套接口,SOCK_DGRAM,数据报套接口,SOCK_RAW,原始套接口(,raw socket,),50,protocol,一般将其设置为,0,,除非用在,原始套接口,上。,Linux,下,protocol,的常量在头文件,中定义,常量名以,IPPROTO_,开头,如,IPPROTO_IGMP,。,51,bind,函数原型,int bind,(,int sockfd,const sockaddr*myaddr,socklen_t addrlen,),;,功能:,bind,函数将一个本地的协议地址和,套接,口联系起来。,bind,函数的第一个参数为,套接,口描述符,sockfd,,它是,socket,()函数的成功返回值。,第二个参数是一个指向与协议有关的地址结构的指针,第三个参数则为地址的长度。,52,设置套接口地址结构的几种常见方式,进 程 指 定,说 明,IP,地址,端口,通配地址(,INADDR_ANY,),0,内核自动选择,IP,地址和端口,通配地址,非,0,内核自动选择,IP,地址,进程指定端口,本地,IP,地址,0,进程指定,IP,地址,端口由内核自动选择,本地,IP,地址,非,0,IP,地址和端口均由应用进程指定,53,例子,struct sockaddr_in serv_addr;,int sockfd;/*,套接口描述符*,/,short port=3500;/*,端口号*,/,bzero,(,char*,),&serv_addr,sizeof,(,serv_addr,),;,/,*,将地址结构缓存清,0*/,serv_addr.sin_family=AF_INET;,/*,设置,IP,地址,并将,IP,地址转换为网络字节顺序,但对于,INADDR_ANY,可不转换,因为,INADDR_ANY,的值等于,0,*/,serv_addr.sin_addr.s_addr=htonl,(,INADDR_ANY,),;,serv_addr.sin_port=htons,(,port,),;/*,将端口号转换成网络字节顺序*,/,if,(,bind,(,sockfd,(,struct sockaddr*,),&serv_addr,sizeof,(,serv_addr,),0,),/*,错误处理*,/,/*,成功处理*,/,54,connect,函数原型,int connect,(,int sockfd,const struct sockaddr*servaddr,socklen addrlen,),函数的第一个参数,sockfd,为,socket,调用返回的套接口描述符。第二个参数指向套接口地址结构的指针,第三个参数为地址结构的大小。套接口地址结构中必须包含有服务器的,IP,地址和端口号。,55,例子,struct sockaddr_in serv_addr;,char srvname=“10.65.19.10”;/*,服务器地址*,/,int sockfd;/*,套接口描述符*,/,/*,已调用,socket,产生,sockfd*/,bzero,(,char*,),&serv_addr,sizeof,(,serv_addr,),;/*,初始化服务器地址结构*,/,serv_addr.sin_family=AF_INET;/*,协议地址类型*,/,serv_addr.sin_addr.s_addr=inet_addr,(,srvrname,),;/*,将点分,IP,地址转换成整型地址*,/,serv_addr.sin_port=htons,(,port,),;/*,将端口号转换为网络字节顺序*,/,if,(,connect,(,sockfd,(,struct sockaddr*,),&serv_addr,sizeof,(,serv_addr,),0,),/*,调用成功,服务客户*,/,else /*,失败处理*,/,59,getsockname,函数原型,int getsockname,(,int sockfd,struct sockaddr*localaddr,socklen_t*addrlen,),;,函数的第一个参数,sockfd,为,socket,函数返回的套接口描述符。第二个参数,localaddr,和第三个参数,addrlen,分别用来返回与套接口关联的本地协议地址和地址长度。调用,getsockname,时,需将,addrlen,设置为结构,localaddr,的大小,函数返回时由内核设置,addrlen,的值。,60,getpeername,函数原型,int getpeername,(,int sockfd,struct sockaddr*peeraddr,socklen_t*addrlen,),;,函数的第一个参数,sockfd,为套接口描述符。第二个参数,peeraddr,和第三个参数,addrlen,分别用来返回与套接口关联的远程协议地址和地址长度。调用,getpeername,时,需将,addrlen,设置为结构,peeraddr,的大小,函数返回时由内核设置,addrlen,的值。,61,shutdown,函数原型,int shutdown,(,int sockfd,int howto,),;,函数的第二个参数,howto,指定关闭方式。,howto,说 明,SHUT_RD,关闭连接的读通道。在套接口上不能再发出读请求,并且套接口接收缓存中的所有数据都被丢弃;再接收的任何数据也被,TCP,丢弃。但是进程仍可往套接口上发送数据,对套接口的发送缓存没有任何影响,SHUT_WR,关闭连接的写通道。在套接口上不能再发送数据,但套接口发送缓存中的数据仍将被发送出去。发送完成后,发送正常的,TCP,连接终止序列(,FIN,)。但进程仍可从套接口上接收数据,对套接口的接收缓存没有任何影响,SHUT_RDWR,关闭连接的读写通道。在套接口上不能再发出任何读、写请求。完成的功能相当于分别执行了,SHUT_RD,和,SHUT_WR,62,基本套接口,I/O,函数,函 数,描述符类型,指定目的地址,标 志 位,控制信息,write,TCP,套接口,不需要,无,无,send,TCP,套接口,不需要,无,无,sendto,TCP,和,UDP,套接口(通常用于,UDP,套接口),需要,有,无,sendmsg,TCP,和,UDP,套接口(通常用于,UDP,套接口),需要,有,有,read,TCP,套接口,不需要,无,无,recv,TCP,套接口,不需要,无,无,recvfrom,TCP,和,UDP,套接口(通常用于,UDP,套接口),需要(返回报文源地址),有,无,recvmsg,TCP,和,UDP,套接口(通常用于,UDP,套接口),需要(返回报文源地址),有,有,63,Recv,send,函数原型,int,recv,(,int socket,void*buffer,size_t length,int flags,),;,int send,(,int socket,const void*buffer,size_t length,int flags,),;,Flags,的描述:,flags,描 述,接收函数,发送函数,MSG_DONTROUTE,旁路路由表,否,是,MSG_DONTWAIT,本操作不阻塞,是,是,MSG_OOB,发送或接收带外数据,是,是,MSG_PEEK,查看但不读取到达的数据,是,否,MSG_WAITALL,等待要读的所有数据,是,否,64,
展开阅读全文